using cross emulator with custom target and ctest

First some info dump in case it may matter:

  • cmake: 3.21.1
  • generator: Unix Makefiles
  • host: linux
  • toolchain: mingw-w64
  • toolchain file contains: set(CMAKE_CROSSCOMPILING_EMULATOR "WINEPATH=Z:\\usr\\x86_64-w64-mingw32\\usr\\bin\\" "/usr/bin/wine")
    Winepath is required as there are a few dlls in the cross-root needed for the targets to run.
  • CMAKE_CROSSCOMPILING gets properly set to true

There are 2 goals here, the first is to run one of the build targets to generate some data using add_custom_target

add_custom_target(data.zip ALL
  COMMAND $<TARGET_FILE:myexe> arg1 argN)
add_dependencies(data.zip myexe)

How do I get cmake to run the the above using the emulator/wine? Based on scattered snippets in the official docs and SO posts it might just work. Unfortunately it does not. It’s as if the setting is ignored. Tried all sort of stuff, to no avail. Surely missing something possibly obvious.

The second goal is to run unit tests using the emulator by running ctest. The setup works well for native builds. If I run ctest using the cross toolchain file it looks for the test executables in the right place but doesn’t add the required file extension .exe to the path, and again the emulator setting is ignored.

CMAKE_CROSSCOMPILING_EMULATOR is, AFAIK, only used in tests or when CMake does its own “this is a target name” replacement. You’ll probably need to do:

add_custom_target(data.zip ALL
  COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $<TARGET_FILE:myexe> arg1 argN)
add_dependencies(data.zip myexe)

Thanks for the your answer.

So about add_custom_target above you say it could reasonably work just that it’s not yet implemented to your knowledge or the $<TARGET_FILE:myexe> isn’t sufficient to trigger a “this is a target name” replacement.

Working around it can certainly be done one way or another, I used the inelegant COMMAND run-my-command.sh for no better idea.


So test registered with add_test(test test.cpp) should use the emulator when running ctest from console, unfortunately I didn’t get that to work either. Further such tests get called by their unix path, ie no .exe is appended to those targets which cmake generated “knowing” it’s cross compiling to Windows.

I can work around this as well (an other shell script), just think it should probably be handled cleanly by cmake and I can’t figure out where I’m off in my attempt or whether it isn’t expected to work in the first place.

Regards

That shouldn’t be necessary according to the docs for add_custom_target(), which state:

If COMMAND specifies an executable target name (created by the add_executable() command), it will automatically be replaced by the location of the executable created at build time if either of the following is true:

  • The target is not being cross-compiled (i.e. the CMAKE_CROSSCOMPILING variable is not set to true).
  • New in version 3.6: The target is being cross-compiled and an emulator is provided (i.e. its CROSSCOMPILING_EMULATOR target property is set). In this case, the contents of CROSSCOMPILING_EMULATOR will be prepended to the command before the location of the target executable.

In other words, I would expect the following to automatically prepend the emulator if one is set on the myexe target:

add_custom_target(data.zip ALL
  COMMAND myexe arg1 argN
)
1 Like

Thanks

add_custom_target(data.zip ALL
  COMMAND myexe arg1 argN
)

Triggers the “run with emulator”.

So explicit specification of target with $<TARGET_FILE:myexe> doesn’t work, only implicit specification. This is unexpected from a user point of view I’d argue and not easy to infer how to get it to work. Any chance this could be fixed reasonably in a future version?

For other readers my CROSSCOMPILING_EMULATOR setting above is treated as a list but doesn’t get expanded. So had to place it into an external wrapper script and use this as emulator which is at least easy to infer.

#!/usr/bin/env sh
WINEPATH=$(winepath -w /usr/x86_64-w64-mingw32/usr/bin/) /usr/bin/wine $1

This leaves the the tests which seems to be a different issue.

Regards

Ok, tracked it down to CXXTEST_ADD_TEST macro. As above only with magic substitution will the emulator be triggered.

Either FindCxxTest module is to be considered broken or cmake needs to become more clever about what is a previously built target.

