-
Notifications
You must be signed in to change notification settings - Fork 91
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
improve CPIO creation #151
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -39,48 +39,101 @@ function(MakeCPIO output_name input_files) | |
if(NOT "${MAKE_CPIO_UNPARSED_ARGUMENTS}" STREQUAL "") | ||
message(FATAL_ERROR "Unknown arguments to MakeCPIO") | ||
endif() | ||
|
||
set(archive_symbol "_cpio_archive") | ||
if(NOT "${MAKE_CPIO_CPIO_SYMBOL}" STREQUAL "") | ||
set(archive_symbol ${MAKE_CPIO_CPIO_SYMBOL}) | ||
endif() | ||
# Check that the reproducible flag is available. Don't use it if it isn't. | ||
|
||
set(cpio_archive "${output_name}.cpio") | ||
|
||
# CMake wants absolute paths when calling file(WRITE ...) | ||
get_filename_component( | ||
output_name_abs | ||
"${output_name}" | ||
ABSOLUTE | ||
BASE_DIR | ||
"${CMAKE_CURRENT_BINARY_DIR}" | ||
) | ||
|
||
# Create a script that prepares CPIO archive contents in a tmp folder and | ||
# then builds the archive. Some cpio versions support the paramter | ||
# "--reproducible" to create the archive with consistent inodes and device | ||
# numbering. In addition, we set each file's the 'modified time' to the | ||
# epoch (ie 0, but simply using 'touch -d @0' is a GNU extension of the | ||
# POSIX standard), and the user/group values to 0:0. | ||
# Since some application access the archive contents by index and not by | ||
# name, the files must be put into the archive in exactly the same order as | ||
# they are listen in 'input_files'. Using simply "ls | cpio ...." instead of | ||
# "printf '%s\\n' \${@##*/} | cpio ..." does not guarantee preserving the | ||
# order. | ||
CheckCPIOArgument(cpio_reproducible_flag "--reproducible") | ||
set( | ||
commands | ||
"bash;-c;cpio ${cpio_reproducible_flag} --quiet --create -H newc --file=${CMAKE_CURRENT_BINARY_DIR}/archive.${output_name}.cpio;&&" | ||
set(cpio_archive_creator "${output_name_abs}.sh") | ||
file( | ||
WRITE | ||
"${cpio_archive_creator}" | ||
# content | ||
"#!/bin/sh\n" | ||
"# auto-generated file from MakeCPIO(), do not edit\n" | ||
axel-h marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"set -e\n" | ||
"TMP_DIR=${cpio_archive}.files\n" | ||
"mkdir -p \${TMP_DIR}\n" | ||
"cp -a \"$@\" \${TMP_DIR}/\n" | ||
"touch -d 1970-01-01T00:00:00Z \${TMP_DIR}/*\n" | ||
"(\n" | ||
" cd \${TMP_DIR}\n" | ||
" printf '%s\\n' \${@##*/} | cpio ${cpio_reproducible_flag} --quiet --create --format=newc --owner=+0:+0\n" | ||
") > ${cpio_archive}\n" | ||
"rm -rf \${TMP_DIR}\n" | ||
) | ||
foreach(file IN LISTS input_files) | ||
# Try and generate reproducible cpio meta-data as we do this: | ||
# - touch -d @0 file sets the modified time to 0 | ||
# - --owner=root:root sets user and group values to 0:0 | ||
# - --reproducible creates reproducible archives with consistent inodes and device numbering | ||
list( | ||
APPEND | ||
commands | ||
"bash;-c; mkdir -p temp_${output_name} && cd temp_${output_name} && cp -a ${file} . && touch -d 1970-01-01T00:00:00Z `basename ${file}` && echo `basename ${file}` | cpio --append ${cpio_reproducible_flag} --owner=+0:+0 --quiet -o -H newc --file=${CMAKE_CURRENT_BINARY_DIR}/archive.${output_name}.cpio && rm `basename ${file}` && cd ../ && rmdir temp_${output_name};&&" | ||
) | ||
endforeach() | ||
list(APPEND commands "true") | ||
|
||
# Create a "program" that makes the compiler generate and object file that | ||
# contains the cpio archive. | ||
# CMake wants the absolute name when creating files | ||
set(cpio_archive_s "${output_name_abs}.S") | ||
file( | ||
WRITE | ||
"${cpio_archive_s}" | ||
# content | ||
"# auto-generated file from MakeCPIO(), do not edit\n" | ||
".section ._archive_cpio, \"aw\"\n" | ||
".globl ${archive_symbol}, ${archive_symbol}_end\n" | ||
"${archive_symbol}:\n" | ||
".incbin \"${cpio_archive}\"\n" | ||
"${archive_symbol}_end:\n" | ||
) | ||
|
||
# Re-run CMake configuration in case 'cpio_archive_creator' or | ||
# 'cpio_archive_s' is deleted. | ||
set_property( | ||
DIRECTORY | ||
APPEND | ||
PROPERTY CMAKE_CONFIGURE_DEPENDS "${cpio_archive_creator}" "${cpio_archive_s}" | ||
) | ||
Comment on lines
+106
to
+112
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't consider that this property is needed unless CMake implicitly adds it for all files it generates in the build directory already. Trying to detect and recover the build directory becoming corrupted isn't the responsibility of the build scripts. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a convenience feature I found quite useful when experimenting with the archive creation. In general, when debugging CMake things, I often corrupt the build folder or delete files on purpose to force partial regeneration. And my expectation from proper dependency management is, that everything in the build folder should be re-generated automatically. |
||
|
||
# The 'cpio_archive' is no explicit parameter for the command, because it is | ||
# hard-coded already in the specific script we have generated above. The | ||
# 'input_files' are explicit here, because they are a CMake list that | ||
# will be converted to parameters for the invokation, | ||
add_custom_command( | ||
OUTPUT ${cpio_archive} | ||
COMMAND bash "${cpio_archive_creator}" ${input_files} | ||
DEPENDS "${cpio_archive_creator}" ${input_files} ${MAKE_CPIO_DEPENDS} | ||
COMMENT "Generate CPIO archive ${cpio_archive}" | ||
) | ||
|
||
separate_arguments(cmake_c_flags_sep NATIVE_COMMAND "${CMAKE_C_FLAGS}") | ||
if(CMAKE_C_COMPILER_ID STREQUAL "Clang") | ||
list(APPEND cmake_c_flags_sep "${CMAKE_C_COMPILE_OPTIONS_TARGET}${CMAKE_C_COMPILER_TARGET}") | ||
endif() | ||
|
||
# The 'cpio_archive' is no explicit parameter for the command, because it is | ||
# hard-coded already in the specific 'cpio_archive_s' file we have generated | ||
# above. | ||
add_custom_command( | ||
OUTPUT ${output_name} | ||
COMMAND rm -f archive.${output_name}.cpio | ||
COMMAND ${commands} | ||
COMMAND | ||
sh -c | ||
"echo 'X.section ._archive_cpio,\"aw\"X.globl ${archive_symbol}, ${archive_symbol}_endX${archive_symbol}:X.incbin \"archive.${output_name}.cpio\"X${archive_symbol}_end:X' | tr X '\\n'" | ||
> ${output_name}.S | ||
COMMAND | ||
${CMAKE_C_COMPILER} ${cmake_c_flags_sep} -c -o ${output_name} ${output_name}.S | ||
DEPENDS ${input_files} ${MAKE_CPIO_DEPENDS} | ||
VERBATIM | ||
BYPRODUCTS | ||
archive.${output_name}.cpio | ||
${output_name}.S | ||
${CMAKE_C_COMPILER} ${cmake_c_flags_sep} -c -o "${output_name}" "${cpio_archive_s}" | ||
DEPENDS "${cpio_archive_creator}" "${cpio_archive_s}" "${cpio_archive}" | ||
COMMENT "Generate CPIO archive ${output_name}" | ||
) | ||
endfunction(MakeCPIO) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This pattern is repeated a lot throughout the CMake files. I recall it's partially because
if (${MAKE_CPIO_UNPARSED_ARGUMENTS})
unexpectedly evaluates to false for almost all string values as CMake's described behavior (https://cmake.org/cmake/help/latest/command/if.html).Using
if(MAKE_CPIO_UNPARSED_ARGUMENTS)
is shorter and you know that this tests if the variable is defined and defined to non-false values. But it would be easy to misread something likeif(${MAKE_CPIO_UNPARSED_ARGUMENTS}
orif("${MAKE_CPIO_UNPARSED_ARGUMENTS}"
) as testing for truth if you aren't more familiar with the if() gotchas . In these cases, if the variable contains a string it will always evaluate to false unless the contents match the truthy string values (eg ON, TRUE, etc). So while"${MAKE_CPIO_UNPARSED_ARGUMENTS}" STREQUAL "")
is more verbose, it's probably a good pattern to leave around for explicitly testing for a non-empty string as it doesn't rely on understanding any if() testing gotchas.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with all points. This is the outcame from digging into CMake usage and a long term plan to use the language as it seems intended nowadays. I can move this to a separate PR to make the intention more clear. There are more places where this could be changed to clarify what checks are non-trivial.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have remove the commit