6

I've got some add_custom_command() stuff in a CMake build to do some things after building an elf file target: convert it to srec, fill various areas with 0xFF and create a binary image, generate a CRC and get the size of the image. add_custom_command() can have DEPENDS, making it run only when the elf file is regenerated, which is great.

What I also want to do is the create a new file using FILE() that contains the binary filename, crc, and size (maybe in simple JSON format), but the documentation implies that I can't do this file activity after the things I mention above have happened.

# This command creates the FF-filled binary file.  It uses objcopy to create the srec
# file to operate on.
add_custom_command(
    OUTPUT ThreadingApp.bin filled.srec
    MAIN_DEPENDENCY ThreadingApp.elf
    COMMAND ${CMAKE_OBJCOPY} ARGS -O srec ThreadingApp.elf ThreadingApp.srec
    COMMAND srec_cat.exe ThreadingApp.srec -offset - -minimum-addr ThreadingApp.srec
            −fill 0xFF -over ThreadingApp.srec -o filled.srec
    COMMAND srec_cat.exe filled.srec -o ThreadingApp.bin -binary
    )

# This command creates the CRC
add_custom_command(
    OUTPUT crc.out size.out
    MAIN_DEPENDENCY filled.srec
    COMMAND srec_cat.exe filled.srec -crc32-b-e 0x08100000 -crop 0x08100000 0x08100004 -o - -hex-dump > crc.out
    COMMAND srec_info.exe filled.srec > size.out
    )

file(
    WRITE ThreadingApp.json

    )

Looking at the synopsis of 'FILE', I don't see how I can make this happen only after my custom commands have already run. Any suggestions on how to achieve this within CMake? My alternative is to write a separate Python script to execute within an add_custom_command to create the json file.

Reading
  file(READ <filename> <out-var> [...])
  file(STRINGS <filename> <out-var> [...])
  file(<HASH> <filename> <out-var>)
  file(TIMESTAMP <filename> <out-var> [...])

Writing
  file({WRITE | APPEND} <filename> <content>...)
  file({TOUCH | TOUCH_NOCREATE} [<file>...])
  file(GENERATE OUTPUT <output-file> [...])

Filesystem
  file({GLOB | GLOB_RECURSE} <out-var> [...] [<globbing-expr>...])
  file(RENAME <oldname> <newname>)
  file({REMOVE | REMOVE_RECURSE } [<files>...])
  file(MAKE_DIRECTORY [<dir>...])
  file({COPY | INSTALL} <file>... DESTINATION <dir> [...])
  file(SIZE <filename> <out-var>)
  file(READ_SYMLINK <linkname> <out-var>)
  file(CREATE_LINK <original> <linkname> [...])

Path Conversion
  file(RELATIVE_PATH <out-var> <directory> <file>)
  file({TO_CMAKE_PATH | TO_NATIVE_PATH} <path> <out-var>)

Transfer
  file(DOWNLOAD <url> <file> [...])
  file(UPLOAD <file> <url> [...])

Locking
  file(LOCK <path> [...])
Rich von Lehe
  • 1,362
  • 2
  • 16
  • 38

1 Answers1

8

Unlike to add_custom_command, which COMMAND is executed on build stage, FILE() is executed immediately, at configuration stage when CMakeLists.txt is processed.

You may however put FILE() invocation into separate CMake script, and run this script with add_custom_command. With that approach the script with FILE() will be executed at build stage of your project, and you may use OUTPUT or TARGET option:

# File: my_script.cmake
file(...)

# File: CMakeLists.txt:
add_custom_command(OUTPUT | TARGET ...
    COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_SOURCE_DIR}/my_script.cmake"
    )

Aside from demonstrated CMake scripting mode (cmake -P) there is a command line mode (cmake -E), which can perform basic operations without needing to write a script.

# File: CMakeLists.txt:
add_custom_command(OUTPUT | TARGET ...
    COMMAND ${CMAKE_COMMAND} -E echo "<content-of-the-file>" > "</path/to/file>"
    )

See also that question about redirecting output in add_custom_command: How to redirect the output of a CMake custom command to a file?.

Tsyvarev
  • 60,011
  • 17
  • 110
  • 153
  • 1
    Is this really the only way to achieve this? It feels really dumb to do it. – Ben Farmer Nov 11 '19 at 14:25
  • 2
    CMake is **not a build tool**. After *configuration stage* is complete, CMake creates files for the **actual** build tool (Makefile, Visual Studio, etc.). So at the *build stage* CMake language is not supported directly. – Tsyvarev Nov 11 '19 at 20:02
  • Ok sure, but then isn't there a way in CMake to generate a Makefile that writes a file as we want? We can do it by add_custom_command and calling some command line tool to do it, but then it won't be portable. I guess what you suggest gets around that but it feels super hacky for such a simple task. – Ben Farmer Nov 12 '19 at 09:21
  • By itself, Make cannot create and write files, it should execute **external utilities** for doing that. *Scripting mode* of CMake (`cmake -P <...>`) is one of such utility, and it is very portable across the platforms. I don't understand why you oppose this approach. There is also a [command line mode](https://cmake.org/cmake/help/v3.16/manual/cmake.1.html#run-a-command-line-tool) of CMake, which perfoms simple actions without needing to write a script. – Tsyvarev Nov 12 '19 at 09:38
  • 1
    Well I don't oppose it, it just feels needlessly complex. I am not saying there *is* a better way, it just seemed like there should be. The command line mode is helpful though, saves having to have a whole other cmake script lying around just to create a file. – Ben Farmer Nov 12 '19 at 10:06
  • 1
    Ok, so your first comment was a hint to "search harder" and it seems to achieve its goal: I have updated my answer about using a CMake command line mode. This way is actually looks nicer and even fits for original author intentions. Note sure why I forgot about this way when initially wrote the answer. Thanks for remind me that! – Tsyvarev Nov 12 '19 at 11:21
  • To run another cmake with `-e` option in `add_custom_command`, you may need to add [`VERBATIM`](https://cmake.org/cmake/help/latest/command/add_custom_command.html) for escaping `echo` command string properly, otherwise the inner cmake command (and the `echo` command) will fail silently. – Ham Mar 08 '23 at 05:20