Marking as solved.

Thanks to all participants. Regards

Hi, could you elaborate a bit please. I’m using the CXXTEST_ADD_TEST macro and would like it to run the unit tests using the emulator I specified in CMAKE_CROSSCOMPILING_EMULATOR. According to the documentation for CROSSCOMPILING_EMULATOR, add_test(), which is called by CxxTest, uses that target property as a prefix to run tests using the emulator. That sounds great! However, that does not happen! :frowning:
And yes, I have verified that the CROSSCOMPILING_EMULATOR property of the unit test-target is set. And yes, the emulator runs unit tests in gtests - but I’d like it to run cxxtests as well.

So, obviously there is a workaround using “magic substitution” that will cause the emulator to be triggered. But I fail to understand what that substitution is, can you please give an explanation? It would be much appreciated! :slight_smile:

My host machine is a PC running Ubuntu. The crosscompilation is for a target system running our Linux on an ARM-processor, the emulator is qemu-arm.

Hi,

not that fresh anymore. Well, I replaced CXXTEST_ADD_TEST with a custom version. Looking at FindCxxTest.cmake replacing

    if(CMAKE_RUNTIME_OUTPUT_DIRECTORY)
        add_test(${_cxxtest_testname} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${_cxxtest_testname})
    elseif(EXECUTABLE_OUTPUT_PATH)
        add_test(${_cxxtest_testname} ${EXECUTABLE_OUTPUT_PATH}/${_cxxtest_testname})
    else()
        add_test(${_cxxtest_testname} ${CMAKE_CURRENT_BINARY_DIR}/${_cxxtest_testname})
    endif()

with

add_test(NAME ${_cxxtest_testname} COMMAND ${_cxxtest_testname})

might do already for you, as absolute paths don’t qualify for “magic substitution”.

I had to at least fix file extensions on top due to unix → windows emulation, maybe more.

Great, doing the same change here made it work! Thank you @sera! :slight_smile:

I think that the documentation of the CROSSCOMPILING_EMULATOR should probably state that the plain add_test() does NOT work with emulator and that add_test(NAME,COMMAND) only works with emulator if paths are not supplied. Or? @craig.scott

(Edit: I forgot the crucial NOT in add_test does work.)

I think both only work if add_test does its “the first argument is a target name, so I’ll replace it with the target file’s path instead” detection. Documentation clarification/enhancement would be appreciated.

The docs for both CROSSCOMPILING_EMULATOR and add_test() should probably be updated with this info. Merge requests welcome. :wink:

1 Like

as to the add_test, say I have:

add_exexcutable(MyTestExe …)

The emulator game fails when we do:
add_test(
NAME MyTestExe
COMMAND $<TARGET_FILE:MyTestExe>)

but does work when: (from what I read here, and what I have heard from a colleague)
add_test(
NAME MyTestExe
COMMAND MyTestExe)

A few questions:

  • is this a bug ?
  • if so could it be solved soon ?
  • apart from this problem what is the preferred way : $<TARGET_FILE:xxxx> of just xxxx ? Different books which is I think are very valuable are not consistent in this. Can somebody give better insights or advise ?

I’d say if you expect the emulator to be used, specify the target name as the executable for the test’s COMMAND. The logic that prepends the emulator requires a target as the command because it gets the emulator details from that target. If you provide a generator expression instead, the test only sees the binary that gets created, not the target.

As for which is preferred, I’d always just specify the target if that gives you what you need. Why add the extra complexity of the generator expression if it isn’t needed and doesn’t give you anything that putting the plain target does?

Guess “COMMAND” hints to use a command instead of a target name. Once you are aware, using target name is fine.

To avoid that confusion, would it be interesting that a “TARGET” directive is added to the syntax ?
We could even go for simpler syntax, where the test name could be inherited from the target ?

add_executable(MyTestExe …)

add_test(
NAME MyTestExe
TARGET MyTestExe
)

or

add_test(
TARGET MyTestExe
)

Using TARGET won’t fit well with use of additional arguments either. In the end it’s still a command, just that if it contains a target name then magic happens.