1#
2# Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
3#
4# SPDX-License-Identifier: BSD-2-Clause
5#
6
7# This module contains functions for creating a cpio archive containing a list
8# of input files and turning it into an object file that can be linked into a binary.
9
10include_guard(GLOBAL)
11
12# Checks the existence of an argument to cpio -o.
13# flag refers to a variable in the parent scope that contains the argument, if
14# the argument isn't supported then the flag is set to the empty string in the parent scope.
15function(CheckCPIOArgument var flag)
16    if(NOT (DEFINED ${var}))
17        file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/cpio-testfile "Testfile contents")
18        execute_process(
19            COMMAND bash -c "echo cpio-testfile | cpio ${flag} -o"
20            WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
21            OUTPUT_QUIET ERROR_QUIET
22            RESULT_VARIABLE result
23        )
24        if(result)
25            set(${var} "" CACHE INTERNAL "")
26            message(STATUS "CPIO test ${var} FAILED")
27        else()
28            set(${var} "${flag}" CACHE INTERNAL "")
29            message(STATUS "CPIO test ${var} PASSED")
30        endif()
31        file(REMOVE ${CMAKE_CURRENT_BINARY_DIR}/cpio-testfile)
32    endif()
33endfunction()
34
35# Function for declaring rules to build a cpio archive that can be linked
36# into another target
37function(MakeCPIO output_name input_files)
38    cmake_parse_arguments(PARSE_ARGV 2 MAKE_CPIO "" "CPIO_SYMBOL" "DEPENDS")
39    if(NOT "${MAKE_CPIO_UNPARSED_ARGUMENTS}" STREQUAL "")
40        message(FATAL_ERROR "Unknown arguments to MakeCPIO")
41    endif()
42    set(archive_symbol "_cpio_archive")
43    if(NOT "${MAKE_CPIO_CPIO_SYMBOL}" STREQUAL "")
44        set(archive_symbol ${MAKE_CPIO_CPIO_SYMBOL})
45    endif()
46    # Check that the reproducible flag is available. Don't use it if it isn't.
47    CheckCPIOArgument(cpio_reproducible_flag "--reproducible")
48    set(append "")
49	set(commands "bash;-c;cpio ${cpio_reproducible_flag} --quiet --create -H newc --file=${CMAKE_CURRENT_BINARY_DIR}/archive.${output_name}.cpio;&&")
50    foreach(file IN LISTS input_files)
51        # Try and generate reproducible cpio meta-data as we do this:
52        # - touch -d @0 file sets the modified time to 0
53        # - --owner=root:root sets user and group values to 0:0
54        # - --reproducible creates reproducible archives with consistent inodes and device numbering
55        list(
56            APPEND
57                commands
58                "bash;-c;cd `dirname ${file}` && mkdir -p temp_${output_name} && cd temp_${output_name} && cp -a ${file} . && touch -d @0 `basename ${file}` && echo `basename ${file}` | cpio --append ${cpio_reproducible_flag} --owner=root:root --quiet -o -H newc --file=${CMAKE_CURRENT_BINARY_DIR}/archive.${output_name}.cpio && rm `basename ${file}` && cd ../ && rmdir temp_${output_name};&&"
59        )
60    endforeach()
61    list(APPEND commands "true")
62    separate_arguments(cmake_c_flags_sep NATIVE_COMMAND "${CMAKE_C_FLAGS}")
63    if (CMAKE_C_COMPILER_ID STREQUAL "Clang")
64        list(APPEND cmake_c_flags_sep "${CMAKE_C_COMPILE_OPTIONS_TARGET}${CMAKE_C_COMPILER_TARGET}")
65    endif()
66
67    add_custom_command(
68        OUTPUT ${output_name}
69        COMMAND rm -f archive.${output_name}.cpio
70        COMMAND ${commands}
71        COMMAND
72            sh -c
73            "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'"
74            > ${output_name}.S
75        COMMAND
76            ${CMAKE_C_COMPILER} ${cmake_c_flags_sep} -c -o ${output_name} ${output_name}.S
77        DEPENDS ${input_files} ${MAKE_CPIO_DEPENDS}
78        VERBATIM
79        BYPRODUCTS
80        archive.${output_name}.cpio
81        ${output_name}.S
82        COMMENT "Generate CPIO archive ${output_name}"
83    )
84endfunction(MakeCPIO)
85