Skip to content

Commit 5962441

Browse files
ksperling-applepull[bot]
authored andcommitted
Improve configure script so ninja can be run without a wrapper (#26379)
Improve configure script so ninja can be run without a wrapper This is done by using gn --script-executable to make Python scripts / actions use, and having the build-venv auto-activate for the current Python process when used in this way. Plus some minor improvements: - Remove special casing of the build environment directory for in-tree builds and be more helpful when running configure without arguments. - Handle VAR=VALUE arguments as environment variable assignments - Guess CXX based on whether cc appears to be gcc or clang when CC and CXX are not provided.
1 parent 8dd2dfa commit 5962441

File tree

3 files changed

+141
-71
lines changed

3 files changed

+141
-71
lines changed

scripts/configure

+129-71
Original file line numberDiff line numberDiff line change
@@ -33,53 +33,59 @@
3333
# the build directory, but an external directory can be specified using the
3434
# --build-env-dir option. The build environment directory can be shared by any
3535
# number of build directories, independently of target / tool chain.
36-
#
37-
# Project options can be passed in the usual GNU configure style (--enable-foo,
38-
# --foo-bar=value) and are translated into GN build arguments. By default,
39-
# configure will override the toolchain for the GN build using a 'custom'
40-
# toolchain assembled from the usual environment variables (CC, CXX, AR, CFLAGS,
41-
# CXXFLAGS, ...).
4236

43-
function usage() {
37+
set -o pipefail
38+
shopt -s extglob
39+
40+
function usage() { # status
4441
info "Usage: $0 [OPTIONS] [--project=... [PROJECT OPTIONS]]"
4542
info "Options:"
46-
info " --build-env-dir=DIR Directory to create (host) build environment in"
47-
info " --project=DIR Sub-directory to build, e.g. examples/lighting-app/linux"
48-
exit 0
43+
info " --build-env-dir=DIR Directory to create (host) build environment in"
44+
info " --project=DIR Sub-directory to build, eg examples/lighting-app/linux"
45+
info ""
46+
info "Project options (mapped to GN build args):"
47+
info " --enable-<ARG>[=no] Enables (or disables with '=no') a bool build arg"
48+
info " --<ARG>=<VALUE> Sets a (non-bool) build arg to the given value"
49+
info " GN argument names can be specified with '-' instead of '_' and prefixes"
50+
info " like 'chip_' can be ommitted from names. For the full list of available"
51+
info " build arguments, see the generated args.configured file."
52+
info ""
53+
info " By default, the toolchain for the GN build will be configured from the usual"
54+
info " environment variables (CC, CXX, AR, CFLAGS, CXXFLAGS, ...), falling back to"
55+
info " default tool names (CC=cc, ...). When using this script within an external"
56+
info " build system, toolchain environment variables should be populated."
57+
exit "$1"
4958
}
5059

5160
function main() { # ...
52-
set -o pipefail
5361
CHIP_ROOT=$(cd "$(dirname "$0")/.." && pwd)
5462
BUILD_ENV_DEPS=(
5563
"${CHIP_ROOT}/scripts/setup/requirements.build.txt"
5664
"${CHIP_ROOT}/scripts/setup/constraints.txt"
5765
"${CHIP_ROOT}/scripts/setup/zap.version"
5866
)
5967

60-
if [[ "$PWD" == "$CHIP_ROOT" ]]; then
61-
BUILD_DIR="out/configured"
62-
BUILD_ROOT="${CHIP_ROOT}/${BUILD_DIR}"
63-
BUILD_ENV_DIR=".venv"
64-
info "Configuring in-tree, will build in $BUILD_DIR using environment $BUILD_ENV_DIR"
65-
else
66-
BUILD_DIR="."
67-
BUILD_ROOT="$PWD"
68-
BUILD_ENV_DIR="build-env"
69-
fi
68+
# Parse global options, process VAR=VALUE style arguments, and collect project options
69+
BUILD_ENV_DIR=
7070
PROJECT=
71-
72-
# Parse main options but leave project options in $@
73-
while [[ $# -gt 0 && -z "$PROJECT" ]]; do
71+
PROJECT_ARGS=()
72+
while [[ $# -gt 0 ]]; do
7473
case "$1" in
75-
--help) usage ;;
74+
--help) usage 0 ;;
7675
--build-env-dir=*) BUILD_ENV_DIR="${1#*=}" ;;
7776
--project=*) PROJECT="${1#*=}" ;;
78-
*) fail "Invalid argument: '$1'" ;;
77+
+([A-Z_])=*) export "$1" ;;
78+
*)
79+
[[ -n "$PROJECT" ]] || fail "Invalid argument: '$1'"
80+
PROJECT_ARGS+=("$1")
81+
;;
7982
esac
8083
shift
8184
done
8285

