I am trying to collect Git information into file and am doing
execute_process(
COMMAND git log -1 --format=full && echo "Modified files:" && git ls-files -m
OUTPUT_FILE "git_log"
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
)
or
execute_process(
COMMAND git log -1 --format=full
COMMAND echo "Modified files:"
COMMAND git ls-files -m
OUTPUT_FILE "git_log"
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
)
And in both cases don't see full content. In first case I see empty file and in second -- only output of last command.
How to gather all in one file?
in first case I see empty file
Sure - no shell is used, and && is invalid argument to git, the command fails. See documentation No intermediate shell is used, so shell operators such as > are treated as normal arguments.
in second -- only output of last command.
Sure - see documentation Commands are executed concurrently as a pipeline, with the standard output of each process piped to the standard input of the next. git ... | echo ... | git ... will only output the last one - git and echo ignore standard input.
execute_process(
COMMAND sh -c "
git log -1 --format=full &&
echo \"Modified files:\" &&
git ls-files -m"
OUTPUT_FILE "git_log1"
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
)
or
execute_process(
COMMAND git log -1 --format=full
OUTPUT_FILE "git_log1"
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
)
execute_process(
COMMAND echo ...
OUTPUT_FILE "git_log2"
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
)
execute_process(
COMMAND git ...
OUTPUT_FILE "git_log3"
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
)
execute_process(
COMMAND ${CMAKE_COMMAND} -E cat git_log1 git_log2 git_log3
OUTPUT_FILE "git_log"
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
)
Related
I have a setup where I use a custom command to check the current hash of a git repository so that other commands can clone it if it has updated
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/module_VERSION.txt
COMMAND ${GIT_EXECUTABLE} ls-remote ${MODULE_URL} master > ${CMAKE_CURRENT_BINARY_DIR}/module_VERSION.txt.tmp
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/module_VERSION.txt.tmp ${CMAKE_CURRENT_BINARY_DIR}/module_VERSION.txt
COMMAND ${CMAKE_COMMAND} -E rm ${CMAKE_CURRENT_BINARY_DIR}/module_VERSION.txt.tmp
)
Of course this will only run once as CMake sees no reason to rerun it. I can force it to run by adding a second (dummy) output - CMake then recognises that this output doesn't exist and then reruns the rule. However the Makefile that this generates actually deletes module_VERSION.txt before running the command rendering the whole pursuit pointless (Ninja does not have this problem).
I am able to get this to work but in an extremely hacky way: creating another target that always runs and then generating a dependency on this.
# Use echo_append as a no-op
add_custom_command(
OUTPUT module_FORCERUN
COMMAND ${CMAKE_COMMAND} -E echo_append
)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/module_VERSION.txt
COMMAND ${GIT_EXECUTABLE} ls-remote ${MODULE_URL} master > ${CMAKE_CURRENT_BINARY_DIR}/module_VERSION.txt.tmp
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/module_VERSION.txt.tmp ${CMAKE_CURRENT_BINARY_DIR}/module_VERSION.txt
COMMAND ${CMAKE_COMMAND} -E rm ${CMAKE_CURRENT_BINARY_DIR}/module_VERSION.txt.tmp
DEPENDS module_FORCERUN
)
This seems just really hacky and like it could be relying on some corner cases in cmake which aren't guaranteed to be stable. Is there a better way to get this working?
I am using cmake 3.21.3
Use add_custom_target for implement "always run" functionality and via BYPRODUCTS keyword specify the file which it could produce/update:
add_custom_target(update_module_version
BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/module_VERSION.txt
COMMAND ${GIT_EXECUTABLE} ls-remote ${MODULE_URL} master > ${CMAKE_CURRENT_BINARY_DIR}/module_VERSION.txt.tmp
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/module_VERSION.txt.tmp ${CMAKE_CURRENT_BINARY_DIR}/module_VERSION.txt
COMMAND ${CMAKE_COMMAND} -E rm ${CMAKE_CURRENT_BINARY_DIR}/module_VERSION.txt.tmp
)
That way, if any other target will depend on update_module_version one, the module_VERSION.txt file will be created/updated before evaluation of the target.
Such target-level dependency will be created automatically by CMake, if given file will be listed as dependency for target/command in the same directory, where target update_module_version is created:
add_custom_command(OUTPUT <...>
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/module_VERSION.txt
COMMAND <...>
)
From other directories the target-level dependency should be specified explicitly:
# If used in other directories
add_custom_command(OUTPUT <...>
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/module_VERSION.txt
update_module_version
COMMAND <...>
)
First, I define the COMMIT_ID variable:
execute_process(COMMAND git rev-parse HEAD
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
OUTPUT_VARIABLE COMMIT_ID )
If you specify the COMMIT_ID variable the project is not built:
add_custom_command(TARGET ${APP_NAME} POST_BUILD
WORKING_DIRECTORY
$<TARGET_FILE_DIR:${APP_NAME}>
DEPENDS
${COMMIT_ID}
COMMAND
${CMAKE_COMMAND} -E echo ${COMMIT_ID} > ./version.md
COMMENT
"Generating file version.md"
VERBATIM)
But, if you specify a static string the project is built without errors:
add_custom_command(TARGET ${APP_NAME} POST_BUILD
WORKING_DIRECTORY
$<TARGET_FILE_DIR:${APP_NAME}>
DEPENDS
${COMMIT_ID}
COMMAND
${CMAKE_COMMAND} -E echo "COMMIT_ID" > ./version.md
COMMENT
"Generating file version.md"
VERBATIM)
The issue with using the ${COMMIT_ID} variable is that it may contain trailing whitespace or newlines from its creation in execute_process. You can add the OUTPUT_STRIP_TRAILING_WHITESPACE argument to cleanup the output of execute_process before it is used in the variable:
execute_process(COMMAND git rev-parse HEAD
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
OUTPUT_VARIABLE COMMIT_ID
OUTPUT_STRIP_TRAILING_WHITESPACE
)
Let's say I have the block below in my CMakeLists.txt.
file (DOWNLOAD http://.../file.zip "${CMAKE_BINARY_DIR}/file.zip")
execute_process (
COMMAND "${CMAKE_COMMAND}" -E tar -xf file.zip
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
)
add_custom_command (
OUTPUT output.txt
COMMAND "${MY_COMMAND}" file-found-in-zip.txt output.txt
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
)
The basic steps are:
Download.
Extract.
Add a custom command that uses a file that was extracted in step 2.
During the build step, the custom command may or may not be executed, but the download and extraction always will. How can I make the download and extraction conditional, such that it happens only if the custom command that needs it will be executed?
I guess something along:
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/download.cmake
"file(DOWNLOAD http://.../file.zip ${CMAKE_CURRENT_BINARY_DIR}/file.zip"
)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/file.zip
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/download.cmake
VERBATIM
)
execute_process (
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/file-found-in-zip.txt
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/file.zip
COMMAND ${CMAKE_COMMAND} -E tar -xf file.zip
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
VERBATIM
)
add_custom_command (
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/output.txt
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/file-found-in-zip.txt
COMMAND "${MY_COMMAND}" file-found-in-zip.txt output.txt
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
VERBATIM
)
Or just:
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/download.cmake
"file(DOWNLOAD http://.../file.zip ${CMAKE_CURRENT_BINARY_DIR}/file.zip"
)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/output.txt
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/download.cmake
COMMAND ${CMAKE_COMMAND} -E tar -xf file.zip
COMMAND "${MY_COMMAND}" file-found-in-zip.txt output.txt
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
VERBATIM
)
I would like to write output of git describe as a string to a file, so I can embed the information in my binary (C++). This has to work across platforms.
The best I can yet come up with was:
add_custom_target( SubmarineGitVersion
COMMAND cmd /c "${CMAKE_EXECUTABLE}" echo czstring GIT_VERSION = STRINGIFY\( > "${CMAKE_CURRENT_BINARY_DIR}/GitVersion.hpp"
COMMAND cmd /c "${GIT_EXECUTABLE}" describe --tags --always >> "${CMAKE_CURRENT_BINARY_DIR}/GitVersion.hpp"
COMMAND cmd /c "${CMAKE_EXECUTABLE}" echo \) >> "${CMAKE_CURRENT_BINARY_DIR}/GitVersion.hpp"
)
This roughly works on Windows (is missing a ; at the end):
czstring GIT_VERSION = STRINGIFY(
tag-343434
)
Is there any better/more cross-platform way of doing this?
Common way for create "version" files is using configure_file command. Such way file will be created at configure stage:
GitVersion.hpp.in:
czstring GIT_VERSION = STRINGIFY(
${GIT_REPO_VERSION}
)
CMakeLists.txt:
# Store version into variable
execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --always
OUTPUT_VARIABLE GIT_REPO_VERSION)
# The variable will be used when file is configured
configure_file("GitVersion.hpp.in" "GitVersion.hpp")
If you want to create version file on build stage, move above cmake commands into some file, and execute this file in CMake script mode:
generate_version.cmake:
# Git executable is extracted from parameters.
execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --always
OUTPUT_VARIABLE GIT_REPO_VERSION)
# Input and output files are extracted from parameters.
configure_file(${INPUT_FILE} ${OUTPUT_FILE})
CMakeLists.txt:
add_custom_target( SubmarineGitVersion
COMMAND ${CMAKE_COMMAND}
-D GIT_EXECUTABLE=${GIT_EXECUTABLE}
-D INPUT_FILE=${CMAKE_CURRENT_SOURCE_DIR}/GitVersion.hpp.in
-D OUTPUT_FILE=${CMAKE_CURRENT_BINARY_DIR}/GitVersion.hpp
-P ${CMAKE_CURRENT_SOURCE_DIR}/generate_version.cmake
)
I'm trying to redirect stdout and stderr to the same file using CMake. I'm using the execute_process option in CMake with the ERROR_FILE and OUTPUT_FILE option specified.
I'm successfully capturing the output, but the error is not there. What am I doing wrong?
File CMakeLists.txt
add_test(NAME test${ID}
COMMAND ${CMAKE_COMMAND}
-DEXE=../examples/test${exampleID}
-DID=${ID}
-DARGS=${args}
-P ${CMAKE_CURRENT_SOURCE_DIR}/Tester.cmake
)
File Tester.cmake
separate_arguments( ARGS )
# Run the test
execute_process(
COMMAND "${EXE}" ${ARGS}
ERROR_FILE test${ID}.out
OUTPUT_FILE test${ID}.out
)
Specifying the same file for both OUTPUT_FILE and ERROR_FILE has only recently been added in CMake 3.3. See release notes.
As a work-around for earlier versions, use the options OUTPUT_VARIABLE and ERROR_VARIABLE with the same variable and then write the contents of the variable to the file, e.g.:
execute_process(
COMMAND "${EXE}" ${ARGS}
ERROR_VARIABLE _testOut
OUTPUT_VARIABLE _testOut
)
file (WRITE "test${ID}.out" "${_testOut}")