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.
This commit is contained in:
parent
eccfc0d185
commit
8c2cedc624
|
@ -76,9 +76,12 @@ The options are:
|
||||||
The optional ``ARGS`` argument is for backward compatibility and
|
The optional ``ARGS`` argument is for backward compatibility and
|
||||||
will be ignored.
|
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
|
: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``
|
(Use the ``TARGET_FILE``
|
||||||
:manual:`generator expression <cmake-generator-expressions(7)>` to
|
:manual:`generator expression <cmake-generator-expressions(7)>` to
|
||||||
reference an executable later in the command line.)
|
reference an executable later in the command line.)
|
||||||
|
|
|
@ -58,9 +58,12 @@ The options are:
|
||||||
:command:`file(GENERATE)` command to create it, and then specify
|
:command:`file(GENERATE)` command to create it, and then specify
|
||||||
a ``COMMAND`` to launch it.)
|
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
|
: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
|
Additionally a target-level dependency will be added so that the
|
||||||
executable target will be built before this custom target.
|
executable target will be built before this custom target.
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
CROSSCOMPILING_EMULATOR
|
CROSSCOMPILING_EMULATOR
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
Use the given emulator to run executables created when crosscompiling. This
|
Use the given emulator to run executables created when crosscompiling.
|
||||||
command will be added as a prefix to :command:`add_test` test commands for
|
This command will be added as a prefix to :command:`add_test`,
|
||||||
built target system executables.
|
:command:`add_custom_command`, and :command:`add_custom_target` commands
|
||||||
|
for built target system executables.
|
||||||
|
|
|
@ -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.
|
|
@ -38,6 +38,19 @@ unsigned int cmCustomCommandGenerator::GetNumberOfCommands() const
|
||||||
return static_cast<unsigned int>(this->CC.GetCommandLines().size());
|
return static_cast<unsigned int>(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
|
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 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<cmCompiledGeneratorExpression> cge = this->GE->Parse(argv0);
|
||||||
|
std::string exe = cge->Evaluate(this->LG, this->Config);
|
||||||
|
|
||||||
|
return exe;
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
|
@ -87,8 +112,13 @@ void
|
||||||
cmCustomCommandGenerator
|
cmCustomCommandGenerator
|
||||||
::AppendArguments(unsigned int c, std::string& cmd) const
|
::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];
|
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 =
|
std::string arg =
|
||||||
this->GE->Parse(commandLine[j])->Evaluate(this->LG,
|
this->GE->Parse(commandLine[j])->Evaluate(this->LG,
|
||||||
|
|
|
@ -36,6 +36,7 @@ public:
|
||||||
cmCustomCommand const& GetCC() const { return this->CC; }
|
cmCustomCommand const& GetCC() const { return this->CC; }
|
||||||
unsigned int GetNumberOfCommands() const;
|
unsigned int GetNumberOfCommands() const;
|
||||||
std::string GetCommand(unsigned int c) const;
|
std::string GetCommand(unsigned int c) const;
|
||||||
|
bool UseCrossCompilingEmulator(unsigned int c) const;
|
||||||
void AppendArguments(unsigned int c, std::string& cmd) const;
|
void AppendArguments(unsigned int c, std::string& cmd) const;
|
||||||
const char* GetComment() const;
|
const char* GetComment() const;
|
||||||
std::string GetWorkingDirectory() const;
|
std::string GetWorkingDirectory() const;
|
||||||
|
|
|
@ -289,8 +289,10 @@ if(CMake_TEST_FindMatlab)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_executable(pseudo_emulator pseudo_emulator.c)
|
add_executable(pseudo_emulator pseudo_emulator.c)
|
||||||
|
add_executable(pseudo_emulator_custom_command pseudo_emulator_custom_command.c)
|
||||||
add_RunCMake_test(CrosscompilingEmulator
|
add_RunCMake_test(CrosscompilingEmulator
|
||||||
-DPSEUDO_EMULATOR=$<TARGET_FILE:pseudo_emulator>)
|
-DPSEUDO_EMULATOR=$<TARGET_FILE:pseudo_emulator>
|
||||||
|
-DPSEUDO_EMULATOR_CUSTOM_COMMAND=$<TARGET_FILE:pseudo_emulator_custom_command>)
|
||||||
# Xcode 2.x forgets to create the output directory before linking
|
# Xcode 2.x forgets to create the output directory before linking
|
||||||
# the individual architectures.
|
# the individual architectures.
|
||||||
if(CMAKE_OSX_ARCHITECTURES AND XCODE AND NOT "${XCODE_VERSION}" MATCHES "^[^12]")
|
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
|
TARGET pseudo_emulator
|
||||||
PRE_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_CFG_INTDIR}"
|
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()
|
endif()
|
||||||
|
|
||||||
if("${CMAKE_GENERATOR}" MATCHES "Make|Ninja")
|
if("${CMAKE_GENERATOR}" MATCHES "Make|Ninja")
|
||||||
|
|
|
@ -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()
|
|
@ -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 "$<TARGET_FILE:generated_exe_emulator_unexpected>"
|
||||||
|
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 $<TARGET_FILE:generated_exe_emulator_unexpected>
|
||||||
|
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
|
||||||
|
)
|
|
@ -0,0 +1 @@
|
||||||
|
include(${CMAKE_CURRENT_LIST_DIR}/AddCustomCommand-build-check.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 "$<TARGET_FILE:generated_exe_emulator_unexpected>"
|
||||||
|
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
|
||||||
|
$<TARGET_FILE:generated_exe_emulator_unexpected>
|
||||||
|
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)
|
|
@ -6,3 +6,18 @@ set(RunCMake_TEST_OPTIONS
|
||||||
run_cmake(CrosscompilingEmulatorProperty)
|
run_cmake(CrosscompilingEmulatorProperty)
|
||||||
run_cmake(TryRun)
|
run_cmake(TryRun)
|
||||||
run_cmake(AddTest)
|
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)
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
int main(int, char **)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
// Usage:
|
||||||
|
//
|
||||||
|
// /path/to/program arg1 [arg2 [...]]
|
||||||
|
//
|
||||||
|
// Return EXIT_SUCCESS if 'generated_exe_emulator_expected'
|
||||||
|
// string was found in <arg1>.
|
||||||
|
// Return EXIT_FAILURE if 'generated_exe_emulator_unexpected'
|
||||||
|
// string was found in <arg1>.
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
Loading…
Reference in New Issue