973 lines
26 KiB
Bash
Executable File
973 lines
26 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
#
|
|
# Copyright 2018 Jesse Haber-Kucharsky
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
#
|
|
|
|
#
|
|
# This is cmake-cooking v0.10.0
|
|
# The home of cmake-cooking is https://github.com/hakuch/CMakeCooking
|
|
#
|
|
|
|
set -e
|
|
|
|
CMAKE=${CMAKE:-cmake}
|
|
|
|
invoked_args=("$@")
|
|
source_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|
initial_wd=$(pwd)
|
|
memory_file="${initial_wd}/.cooking_memory"
|
|
|
|
recipe="${source_dir}/cooking_recipe.cmake"
|
|
declare -a excluded_ingredients
|
|
declare -a included_ingredients
|
|
build_dir="${initial_wd}/build"
|
|
build_type="Debug"
|
|
# Depends on `build_dir`.
|
|
ingredients_dir=""
|
|
generator="Ninja"
|
|
list_only=""
|
|
nested=""
|
|
|
|
usage() {
|
|
cat <<EOF
|
|
|
|
Fetch, configure, build, and install dependencies ("ingredients") for a CMake project
|
|
in a local and repeatable development environment.
|
|
|
|
Usage: $0 [OPTIONS]
|
|
|
|
where OPTIONS are:
|
|
|
|
-a
|
|
-r RECIPE
|
|
-e INGREDIENT
|
|
-i INGREDIENT
|
|
-d BUILD_DIR (=${build_dir})
|
|
-p INGREDIENTS_DIR (=${build_dir}/_cooking/installed)
|
|
-t BUILD_TYPE (=${build_type})
|
|
-g GENERATOR (=${generator})
|
|
-s VAR=VALUE
|
|
-f EXPORT_DIR
|
|
-l
|
|
-h
|
|
|
|
By default, cmake-cooking reads a file called 'cooking_recipe.cmake'.
|
|
|
|
If neither [-i] nor [-e] are specified with a recipe ([-r]), then all ingredients of the recipe
|
|
will be fetched and built.
|
|
|
|
[-i] and [-e] are mutually-exclusive options: only provide one.
|
|
|
|
Option details:
|
|
|
|
-a
|
|
|
|
Invoke 'cooking.sh' with the arguments that were provided to it last time, instead
|
|
of the arguments provided.
|
|
|
|
-r RECIPE
|
|
|
|
Instead of reading the recipe in a file called 'cooking_recipe.cmake', follow the recipe
|
|
in the named file.
|
|
|
|
If the recipe file is a relative path, it is interpretted relative to the source directory
|
|
of the project.
|
|
|
|
-e INGREDIENT
|
|
|
|
Exclude an ingredient from a recipe. This option can be supplied many times.
|
|
|
|
For example, if a recipe consists of 'apple', 'banana', 'carrot', and 'donut', then
|
|
|
|
./cooking.sh -r dev -e apple -e donut
|
|
|
|
will prepare 'banana' and 'carrot' but not prepare 'apple' and 'donut'.
|
|
|
|
If an ingredient is excluded, then it is assumed that all ingredients that depend on it
|
|
can satisfy that dependency in some other way from the system (ie, the dependency is
|
|
removed internally).
|
|
|
|
-i INGREDIENT
|
|
|
|
Include an ingredient from a recipe, ignoring the others. This option can be supplied
|
|
many times.
|
|
|
|
Similar to [-e], but the opposite.
|
|
|
|
For example, if a recipe consists of 'apple', 'banana', 'carrot', and 'donut' then
|
|
|
|
./cooking.sh -r dev -i apple -i donut
|
|
|
|
will prepare 'apple' and 'donut' but not prepare 'banana' and 'carrot'.
|
|
|
|
If an ingredient is not in the "include-list", then it is assumed that all
|
|
ingredients that are in the list and which depend on it can satisfy that dependency
|
|
in some other way from the system.
|
|
|
|
-d BUILD_DIR (=${build_dir})
|
|
|
|
Configure the project and build it in the named directory.
|
|
|
|
-p INGREDIENTS_DIR (=${build_dir}/_cooking/installed)
|
|
|
|
Install compiled ingredients into this directory.
|
|
|
|
-t BUILD_TYPE (=${build_type})
|
|
|
|
Configure all ingredients and the project with the named CMake build-type.
|
|
An example build type is "Release".
|
|
|
|
-g GENERATOR (=${generator})
|
|
|
|
Use the named CMake generator for building all ingredients and the project.
|
|
An example generator is "Unix Makfiles".
|
|
|
|
-s VAR=VALUE
|
|
|
|
Set an environmental variable 'VAR' to the value 'VALUE' during the invocation of CMake.
|
|
|
|
-f EXPORT_DIR
|
|
|
|
If provided, and the project is successfully configured, then the tree of installed ingredients
|
|
is exported to this directory (the actual files: not symbolic links).
|
|
|
|
This option requires rsync.
|
|
|
|
This may be useful for preparing continuous integration environments, but it is not
|
|
recommended for distribution or release purposes (since this would be counter
|
|
to the goal of cmake-cooking).
|
|
|
|
-l
|
|
|
|
Only list available ingredients for a given recipe, and don't do anything else.
|
|
|
|
-h
|
|
|
|
Show this help information and exit.
|
|
|
|
EOF
|
|
}
|
|
|
|
parse_assignment() {
|
|
local var
|
|
local value
|
|
IFS='=' read -r var value <<< "${1}"
|
|
export "${var}"="${value}"
|
|
}
|
|
|
|
yell_include_exclude_mutually_exclusive() {
|
|
echo "Cooking: [-e] and [-i] are mutually exclusive options!" >&2
|
|
}
|
|
|
|
while getopts "ar:e:i:d:p:t:g:s:f:lhx" arg; do
|
|
case "${arg}" in
|
|
a)
|
|
if [ ! -f "${memory_file}" ]; then
|
|
echo "No previous invocation found to recall!" >&2
|
|
exit 1
|
|
fi
|
|
|
|
source "${memory_file}"
|
|
run_previous && exit 0
|
|
;;
|
|
r)
|
|
if [[ "${OPTARG}" = /* ]]; then
|
|
recipe=${OPTARG}
|
|
else
|
|
recipe="${source_dir}/${OPTARG}"
|
|
fi
|
|
;;
|
|
e)
|
|
if [[ ${#included_ingredients[@]} -ne 0 ]]; then
|
|
yell_include_exclude_mutually_exclusive
|
|
exit 1
|
|
fi
|
|
|
|
excluded_ingredients+=(${OPTARG})
|
|
;;
|
|
i)
|
|
if [[ ${#excluded_ingredients[@]} -ne 0 ]]; then
|
|
yell_include_exclude_mutually_exclusive
|
|
exit 1
|
|
fi
|
|
|
|
included_ingredients+=(${OPTARG})
|
|
;;
|
|
d) build_dir=$(realpath "${OPTARG}") ;;
|
|
p) ingredients_dir=$(realpath "${OPTARG}") ;;
|
|
t) build_type=${OPTARG} ;;
|
|
g) generator=${OPTARG} ;;
|
|
s) parse_assignment "${OPTARG}" ;;
|
|
f) export_dir=$(realpath "${OPTARG}") ;;
|
|
l) list_only="1" ;;
|
|
h) usage; exit 0 ;;
|
|
x) nested="1" ;;
|
|
*) usage; exit 1 ;;
|
|
esac
|
|
done
|
|
|
|
shift $((OPTIND - 1))
|
|
|
|
cooking_dir="${build_dir}/_cooking"
|
|
cache_file="${build_dir}/CMakeCache.txt"
|
|
ingredients_ready_file="${cooking_dir}/ready.txt"
|
|
|
|
if [ -z "${ingredients_dir}" ]; then
|
|
ingredients_dir="${cooking_dir}/installed"
|
|
fi
|
|
|
|
mkdir -p "${build_dir}"
|
|
|
|
cat <<'EOF' > "${build_dir}/Cooking.cmake"
|
|
#
|
|
# Copyright 2018 Jesse Haber-Kucharsky
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
#
|
|
|
|
#
|
|
# This file was generated by cmake-cooking v0.10.0
|
|
# The home of cmake-cooking is https://github.com/hakuch/CMakeCooking
|
|
#
|
|
|
|
macro (project name)
|
|
set (_cooking_dir ${CMAKE_CURRENT_BINARY_DIR}/_cooking)
|
|
|
|
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
|
set (_cooking_root ON)
|
|
else ()
|
|
set (_cooking_root OFF)
|
|
endif ()
|
|
|
|
find_program (Cooking_STOW_EXECUTABLE
|
|
stow
|
|
"Executable path of GNU Stow.")
|
|
|
|
if (NOT Cooking_STOW_EXECUTABLE)
|
|
message (FATAL_ERROR "Cooking: GNU Stow is required!")
|
|
endif ()
|
|
|
|
set (Cooking_INGREDIENTS_DIR
|
|
${_cooking_dir}/installed
|
|
CACHE
|
|
PATH
|
|
"Directory where ingredients will be installed.")
|
|
|
|
set (Cooking_EXCLUDED_INGREDIENTS
|
|
""
|
|
CACHE
|
|
STRING
|
|
"Semicolon-separated list of ingredients that are not provided by Cooking.")
|
|
|
|
set (Cooking_INCLUDED_INGREDIENTS
|
|
""
|
|
CACHE
|
|
STRING
|
|
"Semicolon-separated list of ingredients that are provided by Cooking.")
|
|
|
|
option (Cooking_LIST_ONLY
|
|
"Available ingredients will be listed and nothing will be installed."
|
|
OFF)
|
|
|
|
set (Cooking_RECIPE "" CACHE STRING "Configure ${name}'s dependencies according to the named recipe.")
|
|
|
|
if ((NOT DEFINED Cooking_EXCLUDED_INGREDIENTS) OR (Cooking_EXCLUDED_INGREDIENTS STREQUAL ""))
|
|
set (_cooking_is_excluding OFF)
|
|
else ()
|
|
set (_cooking_is_excluding ON)
|
|
endif ()
|
|
|
|
if ((NOT DEFINED Cooking_INCLUDED_INGREDIENTS) OR (Cooking_INCLUDED_INGREDIENTS STREQUAL ""))
|
|
set (_cooking_is_including OFF)
|
|
else ()
|
|
set (_cooking_is_including ON)
|
|
endif ()
|
|
|
|
if (_cooking_is_excluding AND _cooking_is_including)
|
|
message (
|
|
FATAL_ERROR
|
|
"Cooking: The EXCLUDED_INGREDIENTS and INCLUDED_INGREDIENTS lists are mutually exclusive options!")
|
|
endif ()
|
|
|
|
if (_cooking_root)
|
|
_project (${name} ${ARGN})
|
|
|
|
if (NOT ("${Cooking_RECIPE}" STREQUAL ""))
|
|
add_custom_target (_cooking_ingredients)
|
|
|
|
set (_cooking_ready_marker_file ${_cooking_dir}/ready.txt)
|
|
|
|
add_custom_command (
|
|
OUTPUT ${_cooking_ready_marker_file}
|
|
DEPENDS _cooking_ingredients
|
|
COMMAND ${CMAKE_COMMAND} -E touch ${_cooking_ready_marker_file})
|
|
|
|
add_custom_target (_cooking_ingredients_ready
|
|
DEPENDS ${_cooking_ready_marker_file})
|
|
|
|
set (_cooking_local_synchronize_marker_file ${Cooking_INGREDIENTS_DIR}/.cooking_local_synchronize)
|
|
|
|
add_custom_command (
|
|
OUTPUT ${_cooking_local_synchronize_marker_file}
|
|
COMMAND ${CMAKE_COMMAND} -E touch ${_cooking_local_synchronize_marker_file})
|
|
|
|
add_custom_target (_cooking_marked_for_local_synchronization
|
|
DEPENDS ${_cooking_local_synchronize_marker_file})
|
|
|
|
list (APPEND CMAKE_PREFIX_PATH ${Cooking_INGREDIENTS_DIR})
|
|
include (${Cooking_RECIPE})
|
|
|
|
if (NOT EXISTS ${_cooking_ready_marker_file})
|
|
return ()
|
|
endif ()
|
|
endif ()
|
|
endif ()
|
|
endmacro ()
|
|
|
|
function (_cooking_set_union x y var)
|
|
set (r ${${x}})
|
|
|
|
foreach (e ${${y}})
|
|
list (APPEND r ${e})
|
|
endforeach ()
|
|
|
|
list (REMOVE_DUPLICATES r)
|
|
set (${var} ${r} PARENT_SCOPE)
|
|
endfunction ()
|
|
|
|
function (_cooking_set_difference x y var)
|
|
set (r ${${x}})
|
|
|
|
foreach (e ${${y}})
|
|
if (${e} IN_LIST ${x})
|
|
list (REMOVE_ITEM r ${e})
|
|
endif ()
|
|
endforeach ()
|
|
|
|
set (${var} ${r} PARENT_SCOPE)
|
|
endfunction ()
|
|
|
|
function (_cooking_set_intersection x y var)
|
|
set (r "")
|
|
|
|
foreach (e ${${y}})
|
|
if (${e} IN_LIST ${x})
|
|
list (APPEND r ${e})
|
|
endif ()
|
|
endforeach ()
|
|
|
|
list (REMOVE_DUPLICATES r)
|
|
set (${var} ${r} PARENT_SCOPE)
|
|
endfunction ()
|
|
|
|
function (_cooking_query_by_key list key var)
|
|
list (FIND ${list} ${key} index)
|
|
|
|
if (${index} EQUAL "-1")
|
|
set (value NOTFOUND)
|
|
else ()
|
|
math (EXPR value_index "${index} + 1")
|
|
list (GET ${list} ${value_index} value)
|
|
endif ()
|
|
|
|
set (${var} ${value} PARENT_SCOPE)
|
|
endfunction ()
|
|
|
|
function (_cooking_populate_ep_parameter)
|
|
cmake_parse_arguments (
|
|
pa
|
|
""
|
|
"EXTERNAL_PROJECT_ARGS_LIST;PARAMETER;DEFAULT_VALUE"
|
|
""
|
|
${ARGN})
|
|
|
|
string (TOLOWER ${pa_PARAMETER} parameter_lower)
|
|
_cooking_query_by_key (${pa_EXTERNAL_PROJECT_ARGS_LIST} ${pa_PARAMETER} ${parameter_lower})
|
|
set (value ${${parameter_lower}})
|
|
set (var_name _cooking_${parameter_lower})
|
|
set (ep_var_name _cooking_ep_${parameter_lower})
|
|
|
|
if (NOT value)
|
|
set (${var_name} ${pa_DEFAULT_VALUE} PARENT_SCOPE)
|
|
set (${ep_var_name} ${pa_PARAMETER} ${pa_DEFAULT_VALUE} PARENT_SCOPE)
|
|
else ()
|
|
set (${var_name} ${value} PARENT_SCOPE)
|
|
set (${ep_var_name} "" PARENT_SCOPE)
|
|
endif ()
|
|
endfunction ()
|
|
|
|
function (_cooking_define_listing_targets)
|
|
cmake_parse_arguments (
|
|
pa
|
|
""
|
|
"NAME;SOURCE_DIR;RECIPE"
|
|
"REQUIRES"
|
|
${ARGN})
|
|
|
|
set (stale_file ${Cooking_INGREDIENTS_DIR}/.cooking_stale_ingredient_${pa_NAME})
|
|
|
|
add_custom_command (
|
|
OUTPUT ${stale_file}
|
|
COMMAND ${CMAKE_COMMAND} -E touch ${stale_file})
|
|
|
|
add_custom_target (_cooking_ingredient_${pa_NAME}_stale
|
|
DEPENDS ${stale_file})
|
|
|
|
set (commands
|
|
COMMAND
|
|
${CMAKE_COMMAND} -E touch ${Cooking_INGREDIENTS_DIR}/.cooking_ingredient_${pa_NAME})
|
|
|
|
if (pa_RECIPE)
|
|
if (pa_RECIPE STREQUAL <DEFAULT>)
|
|
set (recipe_args "")
|
|
else ()
|
|
set (recipe_args -r ${pa_RECIPE})
|
|
endif ()
|
|
|
|
list (INSERT commands 0
|
|
COMMAND
|
|
${pa_SOURCE_DIR}/cooking.sh
|
|
${recipe_args}
|
|
-p ${Cooking_INGREDIENTS_DIR}
|
|
-g ${CMAKE_GENERATOR}
|
|
-x
|
|
-l)
|
|
endif ()
|
|
|
|
add_custom_command (
|
|
OUTPUT ${Cooking_INGREDIENTS_DIR}/.cooking_ingredient_${pa_NAME}
|
|
DEPENDS
|
|
_cooking_ingredient_${pa_NAME}_stale
|
|
${stale_file}
|
|
${commands})
|
|
|
|
add_custom_target (_cooking_ingredient_${pa_NAME}_listed
|
|
DEPENDS ${Cooking_INGREDIENTS_DIR}/.cooking_ingredient_${pa_NAME})
|
|
|
|
foreach (d ${pa_REQUIRES})
|
|
add_dependencies (_cooking_ingredient_${pa_NAME}_listed _cooking_ingredient_${d}_listed)
|
|
endforeach ()
|
|
|
|
add_dependencies (_cooking_ingredients _cooking_ingredient_${pa_NAME}_listed)
|
|
endfunction ()
|
|
|
|
function (_cooking_adjust_requirements)
|
|
cmake_parse_arguments (
|
|
pa
|
|
""
|
|
"IS_EXCLUDING;IS_INCLUDING;OUTPUT_LIST"
|
|
"REQUIREMENTS"
|
|
${ARGN})
|
|
|
|
if (pa_IS_EXCLUDING)
|
|
# Strip out any dependencies that are excluded.
|
|
_cooking_set_difference (
|
|
pa_REQUIREMENTS
|
|
Cooking_EXCLUDED_INGREDIENTS
|
|
pa_REQUIREMENTS)
|
|
elseif (_cooking_is_including)
|
|
# Eliminate dependencies that have not been included.
|
|
_cooking_set_intersection (
|
|
pa_REQUIREMENTS
|
|
Cooking_INCLUDED_INGREDIENTS
|
|
pa_REQUIREMENTS)
|
|
endif ()
|
|
|
|
set (${pa_OUTPUT_LIST} ${pa_REQUIREMENTS} PARENT_SCOPE)
|
|
endfunction ()
|
|
|
|
function (_cooking_populate_ep_depends)
|
|
cmake_parse_arguments (
|
|
pa
|
|
""
|
|
""
|
|
"REQUIREMENTS"
|
|
${ARGN})
|
|
|
|
if (pa_REQUIREMENTS)
|
|
set (value DEPENDS)
|
|
|
|
foreach (d ${pa_REQUIREMENTS})
|
|
list (APPEND value ingredient_${d})
|
|
endforeach ()
|
|
else ()
|
|
set (value "")
|
|
endif ()
|
|
|
|
set (_cooking_ep_depends ${value} PARENT_SCOPE)
|
|
endfunction ()
|
|
|
|
function (_cooking_prepare_restrictions_arguments)
|
|
cmake_parse_arguments (
|
|
pa
|
|
""
|
|
"IS_EXCLUDING;IS_INCLUDING;OUTPUT_LIST"
|
|
"REQUIREMENTS"
|
|
${ARGN})
|
|
|
|
set (args "")
|
|
|
|
if (pa_IS_INCLUDING)
|
|
_cooking_set_difference (
|
|
Cooking_INCLUDED_INGREDIENTS
|
|
pa_REQUIREMENTS
|
|
included)
|
|
|
|
foreach (x ${included})
|
|
list (APPEND args -i ${x})
|
|
endforeach ()
|
|
elseif (pa_IS_EXCLUDING)
|
|
_cooking_set_union (
|
|
Cooking_EXCLUDED_INGREDIENTS
|
|
pa_REQUIREMENTS
|
|
excluded)
|
|
|
|
foreach (x ${excluded})
|
|
list (APPEND args -e ${x})
|
|
endforeach ()
|
|
else ()
|
|
foreach (x ${pa_REQUIREMENTS})
|
|
list (APPEND args -e ${x})
|
|
endforeach ()
|
|
endif ()
|
|
|
|
set (${pa_OUTPUT_LIST} ${args} PARENT_SCOPE)
|
|
endfunction ()
|
|
|
|
function (_cooking_determine_common_cmake_args output)
|
|
string (REPLACE ";" ":::" prefix_path_with_colons "${CMAKE_PREFIX_PATH}")
|
|
|
|
set (cmake_args "-G" "${CMAKE_GENERATOR}")
|
|
|
|
if (CMAKE_CXX_FLAGS)
|
|
list(APPEND cmake_args -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS})
|
|
endif ()
|
|
if (CMAKE_C_FLAGS)
|
|
list(APPEND cmake_args -DCMAKE_C_FLAGS=${CMAKE_C_FLAGS})
|
|
endif ()
|
|
|
|
list (APPEND cmake_args
|
|
-DCMAKE_INSTALL_PREFIX=<INSTALL_DIR>
|
|
-DCMAKE_PREFIX_PATH=${prefix_path_with_colons}
|
|
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
|
|
-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
|
|
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
|
|
-DBUILD_SHARED_LIBS=${BUILD_SHARED_LIBS})
|
|
|
|
set (${output} ${cmake_args}
|
|
PARENT_SCOPE)
|
|
endfunction ()
|
|
|
|
function (_cooking_populate_ep_configure_command)
|
|
cmake_parse_arguments (
|
|
pa
|
|
""
|
|
"IS_EXCLUDING;IS_INCLUDING;RECIPE;EXTERNAL_PROJECT_ARGS_LIST"
|
|
"REQUIREMENTS;CMAKE_ARGS;COOKING_CMAKE_ARGS"
|
|
${ARGN})
|
|
|
|
if (pa_RECIPE)
|
|
if (pa_RECIPE STREQUAL <DEFAULT>)
|
|
set (recipe_args "")
|
|
else ()
|
|
set (recipe_args -r ${pa_RECIPE})
|
|
endif ()
|
|
|
|
_cooking_prepare_restrictions_arguments (
|
|
IS_EXCLUDING ${pa_IS_EXCLUDING}
|
|
IS_INCLUDING ${pa_IS_INCLUDING}
|
|
REQUIREMENTS ${pa_REQUIREMENTS}
|
|
OUTPUT_LIST restrictions_args)
|
|
|
|
set (value
|
|
CONFIGURE_COMMAND
|
|
<SOURCE_DIR>/cooking.sh
|
|
${recipe_args}
|
|
-d <BINARY_DIR>
|
|
-p ${Cooking_INGREDIENTS_DIR}
|
|
-g ${CMAKE_GENERATOR}
|
|
-x
|
|
${restrictions_args}
|
|
--
|
|
${pa_COOKING_CMAKE_ARGS})
|
|
elseif (NOT (CONFIGURE_COMMAND IN_LIST ${pa_EXTERNAL_PROJECT_ARGS_LIST}))
|
|
set (value
|
|
CONFIGURE_COMMAND
|
|
${CMAKE_COMMAND}
|
|
${pa_CMAKE_ARGS}
|
|
<SOURCE_DIR>)
|
|
else ()
|
|
set (value "")
|
|
endif ()
|
|
|
|
set (_cooking_ep_configure_command ${value} PARENT_SCOPE)
|
|
endfunction ()
|
|
|
|
function (_cooking_populate_ep_build_command ep_args_list)
|
|
if (NOT (BUILD_COMMAND IN_LIST ${ep_args_list}))
|
|
set (value BUILD_COMMAND ${CMAKE_COMMAND} --build <BINARY_DIR>)
|
|
else ()
|
|
set (value "")
|
|
endif ()
|
|
|
|
set (_cooking_ep_build_command ${value} PARENT_SCOPE)
|
|
endfunction ()
|
|
|
|
function (_cooking_populate_ep_install_command ep_args_list)
|
|
if (NOT (INSTALL_COMMAND IN_LIST ${ep_args_list}))
|
|
set (value INSTALL_COMMAND ${CMAKE_COMMAND} --build <BINARY_DIR> --target install)
|
|
else ()
|
|
set (value "")
|
|
endif ()
|
|
|
|
set (_cooking_ep_install_command ${value} PARENT_SCOPE)
|
|
endfunction ()
|
|
|
|
function (_cooking_define_ep)
|
|
cmake_parse_arguments (
|
|
pa
|
|
""
|
|
"NAME;SOURCE_DIR;BINARY_DIR;EXTERNAL_PROJECT_ARGS_LIST;RECIPE;INGREDIENT_DIR;STOW_DIR;LOCAL_RECONFIGURE;LOCAL_REBUILD"
|
|
"DEPENDS;CONFIGURE_COMMAND;BUILD_COMMAND;INSTALL_COMMAND;CMAKE_ARGS"
|
|
${ARGN})
|
|
|
|
string (REPLACE "<DISABLE>" "" forwarded_ep_args "${${pa_EXTERNAL_PROJECT_ARGS_LIST}}")
|
|
set (ep_name ingredient_${pa_NAME})
|
|
include (ExternalProject)
|
|
|
|
set (stamp_dir ${pa_INGREDIENT_DIR}/stamp)
|
|
|
|
ExternalProject_add (${ep_name}
|
|
DEPENDS ${pa_DEPENDS}
|
|
SOURCE_DIR ${pa_SOURCE_DIR}
|
|
BINARY_DIR ${pa_BINARY_DIR}
|
|
CONFIGURE_COMMAND ${pa_CONFIGURE_COMMAND}
|
|
BUILD_COMMAND ${pa_BUILD_COMMAND}
|
|
INSTALL_COMMAND ${pa_INSTALL_COMMAND}
|
|
PREFIX ${pa_INGREDIENT_DIR}
|
|
STAMP_DIR ${stamp_dir}
|
|
INSTALL_DIR ${pa_STOW_DIR}/${pa_NAME}
|
|
CMAKE_ARGS ${pa_CMAKE_ARGS}
|
|
LIST_SEPARATOR :::
|
|
STEP_TARGETS install
|
|
"${forwarded_ep_args}")
|
|
|
|
set (stow_marker_file ${Cooking_INGREDIENTS_DIR}/.cooking_ingredient_${pa_NAME})
|
|
set (lock_file ${Cooking_INGREDIENTS_DIR}/.cooking_stow.lock)
|
|
|
|
add_custom_command (
|
|
OUTPUT ${stow_marker_file}
|
|
DEPENDS
|
|
${ep_name}-install
|
|
${stamp_dir}/ingredient_${pa_NAME}-install
|
|
COMMAND
|
|
flock
|
|
--wait 30
|
|
${lock_file}
|
|
${Cooking_STOW_EXECUTABLE}
|
|
-t ${Cooking_INGREDIENTS_DIR}
|
|
-d ${pa_STOW_DIR}
|
|
${pa_NAME}
|
|
COMMAND ${CMAKE_COMMAND} -E touch ${stow_marker_file})
|
|
|
|
add_custom_target (_cooking_ingredient_${pa_NAME}_stowed
|
|
DEPENDS ${stow_marker_file})
|
|
|
|
if (pa_RECIPE)
|
|
set (reconfigure_marker_file ${Cooking_INGREDIENTS_DIR}/.cooking_reconfigure_ingredient_${pa_NAME})
|
|
|
|
add_custom_command (
|
|
OUTPUT ${reconfigure_marker_file}
|
|
COMMAND ${CMAKE_COMMAND} -E touch ${reconfigure_marker_file})
|
|
|
|
add_custom_target (_cooking_ingredient_${pa_NAME}_marked_for_reconfigure
|
|
DEPENDS ${reconfigure_marker_file})
|
|
|
|
ExternalProject_add_step (${ep_name}
|
|
cooking-reconfigure
|
|
DEPENDERS configure
|
|
DEPENDS ${reconfigure_marker_file}
|
|
COMMAND ${CMAKE_COMMAND} -E echo_append)
|
|
|
|
ExternalProject_add_stepdependencies (${ep_name}
|
|
cooking-reconfigure
|
|
_cooking_ingredient_${pa_NAME}_marked_for_reconfigure)
|
|
endif ()
|
|
|
|
foreach (d ${pa_DEPENDS})
|
|
ExternalProject_add_stepdependencies (${ep_name}
|
|
configure
|
|
_cooking_${d}_stowed)
|
|
endforeach ()
|
|
|
|
add_dependencies (_cooking_ingredients _cooking_ingredient_${pa_NAME}_stowed)
|
|
|
|
if (pa_LOCAL_RECONFIGURE OR pa_LOCAL_REBUILD)
|
|
if (pa_LOCAL_RECONFIGURE)
|
|
set (step configure)
|
|
else ()
|
|
set (step build)
|
|
endif ()
|
|
|
|
ExternalProject_add_step (${ep_name}
|
|
cooking-local-${step}
|
|
DEPENDERS ${step}
|
|
DEPENDS ${_cooking_local_synchronize_marker_file}
|
|
COMMAND ${CMAKE_COMMAND} -E echo_append)
|
|
|
|
ExternalProject_add_stepdependencies (${ep_name}
|
|
cooking-local-${step}
|
|
_cooking_marked_for_local_synchronization)
|
|
endif ()
|
|
endfunction ()
|
|
|
|
macro (cooking_ingredient name)
|
|
set (_cooking_args "${ARGN}")
|
|
|
|
if ((_cooking_is_excluding AND (${name} IN_LIST Cooking_EXCLUDED_INGREDIENTS))
|
|
OR (_cooking_is_including AND (NOT (${name} IN_LIST Cooking_INCLUDED_INGREDIENTS))))
|
|
# Nothing.
|
|
else ()
|
|
set (_cooking_ingredient_dir ${_cooking_dir}/ingredient/${name})
|
|
|
|
cmake_parse_arguments (
|
|
_cooking_pa
|
|
"LOCAL_RECONFIGURE;LOCAL_REBUILD"
|
|
"COOKING_RECIPE"
|
|
"CMAKE_ARGS;COOKING_CMAKE_ARGS;EXTERNAL_PROJECT_ARGS;REQUIRES"
|
|
${_cooking_args})
|
|
|
|
_cooking_populate_ep_parameter (
|
|
EXTERNAL_PROJECT_ARGS_LIST _cooking_pa_EXTERNAL_PROJECT_ARGS
|
|
PARAMETER SOURCE_DIR
|
|
DEFAULT_VALUE ${_cooking_ingredient_dir}/src)
|
|
|
|
_cooking_populate_ep_parameter (
|
|
EXTERNAL_PROJECT_ARGS_LIST _cooking_pa_EXTERNAL_PROJECT_ARGS
|
|
PARAMETER BINARY_DIR
|
|
DEFAULT_VALUE ${_cooking_ingredient_dir}/build)
|
|
|
|
_cooking_populate_ep_parameter (
|
|
EXTERNAL_PROJECT_ARGS_LIST _cooking_pa_EXTERNAL_PROJECT_ARGS
|
|
PARAMETER BUILD_IN_SOURCE
|
|
DEFAULT_VALUE OFF)
|
|
|
|
if (_cooking_build_in_source)
|
|
set (_cooking_ep_binary_dir "")
|
|
endif ()
|
|
|
|
if (Cooking_LIST_ONLY)
|
|
_cooking_define_listing_targets (
|
|
NAME ${name}
|
|
SOURCE_DIR ${_cooking_source_dir}
|
|
RECIPE ${_cooking_pa_COOKING_RECIPE}
|
|
REQUIRES ${_cooking_pa_REQUIRES})
|
|
else ()
|
|
_cooking_adjust_requirements (
|
|
IS_EXCLUDING ${_cooking_is_excluding}
|
|
IS_INCLUDING ${_cooking_is_including}
|
|
REQUIREMENTS ${_cooking_pa_REQUIRES}
|
|
OUTPUT_LIST _cooking_pa_REQUIRES)
|
|
|
|
_cooking_populate_ep_depends (
|
|
REQUIREMENTS ${_cooking_pa_REQUIRES})
|
|
|
|
_cooking_determine_common_cmake_args (_cooking_common_cmake_args)
|
|
|
|
_cooking_populate_ep_configure_command (
|
|
IS_EXCLUDING ${_cooking_is_excluding}
|
|
IS_INCLUDING ${_cooking_is_including}
|
|
RECIPE ${_cooking_pa_COOKING_RECIPE}
|
|
REQUIREMENTS ${_cooking_pa_REQUIRES}
|
|
EXTERNAL_PROJECT_ARGS_LIST _cooking_pa_EXTERNAL_PROJECT_ARGS
|
|
CMAKE_ARGS
|
|
${_cooking_common_cmake_args}
|
|
${_cooking_pa_CMAKE_ARGS}
|
|
COOKING_CMAKE_ARGS
|
|
${_cooking_common_cmake_args}
|
|
${_cooking_pa_COOKING_CMAKE_ARGS})
|
|
|
|
_cooking_populate_ep_build_command (_cooking_pa_EXTERNAL_PROJECT_ARGS)
|
|
_cooking_populate_ep_install_command (_cooking_pa_EXTERNAL_PROJECT_ARGS)
|
|
|
|
_cooking_define_ep (
|
|
NAME ${name}
|
|
RECIPE ${_cooking_pa_COOKING_RECIPE}
|
|
DEPENDS ${_cooking_ep_depends}
|
|
SOURCE_DIR ${_cooking_ep_source_dir}
|
|
BINARY_DIR ${_cooking_ep_binary_dir}
|
|
CONFIGURE_COMMAND ${_cooking_ep_configure_command}
|
|
BUILD_COMMAND ${_cooking_ep_build_command}
|
|
INSTALL_COMMAND ${_cooking_ep_install_command}
|
|
INGREDIENT_DIR ${_cooking_ingredient_dir}
|
|
STOW_DIR ${_cooking_dir}/stow
|
|
CMAKE_ARGS ${_cooking_common_cmake_args}
|
|
EXTERNAL_PROJECT_ARGS_LIST _cooking_pa_EXTERNAL_PROJECT_ARGS
|
|
LOCAL_RECONFIGURE ${_cooking_pa_LOCAL_RECONFIGURE}
|
|
LOCAL_REBUILD ${_cooking_pa_LOCAL_REBUILD})
|
|
endif ()
|
|
endif ()
|
|
endmacro ()
|
|
EOF
|
|
|
|
cmake_cooking_args=(
|
|
"-DCooking_INGREDIENTS_DIR=${ingredients_dir}"
|
|
"-DCooking_RECIPE=${recipe}"
|
|
)
|
|
|
|
#
|
|
# Remove any `Cooking.cmake` file from the source directory. We now generate this file in the build directory, and old
|
|
# copies will cause conflicts.
|
|
#
|
|
|
|
old_cooking_file="${source_dir}/cmake/Cooking.cmake"
|
|
|
|
if [ -f "${old_cooking_file}" ]; then
|
|
grep 'This file was generated by cmake-cooking' "${old_cooking_file}" > /dev/null && rm "${old_cooking_file}"
|
|
fi
|
|
|
|
#
|
|
# Clean-up from a previous run.
|
|
#
|
|
|
|
if [ -e "${ingredients_ready_file}" ]; then
|
|
rm "${ingredients_ready_file}"
|
|
fi
|
|
|
|
if [ -e "${cache_file}" ]; then
|
|
rm "${cache_file}"
|
|
fi
|
|
|
|
if [ -d "${ingredients_dir}" -a -z "${nested}" ]; then
|
|
rm -r --preserve-root "${ingredients_dir}"
|
|
fi
|
|
|
|
mkdir -p "${ingredients_dir}"
|
|
|
|
#
|
|
# Validate recipe.
|
|
#
|
|
|
|
if [ -n "${recipe}" ]; then
|
|
if [ ! -f "${recipe}" ]; then
|
|
echo "Cooking: The '${recipe}' recipe does not exist!" >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
#
|
|
# Prepare lists of included and excluded ingredients.
|
|
#
|
|
|
|
if [ -n "${excluded_ingredients}" ] && [ -z "${list_only}" ]; then
|
|
cmake_cooking_args+=(
|
|
-DCooking_EXCLUDED_INGREDIENTS=$(printf "%s;" "${excluded_ingredients[@]}")
|
|
-DCooking_INCLUDED_INGREDIENTS=
|
|
)
|
|
fi
|
|
|
|
if [ -n "${included_ingredients}" ] && [ -z "${list_only}" ]; then
|
|
cmake_cooking_args+=(
|
|
-DCooking_EXCLUDED_INGREDIENTS=
|
|
-DCooking_INCLUDED_INGREDIENTS=$(printf "%s;" "${included_ingredients[@]}")
|
|
)
|
|
fi
|
|
|
|
#
|
|
# Configure and build ingredients.
|
|
#
|
|
|
|
mkdir -p "${cooking_dir}"/stow
|
|
touch "${cooking_dir}"/stow/.stow
|
|
cd "${build_dir}"
|
|
|
|
declare -a build_args
|
|
|
|
if [ "${generator}" == "Ninja" ]; then
|
|
build_args+=(-v)
|
|
fi
|
|
|
|
if [ -n "${list_only}" ]; then
|
|
cmake_cooking_args+=("-DCooking_LIST_ONLY=ON")
|
|
fi
|
|
|
|
${CMAKE} -DCMAKE_BUILD_TYPE="${build_type}" "${cmake_cooking_args[@]}" -G "${generator}" "${source_dir}" "${@}"
|
|
|
|
if [ -n "${recipe}" ]; then
|
|
${CMAKE} --build . --target _cooking_ingredients_ready -- "${build_args[@]}"
|
|
|
|
#
|
|
# Report what we've done (if we're not nested).
|
|
#
|
|
|
|
if [ -z "${nested}" ]; then
|
|
ingredients=($(find "${ingredients_dir}" -name '.cooking_ingredient_*' -printf '%f\n' | sed -r 's/\.cooking_ingredient_(.+)/\1/'))
|
|
|
|
if [ -z "${list_only}" ]; then
|
|
printf "\nCooking: Installed the following ingredients:\n"
|
|
else
|
|
printf "\nCooking: The following ingredients are necessary for this recipe:\n"
|
|
fi
|
|
|
|
for ingredient in "${ingredients[@]}"; do
|
|
echo " - ${ingredient}"
|
|
done
|
|
|
|
printf '\n'
|
|
fi
|
|
|
|
if [ -n "${list_only}" ]; then
|
|
exit 0
|
|
fi
|
|
|
|
#
|
|
# Configure the project, expecting all requirements satisfied.
|
|
#
|
|
|
|
${CMAKE} -DCMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY=ON "${@}" .
|
|
|
|
#
|
|
# Optionally export the installed files.
|
|
#
|
|
|
|
if [ -n "${export_dir}" ]; then
|
|
rsync "${ingredients_dir}/" "${export_dir}" -a --copy-links
|
|
printf "\nCooking: Exported ingredients to ${export_dir}\n"
|
|
fi
|
|
fi
|
|
|
|
#
|
|
# Save invocation information.
|
|
#
|
|
|
|
cd "${initial_wd}"
|
|
|
|
cat <<EOF > "${memory_file}"
|
|
run_previous() {
|
|
"${0}" ${invoked_args[@]@Q}
|
|
}
|
|
EOF
|