This repository has been archived by the owner on Jul 7, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cmake-bootstrap.cmake
196 lines (171 loc) · 7.68 KB
/
cmake-bootstrap.cmake
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
cmake_minimum_required(VERSION 3.8)
set(_BOOTSTRAP_FILE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake-bootstrap.cmake")
function(propagate_cli_arguments)
cmake_parse_arguments(
prop
""
"RESULT_VAR"
"BUILD_VARIABLES"
${ARGN}
)
#Ensure result parameter was provided
if(NOT DEFINED prop_RESULT_VAR OR NOT DEFINED prop_BUILD_VARIABLES)
message(
FATAL_ERROR
"'propogate_cli_arguments' accepts the following named arguments:\
\n(REQUIRED) 'RESULT_VAR' - The variable to store the parsed parameters in\
\n(REQUIRED) 'BUILD_VARIABLES' - The list of arguments to separate CLI arguments from\
")
endif()
#Assemble argument string
list(SORT prop_BUILD_VARIABLES)
foreach(VAR_NAME ${prop_BUILD_VARIABLES})
if(NOT "${${VAR_NAME}}" STREQUAL "" AND NOT "${VAR_NAME}" MATCHES "^_BOOTSTRAP_")
#All CLI specified build flags will have the property `HELPSTRING="No help, variable specified on the command line."`
get_property(help_string CACHE "${VAR_NAME}" PROPERTY HELPSTRING)
if("${help_string}" MATCHES "(No help, variable specified on the command line.)")
set(result ${result} "-D${VAR_NAME}=${${VAR_NAME}}")
endif()
endif()
endforeach()
set("${prop_RESULT_VAR}" "${result}" PARENT_SCOPE)
endfunction()
function(bootstrap_build)
cmake_parse_arguments(
bb
""
"BOOTSTRAP_NAME;BUILD_CMAKE_ROOT;TARGET_NAME;GENERATOR;BUILD_COMMAND"
"ENVIRONMENT;EXTRA_CMAKE_FLAGS;DEPENDS"
${ARGN}
)
if(NOT DEFINED bb_BOOTSTRAP_NAME OR NOT DEFINED bb_BUILD_CMAKE_ROOT)
message(
FATAL_ERROR
"'bootstrap_build' accepts the following named arguments:\
\n(REQUIRED) 'BOOTSTRAP_NAME' - The name of the bootstrapped project\
\n(REQUIRED) 'BUILD_CMAKE_ROOT' - The directory containing the 'CMakeLists.txt' of the bootstrapped project\
\n(OPTIONAL) 'TARGET_NAME' - Common target name for bootstrap build tasks (will add targets \"\${TARGET_NAME}_configure\" and \"\${TARGET_NAME}_build\"), defaults to '\${BOOTSTRAP_NAME}'\
\n(OPTIONAL) 'GENERATOR' - The CMake generator to use for the bootstrapped project, defaults to 'Ninja'\
\n(OPTIONAL) 'BUILD_COMMAND' - The build command to use for the bootstrapped project, defaults to the generator's build command\
\n(OPTIONAL) 'ENVIRONMENT' - A list of environment variables [VAR1=1 VAR2=2 ...] to use for the bootstrapped configure and build tasks\
\n(OPTIONAL) 'EXTRA_CMAKE_FLAGS' - A list of CMake flags [FLAG1=1 FLAG2=2 ...] to set when configuring the bootstrapped project\
\n(OPTIONAL) 'DEPENDS' - A list of configure dependencies\
")
endif()
#If the path is relative, use the top-level CMakeLists.txt dir as the root
if(NOT "${bb_BUILD_CMAKE_ROOT}" MATCHES "^\/")
set(bb_BUILD_CMAKE_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/${bb_BUILD_CMAKE_ROOT}")
endif()
message(STATUS "Using CMakeLists.txt from: ${bb_BUILD_CMAKE_ROOT}")
#Use default target name if none is specified
if(NOT DEFINED bb_TARGET_NAME)
set(bb_TARGET_NAME "${bb_BOOTSTRAP_NAME}")
endif()
#Use ninja as the default generator is none is specified
if(NOT DEFINED bb_GENERATOR)
message(STATUS "Build generator not specified, defaulting to ninja")
set(bb_GENERATOR Ninja)
find_program(generator_found ninja)
else()
find_program(generator_found "${bb_GENERATOR}")
endif()
if("${generator_found}" STREQUAL "generator_found-NOTFOUND")
message(FATAL_ERROR "The generator '${bb_GENERATOR}' could not be found")
endif()
#Use generator as the default build command if none is specified
if(NOT DEFINED bb_BUILD_COMMAND)
set(bb_BUILD_COMMAND "${generator_found}")
endif()
#If an environment was set, it needs to be converted from a list to a string
if(DEFINED bb_ENVIRONMENT)
foreach(env_pair ${bb_ENVIRONMENT})
set(unpacked_env ${unpacked_env} ${env_pair})
endforeach()
endif()
#Parse all extra cmake flags
if(DEFINED bb_EXTRA_CMAKE_FLAGS)
foreach(flag ${bb_EXTRA_CMAKE_FLAGS})
set(unpacked_cmake_flags ${unpacked_cmake_flags} "-D${flag}")
endforeach()
endif()
#Get CLI arguments to pass to bootstrapped build
get_cmake_property(invocation_args VARIABLES)
propagate_cli_arguments(RESULT_VAR args_to_propagate BUILD_VARIABLES ${invocation_args})
foreach(flag ${args_to_propagate})
set(unpacked_cmake_flags ${unpacked_cmake_flags} ${flag})
endforeach()
#Directory for bootstrapped build (directory created in script mode, see bottom of file)
set(bootstrap_build_dir "${PROJECT_BINARY_DIR}/${bb_BOOTSTRAP_NAME}")
#Build stage markers
set(bootstrap_clean_output "virtual_clean_output")
set(bootstrap_configure_output "${PROJECT_BINARY_DIR}/${bb_BOOTSTRAP_NAME}_configured.txt")
set(bootstrap_build_output "${PROJECT_BINARY_DIR}/${bb_BOOTSTRAP_NAME}_built.txt")
#Copy this file to the build directory for usage in script mode (see bottom of file)
set(bootstrap_script_path "${PROJECT_BINARY_DIR}/cmake-bootstrap.cmake")
if(NOT EXISTS "${bootstrap_script_path}")
file(COPY "${_BOOTSTRAP_FILE_PATH}" DESTINATION "${PROJECT_BINARY_DIR}")
endif()
#Add custom command to deal with stale project builds
add_custom_command(
OUTPUT "Clean up stale artifacts"
OUTPUT "${bootstrap_clean_output}"
COMMAND ${CMAKE_COMMAND} -D_BOOTSTRAP_SCRIPT_MODE="ON" -D_BOOTSTRAP_SCRIPT_BUILD_DIR="${PROJECT_BINARY_DIR}" -D_BOOTSTRAP_SCRIPT_TARGET_NAME="${bb_TARGET_NAME}" -D_BOOTSTRAP_SCRIPT_PROJECT_NAME="${bb_BOOTSTRAP_NAME}" -P "${bootstrap_script_path}"
)
add_custom_target(
"${bb_TARGET_NAME}_clean" ALL
DEPENDS "${bootstrap_clean_output}"
)
#Add custom command to bootstrap configure of dependent project
add_custom_command(
OUTPUT "Configure bootstrapped project"
OUTPUT "${bootstrap_configure_output}"
COMMAND ${CMAKE_COMMAND} -E env ${unpacked_env} ${CMAKE_COMMAND} "-G${bb_GENERATOR}" ${bb_BUILD_CMAKE_ROOT} ${unpacked_cmake_flags}
COMMAND ${CMAKE_COMMAND} -E touch "${bootstrap_configure_output}"
WORKING_DIRECTORY "${bootstrap_build_dir}"
DEPENDS "${bb_TARGET_NAME}_clean" ${bb_DEPENDS}
)
add_custom_target(
"${bb_TARGET_NAME}_configure" ALL
DEPENDS "${bootstrap_configure_output}"
)
#Add custom command to bootstrap build dependent project
add_custom_command(
OUTPUT "Build bootstrapped project"
OUTPUT "${bootstrap_build_output}"
COMMAND ${CMAKE_COMMAND} -E env ${unpacked_env} "${bb_BUILD_COMMAND}"
COMMAND ${CMAKE_COMMAND} -E touch "${bootstrap_build_output}"
WORKING_DIRECTORY "${bootstrap_build_dir}"
DEPENDS "${bb_TARGET_NAME}_configure"
)
add_custom_target(
"${bb_TARGET_NAME}_build" ALL
DEPENDS "${bootstrap_build_output}"
)
endfunction()
#Script mode
#Cleans up stale build artifacts
if(DEFINED _BOOTSTRAP_SCRIPT_MODE)
if(NOT DEFINED _BOOTSTRAP_SCRIPT_BUILD_DIR
OR NOT DEFINED _BOOTSTRAP_SCRIPT_PROJECT_NAME
OR NOT DEFINED _BOOTSTRAP_SCRIPT_TARGET_NAME)
message(
FATAL_ERROR
"cmake-bootstrap.cmake in script mode accepts the following named arguments:\
\n(REQUIRED) '_BOOTSTRAP_SCRIPT_BUILD_DIR' - The directory top level project invoking bootstrapped builds\
\n(REQUIRED) '_BOOTSTRAP_SCRIPT_PROJECT_NAME' - The name of the bootstrapped project\
\n(REQUIRED) '_BOOTSTRAP_SCRIPT_TARGET_NAME' - The target name prefix for the bootstrapped project\
")
endif()
set(bootstrapped_build_dir "${_BOOTSTRAP_SCRIPT_BUILD_DIR}/${_BOOTSTRAP_SCRIPT_PROJECT_NAME}")
#Clean up bootstrapped build directory if the configure stage marker is missing
if(NOT EXISTS "${_BOOTSTRAP_SCRIPT_BUILD_DIR}/${_BOOTSTRAP_SCRIPT_TARGET_NAME}_configure.txt")
if(EXISTS "${bootstrapped_build_dir}")
message(STATUS "Cleaning stale build directory for bootstrapped project: '${_BOOTSTRAP_SCRIPT_PROJECT_NAME}'")
file(REMOVE_RECURSE "${bootstrapped_build_dir}")
endif()
endif()
#Create bootstrap build directory if it doesn't already exist
if(NOT EXISTS "${bootstrapped_build_dir}")
file(MAKE_DIRECTORY "${bootstrapped_build_dir}")
endif()
endif()