I can not get environment at custom target shell - cmake

I can not get environment at custom target shell.
CMakeList.txt
set( ENV{TEST_VAR} "Hello" )
add_custom_target( test
COMMAND ./test.sh
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} )
test.sh
echo test:${TEST_VAR}
when try to "make test", shell can't get ${TEST_VAR}.
Thank you.

You have to use a trick because environment variables SET in the CMakeLists.txt only take effect for cmake itself, so you cannot use this method to set an environment variable that a custom command might need:
test.cmake
set( ENV{TEST_VAR} "Hello" )
execute_process(
COMMAND ./test.sh
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} )
CMakeLists.txt
add_custom_target( test
COMMAND ${CMAKE_COMMAND} -P test.cmake )

Related

Passing environment variable to the COMMAND in CMake execute_process

I have the following CMake snippet that runs COMMAND in WORKING_DIRECTORY. I tried different ways to pass the environment variable (MBEDTLS_INCLUDE_DIR=${CMAKE_CURRENT_SOURCE_DIR}/../mbedtls/mbedtls/include) but without success.
The snippet that works (without env variable):
set(BUILD_CMD cargo build --features parsec-client/no-fs-permission-check)
set(WORKING_DIR "${CMAKE_CURRENT_SOURCE_DIR}/parsec_se_driver")
execute_process( COMMAND ${BUILD_CMD}
RESULT_VARIABLE CMD_ERROR
WORKING_DIRECTORY ${WORKING_DIR} )
if(NOT ${CMD_ERROR} MATCHES "0")
MESSAGE(SEND_ERROR "BUILD_CMD STATUS:" ${CMD_ERROR})
endif()
How can I pass the env variable to the execute_process?
If I write something like this:
execute_process( COMMAND MBEDTLS_INCLUDE_DIR=${CMAKE_CURRENT_SOURCE_DIR}/../mbedtls/mbedtls/include cargo build --features parsec-client/no-fs-permission-check
RESULT_VARIABLE CMD_ERROR
WORKING_DIRECTORY ${WORKING_DIR} )
or taking different parts to variables, or adding quotes, I get:
BUILD_CMD STATUS:No such file or directory
As recommended in the CMake mailing list here, your solution using set(ENV ...) is perfectly valid:
set(ENV{MBEDTLS_INCLUDE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../mbedtls/mbedtls/include)
execute_process(
COMMAND ${BUILD_CMD}
RESULT_VARIABLE CMD_ERROR
WORKING_DIRECTORY ${WORKING_DIR}
)
You could also use CMake's command line utility to run the command in a modified environment using cmake -E env:
execute_process(
COMMAND ${CMAKE_COMMAND} -E env
MBEDTLS_INCLUDE_DIR="${CMAKE_CURRENT_SOURCE_DIR}/../mbedtls/mbedtls/include" ${BUILD_CMD}
RESULT_VARIABLE CMD_ERROR
WORKING_DIRECTORY ${WORKING_DIR}
)

How to append command to add_custom_target in CMake

Suppose I have a custom target in CMake for unit tests like the below
add_custom_target(
test
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/ATest
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/BTest
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/CTest
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/DTest)
but I want to add an additional test to the target based on whether an external dependency is found. Currently, I did it with
if(EXTERNAL_FOUND)
add_custom_target(
test
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/ATest
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/BTest
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/CTest
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/DTest
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/ETest)
else()
add_custom_target(
test
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/ATest
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/BTest
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/CTest
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/DTest)
endif()
This is not very elegant and it quickly becomes unmanageable when there are multiple conditions. Is there something like append to custom target so we can write the below instead?
add_custom_target(
test
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/ATest
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/BTest
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/CTest
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/DTest)
if(EXTERNAL_FOUND)
# I can't seem to find something like this
append_custom_target(test COMMAND ${CMAKE_CURRENT_BINARY_DIR}/ETest)
else()
Or is there a better way to do this?
You can use add_custom_command and use it as dependency to your target. With the custom command you can APPEND commands with same OUTPUT:
add_custom_target(
test
DEPENDS test-cmd
)
add_custom_command(
OUTPUT test-cmd
COMMAND ${CMAKE_COMMAND} -E echo "ATest"
COMMAND ${CMAKE_COMMAND} -E echo "BTest"
COMMAND ${CMAKE_COMMAND} -E echo "CTest"
COMMAND ${CMAKE_COMMAND} -E echo "DTest"
)
if(EXTERNAL_FOUND)
add_custom_command(
OUTPUT test-cmd APPEND
COMMAND ${CMAKE_COMMAND} -E echo "ETest"
)
endif()
# test-cmd is not actually generated so set it to symbolic
set_source_files_properties(test-cmd PROPERTIES SYMBOLIC "true")
See SYMBOLIC for the artifical source file property.

Add files in sub folders to target

I'd like to add output files in subdirectory to a build target.
I wrote in CMakeLists.txt like below:
file(GLOB srcfiles "src/*.txt")
add_custom_target(subtask ALL)
set(dest_dir ${PROJECT_SOURCE_DIR}/sub/)
foreach(srcfile ${srcfiles})
string(REGEX REPLACE "^.*/(.*).txt$" filename ${srcfile})
add_custom_command(OUTPUT ${dest_dir}/${filename}.txt
COMMAND ${CMAKE_COMMAND} ARGS -E make_directory ${dest_dir}
COMMAND ${PROJECT_SOURCE_DIR}/process.sh ARGS ${srcfile}
MAIN_DEPENDENCY ${PROJECT_SOURCE_DIR}/process.sh)
add_dependencies(subtask ${dest_dir}/${filename}.txt)
endforeach(srcfile)
And executed:
mkdir build && cmake .. && make
But sub/*.txt are not created after build.
How should I do to build all commands on build?
updated (2017/2/4)
I solved the issue: use add_custom_command for each target and then declare add_custom_target that depends on all targets of add_custom_command.
set(TARGET_FILES "")
file(GLOB SRC_FILES "src/*.txt")
foreach(SRC_FILE ${SRC_FILES})
string(REGEX REPLACE "^.*/(.*).txt$" "\\1-foo.txt" TARGET_FILE ${SRC_FILE})
add_custom_command(
OUTPUT ${PROJECT_SOURCE_DIR}/sub/${TARGET_FILE}
COMMAND ${CMAKE_COMMAND} ARGS -E make_directory ${PROJECT_SOURCE_DIR}/sub
COMMAND ${PROJECT_SOURCE_DIR}/process.sh ARGS ${SRC_FILE}
MAIN_DEPENDENCY ${PROJECT_SOURCE_DIR}/process.sh)
list(APPEND TARGET_FILES "${PROJECT_SOURCE_DIR}/sub/${TARGET_FILE}")
endforeach()
add_custom_target(foo_txt ALL DEPENDS ${TARGET_FILES})
It's a little unclear what you are trying to achieve, but just from looking at it I would say it should be:
set(dest_dir ${PROJECT_SOURCE_DIR}/sub)
add_custom_target(
subtask ALL
COMMAND ${CMAKE_COMMAND} -E make_directory ${dest_dir}
COMMAND ${PROJECT_SOURCE_DIR}/process.sh
)
Only if the output.txt is an input for something else, you need a custom command:
set(dest_dir ${PROJECT_SOURCE_DIR}/sub)
add_custom_command(
OUTPUT ${dest_dir}/output.txt
COMMAND ${CMAKE_COMMAND} -E make_directory ${dest_dir}
COMMAND ${PROJECT_SOURCE_DIR}/process.sh
MAIN_DEPENDENCY ${PROJECT_SOURCE_DIR}/process.sh
)
add_custom_target(
subtask ALL
DEPENDS ${dest_dir}/output.txt
)
Note that the default working directory for those commands is CMAKE_CURRENT_BINARY_DIR.
Edit: I think the problem in your code is the use of add_dependencies() for file level dependencies. But add_dependencies() can only be used to declare target dependencies.
Edit: With a foreach() you can either collect the dependencies or APPEND them with to a dummy output. The first looks something like this:
file(GLOB srcfiles "src/*.txt")
set(dest_dir "${PROJECT_SOURCE_DIR}/sub")
file(MAKE_DIRECTORY "${dest_dir}")
foreach(srcfile ${srcfiles})
get_filename_component(filename "${srcfile}" NAME_WE)
add_custom_command(
OUTPUT "${dest_dir}/${filename}.txt"
COMMAND ${PROJECT_SOURCE_DIR}/process.sh ${srcfile}
MAIN_DEPENDENCY "${srcfile}"
DEPENDS "${PROJECT_SOURCE_DIR}/process.sh"
WORKING_DIRECTORY "${dest_dir}"
)
list(APPEND subtask_deps "${dest_dir}/${filename}.txt")
endforeach(srcfile)
add_custom_target(
subtask ALL
DEPENDS ${subtask_deps}
)

CMake file in script mode inheriting variables

How can I call to a cmake file in script mode (-P) from other cmake file, so this "cmake child" knows all variable of its parent? Because, if I have a lot of variables the child needs, I have to write many -D options, and I want to avoid it.
Example:
// CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
set(teststr "Hello World!")
add_custom_command(test
${CMAKE_COMMAND} -Dteststr=${teststr} -P test.cmake
)
// test.cmake
message("${teststr}")
$ cmake .
$ make test
Hello world!
Built target test
Works fine!. But, without "-Dteststr":
// CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
set(teststr "Hello World!")
add_custom_command(test
${CMAKE_COMMAND} -P test.cmake
)
// test.cmake
message("${teststr}")
$ cmake .
$ make test
Built target test
Of course, without -D option, the "teststr" variable, in test.cmake, is unset, and thus, the output is empty.
Any option to call test.cmake in "heritage mode", or something like that?
You can pass arguments to a script with cmake -P.
If you call:
cmake -P <script-file> <arg3> <arg4> <arg5> ...
then the variables CMAKE_ARGC, CMAKE_ARGV0, CMAKE_ARGV1, ... will be available for the script.
See documentation for CMAKE_ARGC and CMAKE_ARGV0.
The other way is to define variables, just like with the non-script cmake command. However there's one thing to be aware of: you need to define the variables before -P:
cmake -DVAR=VALUE -DFOO=BAR -P <script-file> <arg5> <arg6> ...
Now in the cmake VAR and FOO will be available.
Also, note that the numbering of the args after the script-file will be shifted accordingly.
There's no particularly easy way to do this that I know of.
You could write all the current variables in the parent CMakeLists.txt to a separate file and then include this in your test.cmake:
# CMakeLists.txt:
cmake_minimum_required(VERSION 2.8)
set(teststr "Hello World!")
set(CacheForScript ${CMAKE_BINARY_DIR}/CMakeCacheForScript.cmake)
file(WRITE ${CacheForScript} "")
get_cmake_property(Vars VARIABLES)
foreach(Var ${Vars})
if(${Var})
string(REPLACE "\\" "\\\\" ${Var} ${${Var}})
endif()
file(APPEND ${CacheForScript} "set(${Var} \"${${Var}}\")\n")
endforeach()
add_custom_target(test ${CMAKE_COMMAND} -P ${CMAKE_SOURCE_DIR}/test.cmake)
# test.cmake
include(${CMAKE_BINARY_DIR}/CMakeCacheForScript.cmake)
message("${teststr}")

How do I initialize a CMake variable with the result of a shell command

Is there a way to set a variable in a CMake script to the output of a shell command?
Something like SET(FOO COMMAND "echo bar") would come to mind
You want the execute_process command.
In your case, on Windows:
execute_process(COMMAND CMD /c echo bar OUTPUT_VARIABLE FOO)
or on Linux, simply:
execute_process(COMMAND echo bar OUTPUT_VARIABLE FOO)
In this particular case, CMake offers a cross-platform solution. CMake can itself be used to run commands that can be used on all systems, one of which is echo. To do this, CMake should be passed the command line arg -E. For the full list of such commands, run cmake -E help
Inside a CMake script, the CMake executable is referred to by ${CMAKE_COMMAND}, so the script needs to do:
execute_process(COMMAND ${CMAKE_COMMAND} -E echo bar OUTPUT_VARIABLE FOO)