86+
# Ensure we have something to do
87+
[[ -n "$PROJECT" || -n "$BUILD_ENV_DIR" ]] || usage 1
88+
8389
if [[ -n "$PROJECT" ]]; then
8490
local subdir="$(cd "${CHIP_ROOT}/${PROJECT}" 2>/dev/null && pwd)"
8591
[[ -n "$subdir" && -r "${subdir}/.gn" ]] || fail "Invalid project '${PROJECT}'"
@@ -89,7 +95,28 @@ function main() { # ...
8995

9096
check_binary gn GN
9197
check_binary ninja NINJA
92-
if ! activate_build_env; then
98+
99+
# Work out build and environment directories
100+
if [[ "$PWD" == "$CHIP_ROOT" ]]; then
101+
BUILD_DIR="out/configured"
102+
NINJA_HINT="ninja -C ${BUILD_DIR}"
103+
else
104+
BUILD_DIR="."
105+
NINJA_HINT="ninja"
106+
fi
107+
108+
if [[ -n "$BUILD_ENV_DIR" ]]; then
109+
mkdir -p "$BUILD_ENV_DIR"
110+
BUILD_ENV_PATH="$(cd "$BUILD_ENV_DIR" && pwd)"
111+
[[ -n "$BUILD_ENV_PATH" ]] || fail "Invalid build-env-dir '${BUILD_ENV_DIR}'"
112+
BUILD_ENV_DIR="$BUILD_ENV_PATH" # absolute
113+
else
114+
BUILD_ENV_DIR="build-env" # relative to BUILD_DIR
115+
BUILD_ENV_PATH="${BUILD_DIR}/${BUILD_ENV_DIR}"
116+
fi
117+
118+
# Create the build environment if necessary
119+
if ! check_build_env; then
93120
check_python
94121
configure_python_env
95122
if ! check_binary zap-cli; then
@@ -98,15 +125,19 @@ function main() { # ...
98125
finalize_build_env
99126
fi
100127

128+
# Configure the project (if requested)
101129
if [[ -z "$PROJECT" ]]; then
102130
info "Build environment created. (Specify --project=DIR to configure a build.)"
103131
return
104132
fi
105133

134+
[[ "$BUILD_DIR" != "." ]] && info "Configuring in-tree, will build in ${BUILD_DIR}"
135+
106136
create_empty_pw_env
107-
gn_generate "$@"
137+
guess_toolchain
138+
gn_generate "${PROJECT_ARGS[@]}"
108139
create_ninja_wrapper
109-
info "You can now run ./ninja-build"
140+
info "You can now run ./ninja-build (or $NINJA_HINT)"
110141
}
111142

112143
function create_empty_pw_env() {
@@ -123,61 +154,90 @@ function create_empty_pw_env() {
123154
fi
124155
}
125156

157+
function guess_toolchain() {
158+
# There is no widely used standard command for the C++ compiler (analogous to
159+
# `cc` for the C compiler), so if neither CC nor CXX are defined try to guess.
160+
if [[ -z "$CC" && -z "$CXX" ]] && have_binary cc; then
161+
local probe="$(cc -E - <<<'gnu=__GNUC__ clang=__clang__' 2>/dev/null)"
162+
# Check for clang first because it also defines __GNUC__
163+
if [[ "$probe" =~ clang=[1-9] ]] && have_binary clang && have_binary clang++; then
164+
info "Guessing CC=clang CXX=clang++ because cc appears to be clang"
165+
export CC=clang CXX=clang++
166+
elif [[ "$probe" =~ gnu=[1-9] ]] && have_binary gcc && have_binary g++; then
167+
info "Guessing CC=gcc CXX=g++ because cc appears to be gcc"
168+
export CC=gcc CXX=g++
169+
else
170+
info "Unable to guess c++ compiler: $probe"
171+
fi
172+
fi
173+
}
174+
126175
function gn_generate() { # [project options]
127-
mkdir -p "${BUILD_ROOT}"
128-
ensure_no_clobber "${BUILD_ROOT}/args.gn"
129-
(
130-
cd "${CHIP_ROOT}/${PROJECT}" # --root= doesn't work for gn args!
131-
132-
# Run gn gen with an empty args.gn first so we can list all arguments
133-
info "Configuring gn build arguments (see $BUILD_DIR/args.configured for full list)"
134-
echo "# ${CONFIGURE_MARKER}" >"${BUILD_ROOT}/args.gn"
135-
gn -q gen "$BUILD_ROOT"
136-
137-
# Use the argument list to drive the mapping of our command line options to GN args
138-
call_impl process_project_args <(gn args "$BUILD_ROOT" --list --json) "$@" >>"${BUILD_ROOT}/args.gn"
139-
gn args "$BUILD_ROOT" --list >"${BUILD_ROOT}/args.configured"
140-
141-
# Now gn gen with the arguments we have configured.
142-
info "Running gn gen to generate ninja files"
143-
gn -q gen "$BUILD_ROOT"
144-
)
176+
mkdir -p "${BUILD_DIR}"
177+
ensure_no_clobber "${BUILD_DIR}/args.gn"
178+
179+
# Pass --script-executable to all `gn` calls so scripts run in our venv
180+
local gn=(gn --script-executable="${BUILD_ENV_DIR}/bin/python" --root="${CHIP_ROOT}/${PROJECT}")
181+
182+
# Run gn gen with an empty args.gn first so we can list all arguments
183+
info "Configuring gn build arguments (see $BUILD_DIR/args.configured for full list)"
184+
{
185+
echo "# ${CONFIGURE_MARKER}"
186+
echo "# project root: ${PROJECT}"
187+
} >"${BUILD_DIR}/args.gn"
188+
"${gn[@]}" -q gen "$BUILD_DIR"
189+
190+
# Use the argument list to drive the mapping of our command line options to GN args
191+
call_impl process_project_args <("${gn[@]}" args "$BUILD_DIR" --list --json) "$@" >>"${BUILD_DIR}/args.gn"
192+
"${gn[@]}" args "$BUILD_DIR" --list >"${BUILD_DIR}/args.configured"
193+
194+
# Now gn gen with the arguments we have configured.
195+
info "Running gn gen to generate ninja files"
196+
"${gn[@]}" -q gen "$BUILD_DIR"
145197
}
146198

147199
function create_ninja_wrapper() {
148-
# Note: "." != $BUILD_DIR for in-tree builds
149200
local wrapper="ninja-build"
150201
ensure_no_clobber "$wrapper"
151-
cat >"$wrapper" <<END
152-
#!/bin/bash -e
153-
# ${CONFIGURE_MARKER}
154-
cd "\$(dirname "\$0")"
155-
source "${BUILD_ENV_DIR}/bin/activate"
156-
exec "${NINJA}" -C "${BUILD_DIR}" "\$@"
157-
END
202+
{
203+
echo "#!/bin/bash -e"
204+
echo "# ${CONFIGURE_MARKER}"
205+
if [[ "$BUILD_DIR" != "." ]]; then
206+
echo 'args=(-C "$(dirname "$0")/'"${BUILD_DIR}"'")'
207+
else
208+
echo 'args=() dir="$(dirname "$0")"'
209+
echo '[[ "$dir" != "." ]] && args=(-C "$dir")'
210+
fi
211+
echo 'exec ninja "${args[@]}" "$@"'
212+
} >"$wrapper"
158213
chmod a+x "$wrapper"
159214
}
160215

161-
function activate_build_env() {
216+
function check_build_env() {
162217
generate_build_env_cksum # re-used by finalize_build_env
163-
[[ -r "${BUILD_ENV_DIR}/.cksum" ]] || return 1
164-
read -r <"${BUILD_ENV_DIR}/.cksum" || true
218+
[[ -r "${BUILD_ENV_PATH}/.cksum" ]] || return 1
219+
read -r <"${BUILD_ENV_PATH}/.cksum" || true
165220
[[ "$REPLY" == "$CURRENT_ENV_CKSUM" ]] || return 1
166221

167-
[[ -r "${BUILD_ENV_DIR}/bin/activate" ]] || return 1
168-
info "Using existing build environment: ${BUILD_ENV_DIR}"
169-
source "${BUILD_ENV_DIR}/bin/activate"
170-
PYTHON="python"
222+
[[ -r "${BUILD_ENV_PATH}/bin/activate" ]] || return 1
223+
info "Using existing build environment: ${BUILD_ENV_PATH}"
224+
PYTHON="${BUILD_ENV_PATH}/bin/python"
171225
}
172226

173227
function configure_python_env() {
174228
progress "Setting up Python venv"
175-
"$PYTHON" -m venv "$BUILD_ENV_DIR"
176-
info "ok"
229+
"$PYTHON" -m venv --clear "$BUILD_ENV_PATH"
230+
info "$BUILD_ENV_PATH"
231+
232+
# Install our auto-loading venvactivate module so that running scripts via
233+
# the venv python has the side-effect of fully activating the environment.
234+
local sitepkgs=("${BUILD_ENV_PATH}/lib/python"*"/site-packages")
235+
[[ -d "$sitepkgs" ]] || fail "Failed to locate venv site-packages"
236+
cp "${CHIP_ROOT}/scripts/configure.venv/venvactivate".{pth,py} "${sitepkgs}/"
177237

178238
progress "Installing Python build dependencies"
179-
"${BUILD_ENV_DIR}/bin/pip" install --require-virtualenv --quiet --upgrade pip wheel
180-
"${BUILD_ENV_DIR}/bin/pip" install --require-virtualenv --quiet \
239+
"${BUILD_ENV_PATH}/bin/pip" install --require-virtualenv --quiet --upgrade pip wheel
240+
"${BUILD_ENV_PATH}/bin/pip" install --require-virtualenv --quiet \
181241
-r "${CHIP_ROOT}/scripts/setup/requirements.build.txt" \
182242
-c "${CHIP_ROOT}/scripts/setup/constraints.txt"
183243
info "ok"
@@ -190,9 +250,7 @@ function generate_build_env_cksum() {
190250
}
191251

192252
function finalize_build_env() {
193-
echo "$CURRENT_ENV_CKSUM" >"${BUILD_ENV_DIR}/.cksum"
194-
source "${BUILD_ENV_DIR}/bin/activate"
195-
PYTHON="python"
253+
echo "$CURRENT_ENV_CKSUM" >"${BUILD_ENV_PATH}/.cksum"
196254
}
197255

198256
function download_zap() {
@@ -206,8 +264,8 @@ function download_zap() {
206264
local url="https://github.com/project-chip/zap/releases/download/${version}/zap-${platform}.zip"
207265

208266
progress "Installing zap-cli from $url"
209-
call_impl download_and_extract_zip "$url" "${BUILD_ENV_DIR}/bin" zap-cli
210-
chmod a+x "${BUILD_ENV_DIR}/bin/zap-cli" # ZipFile.extract() does not handle permissions
267+
call_impl download_and_extract_zip "$url" "${BUILD_ENV_PATH}/bin" zap-cli
268+
chmod a+x "${BUILD_ENV_PATH}/bin/zap-cli" # ZipFile.extract() does not handle permissions
211269
info "ok"
212270
}
213271

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import venvactivate
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Activates the current venv as if the activate script had been sourced
2+
import collections
3+
import os
4+
import sys
5+
6+
# Prepend the venv bin to PATH (without introducing duplicate entries)
7+
path = [os.path.join(sys.prefix, 'bin')] + os.environ['PATH'].split(':')
8+
os.environ['PATH'] = ':'.join(collections.OrderedDict.fromkeys(path).keys())
9+
10+
# Set VIRTUAL_ENV to the venv directory
11+
os.environ['VIRTUAL_ENV'] = sys.prefix

0 commit comments

Comments
 (0)