]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | #!/bin/bash |
2 | ||
3 | # | |
4 | # Copyright 2018 Jesse Haber-Kucharsky | |
5 | # | |
6 | # Licensed under the Apache License, Version 2.0 (the "License"); | |
7 | # you may not use this file except in compliance with the License. | |
8 | # You may obtain a copy of the License at | |
9 | # | |
10 | # http://www.apache.org/licenses/LICENSE-2.0 | |
11 | # | |
12 | # Unless required by applicable law or agreed to in writing, software | |
13 | # distributed under the License is distributed on an "AS IS" BASIS, | |
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
15 | # See the License for the specific language governing permissions and | |
16 | # limitations under the License. | |
17 | # | |
18 | ||
19 | # | |
9f95a23c | 20 | # This is cmake-cooking v0.10.0 |
11fdf7f2 TL |
21 | # The home of cmake-cooking is https://github.com/hakuch/CMakeCooking |
22 | # | |
23 | ||
24 | set -e | |
25 | ||
26 | CMAKE=${CMAKE:-cmake} | |
27 | ||
28 | invoked_args=("$@") | |
29 | source_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" | |
30 | initial_wd=$(pwd) | |
31 | memory_file="${initial_wd}/.cooking_memory" | |
32 | ||
9f95a23c | 33 | recipe="${source_dir}/cooking_recipe.cmake" |
11fdf7f2 TL |
34 | declare -a excluded_ingredients |
35 | declare -a included_ingredients | |
36 | build_dir="${initial_wd}/build" | |
37 | build_type="Debug" | |
38 | # Depends on `build_dir`. | |
39 | ingredients_dir="" | |
40 | generator="Ninja" | |
41 | list_only="" | |
42 | nested="" | |
43 | ||
44 | usage() { | |
45 | cat <<EOF | |
46 | ||
47 | Fetch, configure, build, and install dependencies ("ingredients") for a CMake project | |
48 | in a local and repeatable development environment. | |
49 | ||
50 | Usage: $0 [OPTIONS] | |
51 | ||
52 | where OPTIONS are: | |
53 | ||
54 | -a | |
55 | -r RECIPE | |
56 | -e INGREDIENT | |
57 | -i INGREDIENT | |
58 | -d BUILD_DIR (=${build_dir}) | |
59 | -p INGREDIENTS_DIR (=${build_dir}/_cooking/installed) | |
60 | -t BUILD_TYPE (=${build_type}) | |
61 | -g GENERATOR (=${generator}) | |
62 | -s VAR=VALUE | |
9f95a23c | 63 | -f EXPORT_DIR |
11fdf7f2 TL |
64 | -l |
65 | -h | |
66 | ||
9f95a23c TL |
67 | By default, cmake-cooking reads a file called `cooking_recipe.cmake`. |
68 | ||
11fdf7f2 TL |
69 | If neither [-i] nor [-e] are specified with a recipe ([-r]), then all ingredients of the recipe |
70 | will be fetched and built. | |
71 | ||
72 | [-i] and [-e] are mutually-exclusive options: only provide one. | |
73 | ||
74 | Option details: | |
75 | ||
76 | -a | |
77 | ||
78 | Invoke 'cooking.sh' with the arguments that were provided to it last time, instead | |
79 | of the arguments provided. | |
80 | ||
81 | -r RECIPE | |
82 | ||
9f95a23c TL |
83 | Instead of reading the recipe in a file called `cooking_recipe.cmake`, follow the recipe |
84 | in the named file. | |
85 | ||
86 | If the recipe file is a relative path, it is interpretted relative to the source directory | |
87 | of the project. | |
11fdf7f2 TL |
88 | |
89 | -e INGREDIENT | |
90 | ||
91 | Exclude an ingredient from a recipe. This option can be supplied many times. | |
92 | ||
93 | For example, if a recipe consists of 'apple', 'banana', 'carrot', and 'donut', then | |
94 | ||
95 | ./cooking.sh -r dev -e apple -e donut | |
96 | ||
97 | will prepare 'banana' and 'carrot' but not prepare 'apple' and 'donut'. | |
98 | ||
99 | If an ingredient is excluded, then it is assumed that all ingredients that depend on it | |
100 | can satisfy that dependency in some other way from the system (ie, the dependency is | |
101 | removed internally). | |
102 | ||
103 | -i INGREDIENT | |
104 | ||
105 | Include an ingredient from a recipe, ignoring the others. This option can be supplied | |
106 | many times. | |
107 | ||
108 | Similar to [-e], but the opposite. | |
109 | ||
110 | For example, if a recipe consists of 'apple', 'banana', 'carrot', and 'donut' then | |
111 | ||
112 | ./cooking.sh -r dev -i apple -i donut | |
113 | ||
114 | will prepare 'apple' and 'donut' but not prepare 'banana' and 'carrot'. | |
115 | ||
116 | If an ingredient is not in the "include-list", then it is assumed that all | |
117 | ingredients that are in the list and which depend on it can satisfy that dependency | |
118 | in some other way from the system. | |
119 | ||
120 | -d BUILD_DIR (=${build_dir}) | |
121 | ||
122 | Configure the project and build it in the named directory. | |
123 | ||
124 | -p INGREDIENTS_DIR (=${build_dir}/_cooking/installed) | |
125 | ||
126 | Install compiled ingredients into this directory. | |
127 | ||
128 | -t BUILD_TYPE (=${build_type}) | |
129 | ||
130 | Configure all ingredients and the project with the named CMake build-type. | |
131 | An example build type is "Release". | |
132 | ||
133 | -g GENERATOR (=${generator}) | |
134 | ||
135 | Use the named CMake generator for building all ingredients and the project. | |
136 | An example generator is "Unix Makfiles". | |
137 | ||
138 | -s VAR=VALUE | |
139 | ||
140 | Set an environmental variable 'VAR' to the value 'VALUE' during the invocation of CMake. | |
141 | ||
9f95a23c TL |
142 | -f EXPORT_DIR |
143 | ||
144 | If provided, and the project is successfully configured, then the tree of installed ingredients | |
145 | is exported to this directory (the actual files: not symbolic links). | |
146 | ||
147 | This option requires rsync. | |
148 | ||
149 | This may be useful for preparing continuous integration environments, but it is not | |
150 | recommended for distribution or release purposes (since this would be counter | |
151 | to the goal of cmake-cooking). | |
152 | ||
11fdf7f2 TL |
153 | -l |
154 | ||
155 | Only list available ingredients for a given recipe, and don't do anything else. | |
156 | ||
157 | -h | |
158 | ||
159 | Show this help information and exit. | |
160 | ||
161 | EOF | |
162 | } | |
163 | ||
164 | parse_assignment() { | |
1e59de90 TL |
165 | local var |
166 | local value | |
167 | IFS='=' read -r var value <<< "${1}" | |
168 | export "${var}"="${value}" | |
11fdf7f2 TL |
169 | } |
170 | ||
171 | yell_include_exclude_mutually_exclusive() { | |
172 | echo "Cooking: [-e] and [-i] are mutually exclusive options!" >&2 | |
173 | } | |
174 | ||
9f95a23c | 175 | while getopts "ar:e:i:d:p:t:g:s:f:lhx" arg; do |
11fdf7f2 TL |
176 | case "${arg}" in |
177 | a) | |
178 | if [ ! -f "${memory_file}" ]; then | |
179 | echo "No previous invocation found to recall!" >&2 | |
180 | exit 1 | |
181 | fi | |
182 | ||
183 | source "${memory_file}" | |
184 | run_previous && exit 0 | |
185 | ;; | |
9f95a23c TL |
186 | r) |
187 | if [[ "${OPTARG}" = /* ]]; then | |
188 | recipe=${OPTARG} | |
189 | else | |
190 | recipe="${source_dir}/${OPTARG}" | |
191 | fi | |
192 | ;; | |
11fdf7f2 TL |
193 | e) |
194 | if [[ ${#included_ingredients[@]} -ne 0 ]]; then | |
195 | yell_include_exclude_mutually_exclusive | |
196 | exit 1 | |
197 | fi | |
198 | ||
199 | excluded_ingredients+=(${OPTARG}) | |
200 | ;; | |
201 | i) | |
202 | if [[ ${#excluded_ingredients[@]} -ne 0 ]]; then | |
203 | yell_include_exclude_mutually_exclusive | |
204 | exit 1 | |
205 | fi | |
206 | ||
207 | included_ingredients+=(${OPTARG}) | |
208 | ;; | |
209 | d) build_dir=$(realpath "${OPTARG}") ;; | |
210 | p) ingredients_dir=$(realpath "${OPTARG}") ;; | |
211 | t) build_type=${OPTARG} ;; | |
212 | g) generator=${OPTARG} ;; | |
213 | s) parse_assignment "${OPTARG}" ;; | |
9f95a23c | 214 | f) export_dir=$(realpath "${OPTARG}") ;; |
11fdf7f2 TL |
215 | l) list_only="1" ;; |
216 | h) usage; exit 0 ;; | |
217 | x) nested="1" ;; | |
218 | *) usage; exit 1 ;; | |
219 | esac | |
220 | done | |
221 | ||
222 | shift $((OPTIND - 1)) | |
223 | ||
224 | cooking_dir="${build_dir}/_cooking" | |
11fdf7f2 TL |
225 | cache_file="${build_dir}/CMakeCache.txt" |
226 | ingredients_ready_file="${cooking_dir}/ready.txt" | |
227 | ||
228 | if [ -z "${ingredients_dir}" ]; then | |
229 | ingredients_dir="${cooking_dir}/installed" | |
230 | fi | |
231 | ||
9f95a23c | 232 | mkdir -p "${build_dir}" |
11fdf7f2 | 233 | |
9f95a23c | 234 | cat <<'EOF' > "${build_dir}/Cooking.cmake" |
11fdf7f2 TL |
235 | # |
236 | # Copyright 2018 Jesse Haber-Kucharsky | |
237 | # | |
238 | # Licensed under the Apache License, Version 2.0 (the "License"); | |
239 | # you may not use this file except in compliance with the License. | |
240 | # You may obtain a copy of the License at | |
241 | # | |
242 | # http://www.apache.org/licenses/LICENSE-2.0 | |
243 | # | |
244 | # Unless required by applicable law or agreed to in writing, software | |
245 | # distributed under the License is distributed on an "AS IS" BASIS, | |
246 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
247 | # See the License for the specific language governing permissions and | |
248 | # limitations under the License. | |
249 | # | |
250 | ||
251 | # | |
9f95a23c | 252 | # This file was generated by cmake-cooking v0.10.0 |
11fdf7f2 TL |
253 | # The home of cmake-cooking is https://github.com/hakuch/CMakeCooking |
254 | # | |
255 | ||
256 | macro (project name) | |
257 | set (_cooking_dir ${CMAKE_CURRENT_BINARY_DIR}/_cooking) | |
258 | ||
259 | if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) | |
260 | set (_cooking_root ON) | |
261 | else () | |
262 | set (_cooking_root OFF) | |
263 | endif () | |
264 | ||
265 | find_program (Cooking_STOW_EXECUTABLE | |
266 | stow | |
267 | "Executable path of GNU Stow.") | |
268 | ||
269 | if (NOT Cooking_STOW_EXECUTABLE) | |
270 | message (FATAL_ERROR "Cooking: GNU Stow is required!") | |
271 | endif () | |
272 | ||
273 | set (Cooking_INGREDIENTS_DIR | |
274 | ${_cooking_dir}/installed | |
275 | CACHE | |
276 | PATH | |
277 | "Directory where ingredients will be installed.") | |
278 | ||
279 | set (Cooking_EXCLUDED_INGREDIENTS | |
280 | "" | |
281 | CACHE | |
282 | STRING | |
283 | "Semicolon-separated list of ingredients that are not provided by Cooking.") | |
284 | ||
285 | set (Cooking_INCLUDED_INGREDIENTS | |
286 | "" | |
287 | CACHE | |
288 | STRING | |
289 | "Semicolon-separated list of ingredients that are provided by Cooking.") | |
290 | ||
291 | option (Cooking_LIST_ONLY | |
292 | "Available ingredients will be listed and nothing will be installed." | |
293 | OFF) | |
294 | ||
295 | set (Cooking_RECIPE "" CACHE STRING "Configure ${name}'s dependencies according to the named recipe.") | |
296 | ||
297 | if ((NOT DEFINED Cooking_EXCLUDED_INGREDIENTS) OR (Cooking_EXCLUDED_INGREDIENTS STREQUAL "")) | |
298 | set (_cooking_is_excluding OFF) | |
299 | else () | |
300 | set (_cooking_is_excluding ON) | |
301 | endif () | |
302 | ||
303 | if ((NOT DEFINED Cooking_INCLUDED_INGREDIENTS) OR (Cooking_INCLUDED_INGREDIENTS STREQUAL "")) | |
304 | set (_cooking_is_including OFF) | |
305 | else () | |
306 | set (_cooking_is_including ON) | |
307 | endif () | |
308 | ||
309 | if (_cooking_is_excluding AND _cooking_is_including) | |
310 | message ( | |
311 | FATAL_ERROR | |
312 | "Cooking: The EXCLUDED_INGREDIENTS and INCLUDED_INGREDIENTS lists are mutually exclusive options!") | |
313 | endif () | |
314 | ||
315 | if (_cooking_root) | |
316 | _project (${name} ${ARGN}) | |
317 | ||
318 | if (NOT ("${Cooking_RECIPE}" STREQUAL "")) | |
319 | add_custom_target (_cooking_ingredients) | |
320 | ||
321 | set (_cooking_ready_marker_file ${_cooking_dir}/ready.txt) | |
322 | ||
323 | add_custom_command ( | |
324 | OUTPUT ${_cooking_ready_marker_file} | |
325 | DEPENDS _cooking_ingredients | |
326 | COMMAND ${CMAKE_COMMAND} -E touch ${_cooking_ready_marker_file}) | |
327 | ||
328 | add_custom_target (_cooking_ingredients_ready | |
329 | DEPENDS ${_cooking_ready_marker_file}) | |
330 | ||
331 | set (_cooking_local_synchronize_marker_file ${Cooking_INGREDIENTS_DIR}/.cooking_local_synchronize) | |
332 | ||
333 | add_custom_command ( | |
334 | OUTPUT ${_cooking_local_synchronize_marker_file} | |
335 | COMMAND ${CMAKE_COMMAND} -E touch ${_cooking_local_synchronize_marker_file}) | |
336 | ||
337 | add_custom_target (_cooking_marked_for_local_synchronization | |
338 | DEPENDS ${_cooking_local_synchronize_marker_file}) | |
339 | ||
340 | list (APPEND CMAKE_PREFIX_PATH ${Cooking_INGREDIENTS_DIR}) | |
9f95a23c | 341 | include (${Cooking_RECIPE}) |
11fdf7f2 TL |
342 | |
343 | if (NOT EXISTS ${_cooking_ready_marker_file}) | |
344 | return () | |
345 | endif () | |
346 | endif () | |
347 | endif () | |
348 | endmacro () | |
349 | ||
350 | function (_cooking_set_union x y var) | |
351 | set (r ${${x}}) | |
352 | ||
353 | foreach (e ${${y}}) | |
354 | list (APPEND r ${e}) | |
355 | endforeach () | |
356 | ||
357 | list (REMOVE_DUPLICATES r) | |
358 | set (${var} ${r} PARENT_SCOPE) | |
359 | endfunction () | |
360 | ||
361 | function (_cooking_set_difference x y var) | |
362 | set (r ${${x}}) | |
363 | ||
364 | foreach (e ${${y}}) | |
365 | if (${e} IN_LIST ${x}) | |
366 | list (REMOVE_ITEM r ${e}) | |
367 | endif () | |
368 | endforeach () | |
369 | ||
370 | set (${var} ${r} PARENT_SCOPE) | |
371 | endfunction () | |
372 | ||
373 | function (_cooking_set_intersection x y var) | |
374 | set (r "") | |
375 | ||
376 | foreach (e ${${y}}) | |
377 | if (${e} IN_LIST ${x}) | |
378 | list (APPEND r ${e}) | |
379 | endif () | |
380 | endforeach () | |
381 | ||
382 | list (REMOVE_DUPLICATES r) | |
383 | set (${var} ${r} PARENT_SCOPE) | |
384 | endfunction () | |
385 | ||
386 | function (_cooking_query_by_key list key var) | |
387 | list (FIND ${list} ${key} index) | |
388 | ||
389 | if (${index} EQUAL "-1") | |
390 | set (value NOTFOUND) | |
391 | else () | |
392 | math (EXPR value_index "${index} + 1") | |
393 | list (GET ${list} ${value_index} value) | |
394 | endif () | |
395 | ||
396 | set (${var} ${value} PARENT_SCOPE) | |
397 | endfunction () | |
398 | ||
399 | function (_cooking_populate_ep_parameter) | |
400 | cmake_parse_arguments ( | |
401 | pa | |
402 | "" | |
403 | "EXTERNAL_PROJECT_ARGS_LIST;PARAMETER;DEFAULT_VALUE" | |
404 | "" | |
405 | ${ARGN}) | |
406 | ||
407 | string (TOLOWER ${pa_PARAMETER} parameter_lower) | |
408 | _cooking_query_by_key (${pa_EXTERNAL_PROJECT_ARGS_LIST} ${pa_PARAMETER} ${parameter_lower}) | |
409 | set (value ${${parameter_lower}}) | |
410 | set (var_name _cooking_${parameter_lower}) | |
411 | set (ep_var_name _cooking_ep_${parameter_lower}) | |
412 | ||
413 | if (NOT value) | |
414 | set (${var_name} ${pa_DEFAULT_VALUE} PARENT_SCOPE) | |
415 | set (${ep_var_name} ${pa_PARAMETER} ${pa_DEFAULT_VALUE} PARENT_SCOPE) | |
416 | else () | |
417 | set (${var_name} ${value} PARENT_SCOPE) | |
418 | set (${ep_var_name} "" PARENT_SCOPE) | |
419 | endif () | |
420 | endfunction () | |
421 | ||
422 | function (_cooking_define_listing_targets) | |
423 | cmake_parse_arguments ( | |
424 | pa | |
425 | "" | |
426 | "NAME;SOURCE_DIR;RECIPE" | |
427 | "REQUIRES" | |
428 | ${ARGN}) | |
429 | ||
430 | set (stale_file ${Cooking_INGREDIENTS_DIR}/.cooking_stale_ingredient_${pa_NAME}) | |
431 | ||
432 | add_custom_command ( | |
433 | OUTPUT ${stale_file} | |
434 | COMMAND ${CMAKE_COMMAND} -E touch ${stale_file}) | |
435 | ||
436 | add_custom_target (_cooking_ingredient_${pa_NAME}_stale | |
437 | DEPENDS ${stale_file}) | |
438 | ||
439 | set (commands | |
440 | COMMAND | |
441 | ${CMAKE_COMMAND} -E touch ${Cooking_INGREDIENTS_DIR}/.cooking_ingredient_${pa_NAME}) | |
442 | ||
443 | if (pa_RECIPE) | |
9f95a23c TL |
444 | if (pa_RECIPE STREQUAL <DEFAULT>) |
445 | set (recipe_args "") | |
446 | else () | |
447 | set (recipe_args -r ${pa_RECIPE}) | |
448 | endif () | |
449 | ||
11fdf7f2 TL |
450 | list (INSERT commands 0 |
451 | COMMAND | |
452 | ${pa_SOURCE_DIR}/cooking.sh | |
9f95a23c | 453 | ${recipe_args} |
11fdf7f2 TL |
454 | -p ${Cooking_INGREDIENTS_DIR} |
455 | -g ${CMAKE_GENERATOR} | |
456 | -x | |
457 | -l) | |
458 | endif () | |
459 | ||
460 | add_custom_command ( | |
461 | OUTPUT ${Cooking_INGREDIENTS_DIR}/.cooking_ingredient_${pa_NAME} | |
462 | DEPENDS | |
463 | _cooking_ingredient_${pa_NAME}_stale | |
464 | ${stale_file} | |
465 | ${commands}) | |
466 | ||
467 | add_custom_target (_cooking_ingredient_${pa_NAME}_listed | |
468 | DEPENDS ${Cooking_INGREDIENTS_DIR}/.cooking_ingredient_${pa_NAME}) | |
469 | ||
470 | foreach (d ${pa_REQUIRES}) | |
471 | add_dependencies (_cooking_ingredient_${pa_NAME}_listed _cooking_ingredient_${d}_listed) | |
472 | endforeach () | |
473 | ||
474 | add_dependencies (_cooking_ingredients _cooking_ingredient_${pa_NAME}_listed) | |
475 | endfunction () | |
476 | ||
477 | function (_cooking_adjust_requirements) | |
478 | cmake_parse_arguments ( | |
479 | pa | |
480 | "" | |
481 | "IS_EXCLUDING;IS_INCLUDING;OUTPUT_LIST" | |
482 | "REQUIREMENTS" | |
483 | ${ARGN}) | |
484 | ||
485 | if (pa_IS_EXCLUDING) | |
486 | # Strip out any dependencies that are excluded. | |
487 | _cooking_set_difference ( | |
488 | pa_REQUIREMENTS | |
489 | Cooking_EXCLUDED_INGREDIENTS | |
490 | pa_REQUIREMENTS) | |
491 | elseif (_cooking_is_including) | |
492 | # Eliminate dependencies that have not been included. | |
493 | _cooking_set_intersection ( | |
494 | pa_REQUIREMENTS | |
495 | Cooking_INCLUDED_INGREDIENTS | |
496 | pa_REQUIREMENTS) | |
497 | endif () | |
498 | ||
499 | set (${pa_OUTPUT_LIST} ${pa_REQUIREMENTS} PARENT_SCOPE) | |
500 | endfunction () | |
501 | ||
502 | function (_cooking_populate_ep_depends) | |
503 | cmake_parse_arguments ( | |
504 | pa | |
505 | "" | |
506 | "" | |
507 | "REQUIREMENTS" | |
508 | ${ARGN}) | |
509 | ||
510 | if (pa_REQUIREMENTS) | |
511 | set (value DEPENDS) | |
512 | ||
513 | foreach (d ${pa_REQUIREMENTS}) | |
514 | list (APPEND value ingredient_${d}) | |
515 | endforeach () | |
516 | else () | |
517 | set (value "") | |
518 | endif () | |
519 | ||
520 | set (_cooking_ep_depends ${value} PARENT_SCOPE) | |
521 | endfunction () | |
522 | ||
523 | function (_cooking_prepare_restrictions_arguments) | |
524 | cmake_parse_arguments ( | |
525 | pa | |
526 | "" | |
527 | "IS_EXCLUDING;IS_INCLUDING;OUTPUT_LIST" | |
528 | "REQUIREMENTS" | |
529 | ${ARGN}) | |
530 | ||
531 | set (args "") | |
532 | ||
533 | if (pa_IS_INCLUDING) | |
534 | _cooking_set_difference ( | |
535 | Cooking_INCLUDED_INGREDIENTS | |
536 | pa_REQUIREMENTS | |
537 | included) | |
538 | ||
539 | foreach (x ${included}) | |
540 | list (APPEND args -i ${x}) | |
541 | endforeach () | |
542 | elseif (pa_IS_EXCLUDING) | |
543 | _cooking_set_union ( | |
544 | Cooking_EXCLUDED_INGREDIENTS | |
545 | pa_REQUIREMENTS | |
546 | excluded) | |
547 | ||
548 | foreach (x ${excluded}) | |
549 | list (APPEND args -e ${x}) | |
550 | endforeach () | |
551 | else () | |
552 | foreach (x ${pa_REQUIREMENTS}) | |
553 | list (APPEND args -e ${x}) | |
554 | endforeach () | |
555 | endif () | |
556 | ||
557 | set (${pa_OUTPUT_LIST} ${args} PARENT_SCOPE) | |
558 | endfunction () | |
559 | ||
560 | function (_cooking_determine_common_cmake_args output) | |
561 | string (REPLACE ";" ":::" prefix_path_with_colons "${CMAKE_PREFIX_PATH}") | |
562 | ||
1e59de90 TL |
563 | if (CMAKE_CXX_FLAGS) |
564 | list(APPEND cmake_args -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}) | |
565 | endif () | |
566 | if (CMAKE_C_FLAGS) | |
567 | list(APPEND cmake_args -DCMAKE_C_FLAGS=${CMAKE_C_FLAGS}) | |
568 | endif () | |
569 | ||
570 | list (APPEND cmake_args | |
11fdf7f2 TL |
571 | -DCMAKE_INSTALL_PREFIX=<INSTALL_DIR> |
572 | -DCMAKE_PREFIX_PATH=${prefix_path_with_colons} | |
573 | -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} | |
1e59de90 TL |
574 | -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} |
575 | -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}) | |
576 | ||
577 | set (${output} ${cmake_args} | |
11fdf7f2 TL |
578 | PARENT_SCOPE) |
579 | endfunction () | |
580 | ||
581 | function (_cooking_populate_ep_configure_command) | |
582 | cmake_parse_arguments ( | |
583 | pa | |
584 | "" | |
585 | "IS_EXCLUDING;IS_INCLUDING;RECIPE;EXTERNAL_PROJECT_ARGS_LIST" | |
586 | "REQUIREMENTS;CMAKE_ARGS;COOKING_CMAKE_ARGS" | |
587 | ${ARGN}) | |
588 | ||
589 | if (pa_RECIPE) | |
9f95a23c TL |
590 | if (pa_RECIPE STREQUAL <DEFAULT>) |
591 | set (recipe_args "") | |
592 | else () | |
593 | set (recipe_args -r ${pa_RECIPE}) | |
594 | endif () | |
595 | ||
11fdf7f2 TL |
596 | _cooking_prepare_restrictions_arguments ( |
597 | IS_EXCLUDING ${pa_IS_EXCLUDING} | |
598 | IS_INCLUDING ${pa_IS_INCLUDING} | |
599 | REQUIREMENTS ${pa_REQUIREMENTS} | |
600 | OUTPUT_LIST restrictions_args) | |
601 | ||
602 | set (value | |
603 | CONFIGURE_COMMAND | |
604 | <SOURCE_DIR>/cooking.sh | |
9f95a23c | 605 | ${recipe_args} |
11fdf7f2 TL |
606 | -d <BINARY_DIR> |
607 | -p ${Cooking_INGREDIENTS_DIR} | |
608 | -g ${CMAKE_GENERATOR} | |
609 | -x | |
610 | ${restrictions_args} | |
611 | -- | |
612 | ${pa_COOKING_CMAKE_ARGS}) | |
613 | elseif (NOT (CONFIGURE_COMMAND IN_LIST ${pa_EXTERNAL_PROJECT_ARGS_LIST})) | |
614 | set (value | |
615 | CONFIGURE_COMMAND | |
616 | ${CMAKE_COMMAND} | |
617 | ${pa_CMAKE_ARGS} | |
618 | <SOURCE_DIR>) | |
619 | else () | |
620 | set (value "") | |
621 | endif () | |
622 | ||
623 | set (_cooking_ep_configure_command ${value} PARENT_SCOPE) | |
624 | endfunction () | |
625 | ||
626 | function (_cooking_populate_ep_build_command ep_args_list) | |
627 | if (NOT (BUILD_COMMAND IN_LIST ${ep_args_list})) | |
628 | set (value BUILD_COMMAND ${CMAKE_COMMAND} --build <BINARY_DIR>) | |
629 | else () | |
630 | set (value "") | |
631 | endif () | |
632 | ||
633 | set (_cooking_ep_build_command ${value} PARENT_SCOPE) | |
634 | endfunction () | |
635 | ||
636 | function (_cooking_populate_ep_install_command ep_args_list) | |
637 | if (NOT (INSTALL_COMMAND IN_LIST ${ep_args_list})) | |
638 | set (value INSTALL_COMMAND ${CMAKE_COMMAND} --build <BINARY_DIR> --target install) | |
639 | else () | |
640 | set (value "") | |
641 | endif () | |
642 | ||
643 | set (_cooking_ep_install_command ${value} PARENT_SCOPE) | |
644 | endfunction () | |
645 | ||
646 | function (_cooking_define_ep) | |
647 | cmake_parse_arguments ( | |
648 | pa | |
649 | "" | |
650 | "NAME;SOURCE_DIR;BINARY_DIR;EXTERNAL_PROJECT_ARGS_LIST;RECIPE;INGREDIENT_DIR;STOW_DIR;LOCAL_RECONFIGURE;LOCAL_REBUILD" | |
651 | "DEPENDS;CONFIGURE_COMMAND;BUILD_COMMAND;INSTALL_COMMAND;CMAKE_ARGS" | |
652 | ${ARGN}) | |
653 | ||
654 | string (REPLACE "<DISABLE>" "" forwarded_ep_args "${${pa_EXTERNAL_PROJECT_ARGS_LIST}}") | |
655 | set (ep_name ingredient_${pa_NAME}) | |
656 | include (ExternalProject) | |
657 | ||
658 | set (stamp_dir ${pa_INGREDIENT_DIR}/stamp) | |
659 | ||
660 | ExternalProject_add (${ep_name} | |
661 | DEPENDS ${pa_DEPENDS} | |
662 | SOURCE_DIR ${pa_SOURCE_DIR} | |
663 | BINARY_DIR ${pa_BINARY_DIR} | |
664 | CONFIGURE_COMMAND ${pa_CONFIGURE_COMMAND} | |
665 | BUILD_COMMAND ${pa_BUILD_COMMAND} | |
666 | INSTALL_COMMAND ${pa_INSTALL_COMMAND} | |
667 | PREFIX ${pa_INGREDIENT_DIR} | |
668 | STAMP_DIR ${stamp_dir} | |
669 | INSTALL_DIR ${pa_STOW_DIR}/${pa_NAME} | |
670 | CMAKE_ARGS ${pa_CMAKE_ARGS} | |
671 | LIST_SEPARATOR ::: | |
672 | STEP_TARGETS install | |
673 | "${forwarded_ep_args}") | |
674 | ||
675 | set (stow_marker_file ${Cooking_INGREDIENTS_DIR}/.cooking_ingredient_${pa_NAME}) | |
676 | set (lock_file ${Cooking_INGREDIENTS_DIR}/.cooking_stow.lock) | |
677 | ||
678 | add_custom_command ( | |
679 | OUTPUT ${stow_marker_file} | |
680 | DEPENDS | |
681 | ${ep_name}-install | |
682 | ${stamp_dir}/ingredient_${pa_NAME}-install | |
683 | COMMAND | |
684 | flock | |
685 | --wait 30 | |
686 | ${lock_file} | |
687 | ${Cooking_STOW_EXECUTABLE} | |
688 | -t ${Cooking_INGREDIENTS_DIR} | |
689 | -d ${pa_STOW_DIR} | |
690 | ${pa_NAME} | |
691 | COMMAND ${CMAKE_COMMAND} -E touch ${stow_marker_file}) | |
692 | ||
693 | add_custom_target (_cooking_ingredient_${pa_NAME}_stowed | |
694 | DEPENDS ${stow_marker_file}) | |
695 | ||
696 | if (pa_RECIPE) | |
697 | set (reconfigure_marker_file ${Cooking_INGREDIENTS_DIR}/.cooking_reconfigure_ingredient_${pa_NAME}) | |
698 | ||
699 | add_custom_command ( | |
700 | OUTPUT ${reconfigure_marker_file} | |
701 | COMMAND ${CMAKE_COMMAND} -E touch ${reconfigure_marker_file}) | |
702 | ||
703 | add_custom_target (_cooking_ingredient_${pa_NAME}_marked_for_reconfigure | |
704 | DEPENDS ${reconfigure_marker_file}) | |
705 | ||
706 | ExternalProject_add_step (${ep_name} | |
707 | cooking-reconfigure | |
708 | DEPENDERS configure | |
709 | DEPENDS ${reconfigure_marker_file} | |
710 | COMMAND ${CMAKE_COMMAND} -E echo_append) | |
711 | ||
712 | ExternalProject_add_stepdependencies (${ep_name} | |
713 | cooking-reconfigure | |
714 | _cooking_ingredient_${pa_NAME}_marked_for_reconfigure) | |
715 | endif () | |
716 | ||
717 | foreach (d ${pa_DEPENDS}) | |
718 | ExternalProject_add_stepdependencies (${ep_name} | |
719 | configure | |
720 | _cooking_${d}_stowed) | |
721 | endforeach () | |
722 | ||
723 | add_dependencies (_cooking_ingredients _cooking_ingredient_${pa_NAME}_stowed) | |
724 | ||
725 | if (pa_LOCAL_RECONFIGURE OR pa_LOCAL_REBUILD) | |
726 | if (pa_LOCAL_RECONFIGURE) | |
727 | set (step configure) | |
728 | else () | |
729 | set (step build) | |
730 | endif () | |
731 | ||
732 | ExternalProject_add_step (${ep_name} | |
733 | cooking-local-${step} | |
734 | DEPENDERS ${step} | |
735 | DEPENDS ${_cooking_local_synchronize_marker_file} | |
736 | COMMAND ${CMAKE_COMMAND} -E echo_append) | |
737 | ||
738 | ExternalProject_add_stepdependencies (${ep_name} | |
739 | cooking-local-${step} | |
740 | _cooking_marked_for_local_synchronization) | |
741 | endif () | |
742 | endfunction () | |
743 | ||
744 | macro (cooking_ingredient name) | |
745 | set (_cooking_args "${ARGN}") | |
746 | ||
747 | if ((_cooking_is_excluding AND (${name} IN_LIST Cooking_EXCLUDED_INGREDIENTS)) | |
748 | OR (_cooking_is_including AND (NOT (${name} IN_LIST Cooking_INCLUDED_INGREDIENTS)))) | |
749 | # Nothing. | |
750 | else () | |
751 | set (_cooking_ingredient_dir ${_cooking_dir}/ingredient/${name}) | |
752 | ||
753 | cmake_parse_arguments ( | |
754 | _cooking_pa | |
755 | "LOCAL_RECONFIGURE;LOCAL_REBUILD" | |
756 | "COOKING_RECIPE" | |
757 | "CMAKE_ARGS;COOKING_CMAKE_ARGS;EXTERNAL_PROJECT_ARGS;REQUIRES" | |
758 | ${_cooking_args}) | |
759 | ||
760 | _cooking_populate_ep_parameter ( | |
761 | EXTERNAL_PROJECT_ARGS_LIST _cooking_pa_EXTERNAL_PROJECT_ARGS | |
762 | PARAMETER SOURCE_DIR | |
763 | DEFAULT_VALUE ${_cooking_ingredient_dir}/src) | |
764 | ||
765 | _cooking_populate_ep_parameter ( | |
766 | EXTERNAL_PROJECT_ARGS_LIST _cooking_pa_EXTERNAL_PROJECT_ARGS | |
767 | PARAMETER BINARY_DIR | |
768 | DEFAULT_VALUE ${_cooking_ingredient_dir}/build) | |
769 | ||
770 | _cooking_populate_ep_parameter ( | |
771 | EXTERNAL_PROJECT_ARGS_LIST _cooking_pa_EXTERNAL_PROJECT_ARGS | |
772 | PARAMETER BUILD_IN_SOURCE | |
773 | DEFAULT_VALUE OFF) | |
774 | ||
775 | if (_cooking_build_in_source) | |
776 | set (_cooking_ep_binary_dir "") | |
777 | endif () | |
778 | ||
779 | if (Cooking_LIST_ONLY) | |
780 | _cooking_define_listing_targets ( | |
781 | NAME ${name} | |
782 | SOURCE_DIR ${_cooking_source_dir} | |
783 | RECIPE ${_cooking_pa_COOKING_RECIPE} | |
784 | REQUIRES ${_cooking_pa_REQUIRES}) | |
785 | else () | |
786 | _cooking_adjust_requirements ( | |
787 | IS_EXCLUDING ${_cooking_is_excluding} | |
788 | IS_INCLUDING ${_cooking_is_including} | |
789 | REQUIREMENTS ${_cooking_pa_REQUIRES} | |
790 | OUTPUT_LIST _cooking_pa_REQUIRES) | |
791 | ||
792 | _cooking_populate_ep_depends ( | |
793 | REQUIREMENTS ${_cooking_pa_REQUIRES}) | |
794 | ||
795 | _cooking_determine_common_cmake_args (_cooking_common_cmake_args) | |
796 | ||
797 | _cooking_populate_ep_configure_command ( | |
798 | IS_EXCLUDING ${_cooking_is_excluding} | |
799 | IS_INCLUDING ${_cooking_is_including} | |
800 | RECIPE ${_cooking_pa_COOKING_RECIPE} | |
801 | REQUIREMENTS ${_cooking_pa_REQUIRES} | |
802 | EXTERNAL_PROJECT_ARGS_LIST _cooking_pa_EXTERNAL_PROJECT_ARGS | |
803 | CMAKE_ARGS | |
804 | ${_cooking_common_cmake_args} | |
805 | ${_cooking_pa_CMAKE_ARGS} | |
806 | COOKING_CMAKE_ARGS | |
807 | ${_cooking_common_cmake_args} | |
808 | ${_cooking_pa_COOKING_CMAKE_ARGS}) | |
809 | ||
810 | _cooking_populate_ep_build_command (_cooking_pa_EXTERNAL_PROJECT_ARGS) | |
811 | _cooking_populate_ep_install_command (_cooking_pa_EXTERNAL_PROJECT_ARGS) | |
812 | ||
813 | _cooking_define_ep ( | |
814 | NAME ${name} | |
815 | RECIPE ${_cooking_pa_COOKING_RECIPE} | |
816 | DEPENDS ${_cooking_ep_depends} | |
817 | SOURCE_DIR ${_cooking_ep_source_dir} | |
818 | BINARY_DIR ${_cooking_ep_binary_dir} | |
819 | CONFIGURE_COMMAND ${_cooking_ep_configure_command} | |
820 | BUILD_COMMAND ${_cooking_ep_build_command} | |
821 | INSTALL_COMMAND ${_cooking_ep_install_command} | |
822 | INGREDIENT_DIR ${_cooking_ingredient_dir} | |
823 | STOW_DIR ${_cooking_dir}/stow | |
824 | CMAKE_ARGS ${_cooking_common_cmake_args} | |
825 | EXTERNAL_PROJECT_ARGS_LIST _cooking_pa_EXTERNAL_PROJECT_ARGS | |
826 | LOCAL_RECONFIGURE ${_cooking_pa_LOCAL_RECONFIGURE} | |
827 | LOCAL_REBUILD ${_cooking_pa_LOCAL_REBUILD}) | |
828 | endif () | |
829 | endif () | |
830 | endmacro () | |
831 | EOF | |
832 | ||
833 | cmake_cooking_args=( | |
834 | "-DCooking_INGREDIENTS_DIR=${ingredients_dir}" | |
835 | "-DCooking_RECIPE=${recipe}" | |
836 | ) | |
837 | ||
9f95a23c TL |
838 | # |
839 | # Remove any `Cooking.cmake` file from the source directory. We now generate this file in the build directory, and old | |
840 | # copies will cause conflicts. | |
841 | # | |
842 | ||
843 | old_cooking_file="${source_dir}/cmake/Cooking.cmake" | |
844 | ||
845 | if [ -f "${old_cooking_file}" ]; then | |
846 | grep 'This file was generated by cmake-cooking' "${old_cooking_file}" > /dev/null && rm "${old_cooking_file}" | |
847 | fi | |
848 | ||
11fdf7f2 TL |
849 | # |
850 | # Clean-up from a previous run. | |
851 | # | |
852 | ||
853 | if [ -e "${ingredients_ready_file}" ]; then | |
854 | rm "${ingredients_ready_file}" | |
855 | fi | |
856 | ||
857 | if [ -e "${cache_file}" ]; then | |
858 | rm "${cache_file}" | |
859 | fi | |
860 | ||
861 | if [ -d "${ingredients_dir}" -a -z "${nested}" ]; then | |
862 | rm -r --preserve-root "${ingredients_dir}" | |
863 | fi | |
864 | ||
865 | mkdir -p "${ingredients_dir}" | |
866 | ||
867 | # | |
868 | # Validate recipe. | |
869 | # | |
870 | ||
871 | if [ -n "${recipe}" ]; then | |
9f95a23c | 872 | if [ ! -f "${recipe}" ]; then |
11fdf7f2 TL |
873 | echo "Cooking: The '${recipe}' recipe does not exist!" >&2 |
874 | exit 1 | |
875 | fi | |
876 | fi | |
877 | ||
878 | # | |
879 | # Prepare lists of included and excluded ingredients. | |
880 | # | |
881 | ||
882 | if [ -n "${excluded_ingredients}" ] && [ -z "${list_only}" ]; then | |
883 | cmake_cooking_args+=( | |
884 | -DCooking_EXCLUDED_INGREDIENTS=$(printf "%s;" "${excluded_ingredients[@]}") | |
885 | -DCooking_INCLUDED_INGREDIENTS= | |
886 | ) | |
887 | fi | |
888 | ||
889 | if [ -n "${included_ingredients}" ] && [ -z "${list_only}" ]; then | |
890 | cmake_cooking_args+=( | |
891 | -DCooking_EXCLUDED_INGREDIENTS= | |
892 | -DCooking_INCLUDED_INGREDIENTS=$(printf "%s;" "${included_ingredients[@]}") | |
893 | ) | |
894 | fi | |
895 | ||
896 | # | |
897 | # Configure and build ingredients. | |
898 | # | |
899 | ||
11fdf7f2 TL |
900 | mkdir -p "${cooking_dir}"/stow |
901 | touch "${cooking_dir}"/stow/.stow | |
902 | cd "${build_dir}" | |
903 | ||
904 | declare -a build_args | |
905 | ||
906 | if [ "${generator}" == "Ninja" ]; then | |
907 | build_args+=(-v) | |
908 | fi | |
909 | ||
910 | if [ -n "${list_only}" ]; then | |
911 | cmake_cooking_args+=("-DCooking_LIST_ONLY=ON") | |
912 | fi | |
913 | ||
914 | ${CMAKE} -DCMAKE_BUILD_TYPE="${build_type}" "${cmake_cooking_args[@]}" -G "${generator}" "${source_dir}" "${@}" | |
11fdf7f2 | 915 | |
9f95a23c TL |
916 | if [ -n "${recipe}" ]; then |
917 | ${CMAKE} --build . --target _cooking_ingredients_ready -- "${build_args[@]}" | |
918 | ||
919 | # | |
920 | # Report what we've done (if we're not nested). | |
921 | # | |
922 | ||
923 | if [ -z "${nested}" ]; then | |
924 | ingredients=($(find "${ingredients_dir}" -name '.cooking_ingredient_*' -printf '%f\n' | sed -r 's/\.cooking_ingredient_(.+)/\1/')) | |
11fdf7f2 | 925 | |
9f95a23c TL |
926 | if [ -z "${list_only}" ]; then |
927 | printf "\nCooking: Installed the following ingredients:\n" | |
928 | else | |
929 | printf "\nCooking: The following ingredients are necessary for this recipe:\n" | |
930 | fi | |
11fdf7f2 | 931 | |
9f95a23c TL |
932 | for ingredient in "${ingredients[@]}"; do |
933 | echo " - ${ingredient}" | |
934 | done | |
935 | ||
936 | printf '\n' | |
11fdf7f2 TL |
937 | fi |
938 | ||
9f95a23c TL |
939 | if [ -n "${list_only}" ]; then |
940 | exit 0 | |
941 | fi | |
11fdf7f2 | 942 | |
9f95a23c TL |
943 | # |
944 | # Configure the project, expecting all requirements satisfied. | |
945 | # | |
11fdf7f2 | 946 | |
9f95a23c | 947 | ${CMAKE} -DCMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY=ON "${@}" . |
11fdf7f2 | 948 | |
9f95a23c TL |
949 | # |
950 | # Optionally export the installed files. | |
951 | # | |
11fdf7f2 | 952 | |
9f95a23c TL |
953 | if [ -n "${export_dir}" ]; then |
954 | rsync "${ingredients_dir}/" "${export_dir}" -a --copy-links | |
955 | printf "\nCooking: Exported ingredients to ${export_dir}\n" | |
956 | fi | |
957 | fi | |
11fdf7f2 TL |
958 | |
959 | # | |
960 | # Save invocation information. | |
961 | # | |
962 | ||
963 | cd "${initial_wd}" | |
964 | ||
965 | cat <<EOF > "${memory_file}" | |
966 | run_previous() { | |
967 | "${0}" ${invoked_args[@]@Q} | |
968 | } | |
969 | EOF |