-1

How can I force an add_test() to build an add_executable() or add_custom_target() target before it runs?

I have a very complicated cmake flow because some of my binaries (created with add_executable() on the host GENERATE source code that I use in later add_executable() declarations for a number of cross-compile platforms.

The gist of my question supposes I have a subdirectory of my cmake project containing this...

set(CMAKE_C_COMPILER /path/to/cross-compiler/bin/gcc)
add_executable(foo.elf)
...
add_test(test_foo COMMAND foo_script.sh foo.elf)
set_tests_properties(test_foo PROPERTIES
  DEPENDS(foo.elf) ...)  # Build "foo.elf" before running "test_foo"

I understand about chains of add_test() dependencies possible with FIXTURES_REQUIRED, that allows me to say that one test must run after another test. I don't see any way to express that a test requires that a "target" must be built before running the test.

The code above does not have any errors/warnings with my cmake command, but with ninja it simply acts as if the DEPENDS is not present.

How can I force an add_test() to build an add_executable() or add_custom_target() target before it runs?

To clarify a bit for @user, I cannot have a single "build phase". I need a "first build phase" who's "first generate phase" will create a C file. Then I need a "second build phase" (with a cross-compiler) to create an executable. Then another add_test() to run that executable on a platform.

EDIT:

Okay, I got this working.

For @Tsyvarev, I built a toolchain file for each of my platforms, and I called my "elf" target "elf" with filename "elf". I understand the SUFFIX property could choose a filename suffix, and I used $<TARGET_FILE:elf> everywhere to refer to that file.

I used @KamilCuk's approach. My project used configure_file() to create a totally new cmake project within the build tree of the main project. That project is only responsible for compiling the generated C file into the platform-specific elf file.

The add_test() command is a script that asks the main project to generate the C file, then create the subproject for it, then generate the elf, then the main project runs the test on the platform.

Thanks for your help! As you can tell, I had trouble even asking the right question at the start of this...

Lance E.T. Compte
  • 932
  • 1
  • 11
  • 33
  • 2
    why does your executable target have ".elf" in its name? – starball Apr 23 '23 at 07:35
  • the build and test phases are separate. See also https://cmake.org/cmake/help/latest/manual/ctest.1.html#cmdoption-ctest-build-and-test. I'm a bit confused about this question. – starball Apr 23 '23 at 07:37
  • @user, The executable target has ".elf" in the name because it was built with a cross-compiler. It will not run on the host machine where cmake is running. – Lance E.T. Compte Apr 23 '23 at 16:29
  • 1
    @LanceE.T.Compte: In CMake **targets** are not **files** and usually are named without "extension". It is possible to add extension to the file, generated by a target, by setting [SUFFIX](https://cmake.org/cmake/help/latest/prop_tgt/SUFFIX.html) property. A toolchain may also affect on the executable's suffix by setting variable [CMAKE_EXECUTABLE_SUFFIX](https://cmake.org/cmake/help/latest/variable/CMAKE_EXECUTABLE_SUFFIX.html). BTW, a **cross-compilation** should be expressed via **toolchain**; your setting of `CMAKE_C_COMPILER` is plain wrong: https://stackoverflow.com/a/63944545/3440745. – Tsyvarev Apr 23 '23 at 17:08
  • @Tsyvarev, You are right about targets/files. I just generally name them the same for simplicity. For toolchains, how can I have a single cmake project that uses a four different C cross-compilers/assemblers and two C++ compilers? I thank you for your help!, but how can I make a "target" into a dependency of an `add_test()`. FIXTURES just express dependencies of other tests.... – Lance E.T. Compte Apr 23 '23 at 17:16
  • `I need a "first build phase" who's "first generate phase" will create a C file. Then I need a "second build phase" (with a cross-compiler) to create an executable.` That's one build phase. In the first subphase you create a C file, then compile, that's completely normal. So are you writing a test __or__ do you want to generate a C file for compilation? That's different. I do not understand. `".elf" in the name because it was built with a cross-compiler` Do not use `.elf` in the name, there is no reason to. – KamilCuk Apr 23 '23 at 18:18
  • Note your question is `How can I force an add_test() to build an add_executable() or add_custom_target() target before it runs?`. _Not_ how to generate a file or chain it. – KamilCuk Apr 23 '23 at 18:19
  • 1
    "For toolchains, how can I have a single cmake project that uses a four different C cross-compilers/assemblers and two C++ compilers?" - You cannot/shouldn't. If you want to build host tools and then use them in building files for remote machine, then you could create a separate project which creates these host tools and [export](https://cmake.org/cmake/help/latest/command/export.html) its build tree. In the cross-compile project you may include that build tree via `find_package`. – Tsyvarev Apr 24 '23 at 07:57

1 Answers1

1

How can I force an add_test() to build an add_executable() or add_custom_target() target before it runs?

Do exactly that, verbatim. Run the build, then execute your command. You need a script, or executable, that will wrap that in one command. You can for example use bash.

add_test(test_foo COMMAND
   bash
   -c
   "\"$1\" --build \"$2\" --target foo.elf && foo_script.sh \"$3\""
   --
   ${CMAKE_COMMAND}
   ${CMAKE_BINARY_DIR}
   $<TARGET_FILE:foo.elf>
)
KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • I get this approach, but you're kindof bypassing cmake. I have a (generated) C file and wanted to create the executable using cmake. Maybe I could use this approach to GENERATE another cmake (sub)project for that and build/generate it as a target... Yes? I could also fix @Tsyvarev's concerns about toolchains this way. – Lance E.T. Compte Apr 23 '23 at 18:37
  • Consider asking another question where you explain that problem. See https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem and https://meta.stackoverflow.com/questions/266767/what-is-the-the-best-way-to-ask-follow-up-questions . I do not understand your problem - that's a normal file, just compile it then. Also, cmake is very widely used. There is very high chance that your question has been asked and implemented 1000s times already - search this forum first. I myself have written CMake that compiled a C source file that generated a C source file that was then compiled. – KamilCuk Apr 23 '23 at 18:45
  • 1
    `Maybe I could use this approach to GENERATE another cmake (sub)project` While you could, sounds like an overkill. One would use a `add_custom_command`. However, you can't change targets after generation CMake phase, so you would have to chain two CMake generation+build phases if the list of targets is dynamic - where I would prefer to use an outside short Makefile script rather then `execute_process()` with CMake code. I doubt this is related to your problem, as I suspect the list of files is static, which would be great, if you would ask another question just with your problem. – KamilCuk Apr 23 '23 at 18:46
  • I used a modification of this approach (described as an edit at the end of the question). Thanks again! – Lance E.T. Compte Apr 24 '23 at 19:39