From 8c2cedc6243b281a0814b284abbcd1c45c42b085 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Wed, 4 May 2016 13:30:19 -0400 Subject: [PATCH] CustomCommandGenerator: Add support for CROSSCOMPILING_EMULATOR Teach the `add_custom_command` and `add_custom_target' commands to substitute argv0 with the crosscompiling emulator if it is a target with the `CROSSCOMPILING_EMULATOR` property set. --- Help/command/add_custom_command.rst | 7 +++- Help/command/add_custom_target.rst | 7 +++- Help/prop_tgt/CROSSCOMPILING_EMULATOR.rst | 7 ++-- ...custom-command-CROSSCOMPILING_EMULATOR.rst | 6 +++ Source/cmCustomCommandGenerator.cxx | 34 ++++++++++++++++- Source/cmCustomCommandGenerator.h | 1 + Tests/RunCMake/CMakeLists.txt | 8 +++- .../AddCustomCommand-build-check.cmake | 5 +++ .../AddCustomCommand.cmake | 38 +++++++++++++++++++ .../AddCustomTarget-build-check.cmake | 1 + .../AddCustomTarget.cmake | 30 +++++++++++++++ .../CrosscompilingEmulator/RunCMakeTest.cmake | 15 ++++++++ .../simple_src_exitsuccess.cxx | 4 ++ .../RunCMake/pseudo_emulator_custom_command.c | 34 +++++++++++++++++ 14 files changed, 187 insertions(+), 10 deletions(-) create mode 100644 Help/release/dev/custom-command-CROSSCOMPILING_EMULATOR.rst create mode 100644 Tests/RunCMake/CrosscompilingEmulator/AddCustomCommand-build-check.cmake create mode 100644 Tests/RunCMake/CrosscompilingEmulator/AddCustomCommand.cmake create mode 100644 Tests/RunCMake/CrosscompilingEmulator/AddCustomTarget-build-check.cmake create mode 100644 Tests/RunCMake/CrosscompilingEmulator/AddCustomTarget.cmake create mode 100644 Tests/RunCMake/CrosscompilingEmulator/simple_src_exitsuccess.cxx create mode 100644 Tests/RunCMake/pseudo_emulator_custom_command.c diff --git a/Help/command/add_custom_command.rst b/Help/command/add_custom_command.rst index 8726b709e..d42136432 100644 --- a/Help/command/add_custom_command.rst +++ b/Help/command/add_custom_command.rst @@ -76,9 +76,12 @@ The options are: The optional ``ARGS`` argument is for backward compatibility and will be ignored. - If ``COMMAND`` specifies an executable target (created by the + If ``COMMAND`` specifies an executable target name (created by the :command:`add_executable` command) it will automatically be replaced - by the location of the executable created at build time. + by the location of the executable created at build time. If set, the + :prop_tgt:`CROSSCOMPILING_EMULATOR` executable target property will + also be prepended to the command to allow the executable to run on + the host. (Use the ``TARGET_FILE`` :manual:`generator expression ` to reference an executable later in the command line.) diff --git a/Help/command/add_custom_target.rst b/Help/command/add_custom_target.rst index 82d69db9f..6980d617a 100644 --- a/Help/command/add_custom_target.rst +++ b/Help/command/add_custom_target.rst @@ -58,9 +58,12 @@ The options are: :command:`file(GENERATE)` command to create it, and then specify a ``COMMAND`` to launch it.) - If ``COMMAND`` specifies an executable target (created by the + If ``COMMAND`` specifies an executable target name (created by the :command:`add_executable` command) it will automatically be replaced - by the location of the executable created at build time. + by the location of the executable created at build time. If set, the + :prop_tgt:`CROSSCOMPILING_EMULATOR` executable target property will + also be prepended to the command to allow the executable to run on + the host. Additionally a target-level dependency will be added so that the executable target will be built before this custom target. diff --git a/Help/prop_tgt/CROSSCOMPILING_EMULATOR.rst b/Help/prop_tgt/CROSSCOMPILING_EMULATOR.rst index 3ef8e0308..d30a2f2b6 100644 --- a/Help/prop_tgt/CROSSCOMPILING_EMULATOR.rst +++ b/Help/prop_tgt/CROSSCOMPILING_EMULATOR.rst @@ -1,6 +1,7 @@ CROSSCOMPILING_EMULATOR ----------------------- -Use the given emulator to run executables created when crosscompiling. This -command will be added as a prefix to :command:`add_test` test commands for -built target system executables. +Use the given emulator to run executables created when crosscompiling. +This command will be added as a prefix to :command:`add_test`, +:command:`add_custom_command`, and :command:`add_custom_target` commands +for built target system executables. diff --git a/Help/release/dev/custom-command-CROSSCOMPILING_EMULATOR.rst b/Help/release/dev/custom-command-CROSSCOMPILING_EMULATOR.rst new file mode 100644 index 000000000..390463ec4 --- /dev/null +++ b/Help/release/dev/custom-command-CROSSCOMPILING_EMULATOR.rst @@ -0,0 +1,6 @@ +custom-command-CROSSCOMPILING_EMULATOR +-------------------------------------- + +* The :command:`add_custom_command` and :command:`add_custom_target` commands + learned how to use the :prop_tgt:`CROSSCOMPILING_EMULATOR` executable + target property. diff --git a/Source/cmCustomCommandGenerator.cxx b/Source/cmCustomCommandGenerator.cxx index 5d3a1cecc..81c51424a 100644 --- a/Source/cmCustomCommandGenerator.cxx +++ b/Source/cmCustomCommandGenerator.cxx @@ -38,6 +38,19 @@ unsigned int cmCustomCommandGenerator::GetNumberOfCommands() const return static_cast(this->CC.GetCommandLines().size()); } +//---------------------------------------------------------------------------- +bool cmCustomCommandGenerator::UseCrossCompilingEmulator(unsigned int c) const +{ + std::string const& argv0 = this->CC.GetCommandLines()[c][0]; + cmGeneratorTarget* target = + this->LG->FindGeneratorTargetToUse(argv0); + if(target && target->GetType() == cmState::EXECUTABLE) + { + return target->GetProperty("CROSSCOMPILING_EMULATOR") != 0; + } + return false; +} + //---------------------------------------------------------------------------- std::string cmCustomCommandGenerator::GetCommand(unsigned int c) const { @@ -50,7 +63,19 @@ std::string cmCustomCommandGenerator::GetCommand(unsigned int c) const { return target->GetLocation(this->Config); } - return this->GE->Parse(argv0)->Evaluate(this->LG, this->Config); + if (target && target->GetType() == cmState::EXECUTABLE) + { + const char* emulator = target->GetProperty("CROSSCOMPILING_EMULATOR"); + if (emulator) + { + return std::string(emulator); + } + } + + cmsys::auto_ptr cge = this->GE->Parse(argv0); + std::string exe = cge->Evaluate(this->LG, this->Config); + + return exe; } //---------------------------------------------------------------------------- @@ -87,8 +112,13 @@ void cmCustomCommandGenerator ::AppendArguments(unsigned int c, std::string& cmd) const { + unsigned int offset = 1; + if (this->UseCrossCompilingEmulator(c)) + { + offset = 0; + } cmCustomCommandLine const& commandLine = this->CC.GetCommandLines()[c]; - for(unsigned int j=1;j < commandLine.size(); ++j) + for(unsigned int j=offset;j < commandLine.size(); ++j) { std::string arg = this->GE->Parse(commandLine[j])->Evaluate(this->LG, diff --git a/Source/cmCustomCommandGenerator.h b/Source/cmCustomCommandGenerator.h index a637fed7c..65ce03143 100644 --- a/Source/cmCustomCommandGenerator.h +++ b/Source/cmCustomCommandGenerator.h @@ -36,6 +36,7 @@ public: cmCustomCommand const& GetCC() const { return this->CC; } unsigned int GetNumberOfCommands() const; std::string GetCommand(unsigned int c) const; + bool UseCrossCompilingEmulator(unsigned int c) const; void AppendArguments(unsigned int c, std::string& cmd) const; const char* GetComment() const; std::string GetWorkingDirectory() const; diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index 02e14e6b4..d16e5e7c6 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -289,8 +289,10 @@ if(CMake_TEST_FindMatlab) endif() add_executable(pseudo_emulator pseudo_emulator.c) +add_executable(pseudo_emulator_custom_command pseudo_emulator_custom_command.c) add_RunCMake_test(CrosscompilingEmulator - -DPSEUDO_EMULATOR=$) + -DPSEUDO_EMULATOR=$ + -DPSEUDO_EMULATOR_CUSTOM_COMMAND=$) # Xcode 2.x forgets to create the output directory before linking # the individual architectures. if(CMAKE_OSX_ARCHITECTURES AND XCODE AND NOT "${XCODE_VERSION}" MATCHES "^[^12]") @@ -298,6 +300,10 @@ if(CMAKE_OSX_ARCHITECTURES AND XCODE AND NOT "${XCODE_VERSION}" MATCHES "^[^12]" TARGET pseudo_emulator PRE_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_CFG_INTDIR}" ) + add_custom_command( + TARGET pseudo_emulator_custom_command + PRE_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_CFG_INTDIR}" + ) endif() if("${CMAKE_GENERATOR}" MATCHES "Make|Ninja") diff --git a/Tests/RunCMake/CrosscompilingEmulator/AddCustomCommand-build-check.cmake b/Tests/RunCMake/CrosscompilingEmulator/AddCustomCommand-build-check.cmake new file mode 100644 index 000000000..e10b161ac --- /dev/null +++ b/Tests/RunCMake/CrosscompilingEmulator/AddCustomCommand-build-check.cmake @@ -0,0 +1,5 @@ +foreach(output IN ITEMS output1 output2 output3 output4) + if(NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/${output}") + message(FATAL_ERROR "Failed to create output: ${RunCMake_TEST_BINARY_DIR}/${output}") + endif() +endforeach() diff --git a/Tests/RunCMake/CrosscompilingEmulator/AddCustomCommand.cmake b/Tests/RunCMake/CrosscompilingEmulator/AddCustomCommand.cmake new file mode 100644 index 000000000..67fa30fdf --- /dev/null +++ b/Tests/RunCMake/CrosscompilingEmulator/AddCustomCommand.cmake @@ -0,0 +1,38 @@ +set(CMAKE_CROSSCOMPILING 1) + +# Executable: Return error code different from 0 +add_executable(generated_exe_emulator_expected simple_src_exiterror.cxx) + +# Executable: Return error code equal to 0 +add_executable(generated_exe_emulator_unexpected simple_src_exitsuccess.cxx) + +# DoesNotUseEmulator +add_custom_command(OUTPUT output1 + COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/output1) + +# DoesNotUseEmulator: The command will fail if emulator is prepended +add_custom_command(OUTPUT output2 + COMMAND ${CMAKE_COMMAND} -E echo "$" + COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/output2 + DEPENDS generated_exe_emulator_unexpected) + +# DoesNotUseEmulator: The command will fail if emulator is prepended +add_custom_command(OUTPUT output3 + COMMAND $ + COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/output3 + DEPENDS generated_exe_emulator_unexpected) + +# UsesEmulator: The command only succeeds if the emulator is prepended +# to the command. +add_custom_command(OUTPUT output4 + COMMAND generated_exe_emulator_expected + COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/output4 + DEPENDS generated_exe_emulator_expected) + +add_custom_target(ensure_build ALL + SOURCES + ${CMAKE_CURRENT_BINARY_DIR}/output1 + ${CMAKE_CURRENT_BINARY_DIR}/output2 + ${CMAKE_CURRENT_BINARY_DIR}/output3 + ${CMAKE_CURRENT_BINARY_DIR}/output4 +) diff --git a/Tests/RunCMake/CrosscompilingEmulator/AddCustomTarget-build-check.cmake b/Tests/RunCMake/CrosscompilingEmulator/AddCustomTarget-build-check.cmake new file mode 100644 index 000000000..c62192216 --- /dev/null +++ b/Tests/RunCMake/CrosscompilingEmulator/AddCustomTarget-build-check.cmake @@ -0,0 +1 @@ +include(${CMAKE_CURRENT_LIST_DIR}/AddCustomCommand-build-check.cmake) diff --git a/Tests/RunCMake/CrosscompilingEmulator/AddCustomTarget.cmake b/Tests/RunCMake/CrosscompilingEmulator/AddCustomTarget.cmake new file mode 100644 index 000000000..ced569fd4 --- /dev/null +++ b/Tests/RunCMake/CrosscompilingEmulator/AddCustomTarget.cmake @@ -0,0 +1,30 @@ +set(CMAKE_CROSSCOMPILING 1) + +# Executable: Return error code different from 0 +add_executable(generated_exe_emulator_expected simple_src_exiterror.cxx) + +# Executable: Return error code equal to 0 +add_executable(generated_exe_emulator_unexpected simple_src_exitsuccess.cxx) + +# DoesNotUseEmulator +add_custom_target(generate_output1 ALL + ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/output1) + +# DoesNotUseEmulator: The command will fail if emulator is prepended +add_custom_target(generate_output2 ALL + ${CMAKE_COMMAND} -E echo "$" + COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/output2 + DEPENDS generated_exe_emulator_unexpected) + +# DoesNotUseEmulator: The command will fail if emulator is prepended +add_custom_target(generate_output3 ALL + $ + COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/output3 + DEPENDS generated_exe_emulator_unexpected) + +# UsesEmulator: The command only succeeds if the emulator is prepended +# to the command. +add_custom_target(generate_output4 ALL + generated_exe_emulator_expected + COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/output4 + DEPENDS generated_exe_emulator_expected) diff --git a/Tests/RunCMake/CrosscompilingEmulator/RunCMakeTest.cmake b/Tests/RunCMake/CrosscompilingEmulator/RunCMakeTest.cmake index 2581cfcc2..71aaad19b 100644 --- a/Tests/RunCMake/CrosscompilingEmulator/RunCMakeTest.cmake +++ b/Tests/RunCMake/CrosscompilingEmulator/RunCMakeTest.cmake @@ -6,3 +6,18 @@ set(RunCMake_TEST_OPTIONS run_cmake(CrosscompilingEmulatorProperty) run_cmake(TryRun) run_cmake(AddTest) + +function(CustomCommandGenerator_run_and_build case) + # Use a single build tree for a few tests without cleaning. + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${case}-build) + set(RunCMake_TEST_NO_CLEAN 1) + set(RunCMake_TEST_OPTIONS + "-DCMAKE_CROSSCOMPILING_EMULATOR=${PSEUDO_EMULATOR_CUSTOM_COMMAND}") + file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") + file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") + run_cmake(${case}) + run_cmake_command(${case}-build ${CMAKE_COMMAND} --build .) +endfunction() + +CustomCommandGenerator_run_and_build(AddCustomCommand) +CustomCommandGenerator_run_and_build(AddCustomTarget) diff --git a/Tests/RunCMake/CrosscompilingEmulator/simple_src_exitsuccess.cxx b/Tests/RunCMake/CrosscompilingEmulator/simple_src_exitsuccess.cxx new file mode 100644 index 000000000..630adc6bc --- /dev/null +++ b/Tests/RunCMake/CrosscompilingEmulator/simple_src_exitsuccess.cxx @@ -0,0 +1,4 @@ +int main(int, char **) +{ + return 0; +} diff --git a/Tests/RunCMake/pseudo_emulator_custom_command.c b/Tests/RunCMake/pseudo_emulator_custom_command.c new file mode 100644 index 000000000..17181c987 --- /dev/null +++ b/Tests/RunCMake/pseudo_emulator_custom_command.c @@ -0,0 +1,34 @@ +#include +#include +#include + +// Usage: +// +// /path/to/program arg1 [arg2 [...]] +// +// Return EXIT_SUCCESS if 'generated_exe_emulator_expected' +// string was found in . +// Return EXIT_FAILURE if 'generated_exe_emulator_unexpected' +// string was found in . + +int main(int argc, const char* argv[]) +{ + const char* substring_failure = "generated_exe_emulator_unexpected"; + const char* substring_success = "generated_exe_emulator_expected"; + const char* str = argv[1]; + if (argc < 2) + { + return EXIT_FAILURE; + } + if (strstr(str, substring_success) != 0) + { + return EXIT_SUCCESS; + } + if (strstr(str, substring_failure) != 0) + { + return EXIT_FAILURE; + } + fprintf(stderr, "Failed to find string '%s' in '%s'\n", + substring_success, str); + return EXIT_FAILURE; +}