blob: afd0be003e078717838a6ef3ea7d51f6baf59deb [file] [log] [blame]
#!/bin/bash
# Copyright 2017 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#### CATEGORY=Build
### Run Ninja to build Fuchsia
## usage: fx build [options] [LABELS_OR_TARGETS]... [-- NINJA_OPTIONS...]
##
## Builds a set of GN or Ninja targets after performing a few correctness checks for
## the Fuchsia build. Ninja-specific options must be provided after a -- specifier.
##
## If `FX_BUILD_WITH_LABELS=1` is set in the environment, LABELS_OR_TARGETS is a
## list of Ninja target path, GN labels (beginning with //) or `--toolchain=NAME`
## options or related aliases (see below). For example, the following lines
## are equivalent ways to ask for the host `zbi` tool to be built:
##
## fx build --host //zircon/tools/zbi
## fx build --host //zircon/tools/zbi:zbi
## fx build --toolchain=//build/toolchain:host_x64 //zircon/tools/zbi
## fx build '//zircon/tools/zbi(//build/toolchain:host_x64)'
## fx build host_x64/zbi
##
## The first line above is favored. Using the last line will print a warning
## instructing the user which (optional) toolchain option + label to use instead.
##
## If `FX_BUILD_WITH_LABELS=1` is *not* set, then LABELS_OR_TARGETS must be
## a list of Ninja target paths only.
##
## optional arguments:
## -h, --help Print this message. Use '-- --help' to print
## Ninja-specific help.
## --no-checks Disable consistency checks (for fx development only).
## --log LOGFILE Print debug information to LOGFILE. Please attach
## the resulting file when reporting bugs.
## --fint-params-path PATH Path to a fint params file used by an infra
## builder. This is useful for reproducing the exact
## set of targets built by the infrastructure.
##
## --toolchain=TOOLCHAIN Specify the current GN toolchain suffix to use for
## GN labels that appear after this option.
##
## --host Alias for --toolchain=host
## --default Alias for --toolchain=default
## --fuchsia Alias for --toolchain=fuchsia
## --fidl Alias for --toolchain=fidl
##
## --help-toolchains Print list of valid TOOLCHAIN values.
##
## Run `fx build -h` to see Ninja argument details.
# Note: Do not use $(dirname "${BASH_SOURCE[0]}") to locate the script's
# directory to save about 7ms of startup time. See https://fxbug.dev/42085680
# The fallback is only needed when invoking directly with bash as in:
# `(cd tools/devshell && bash build)`
_script_dir="${BASH_SOURCE[0]%/*}"; if [[ "${_script_dir}" == "${BASH_SOURCE[0]}" ]]; then _script_dir=.; fi
# shellcheck source=/dev/null
source "${_script_dir}/lib/vars.sh" || exit 1
# shellcheck source=/dev/null
source "${_script_dir}/lib/build_api_client.sh" || exit $?
fx-config-read
shopt -s extglob
function print-toolchain-help {
cat <<EOF
Possible --toolchain aliases
host Host toolchain (//build/toolchain:host_$HOST_CPU)
default Default toolchain (//build/toolchain/fuchsia:\$TARGET_CPU)
fuchsia Default toolchain as well.
fidl FIDL toolchain (//build/fidl:fidling)
Apart from that, the --toolchain value must be a valid GN toolchain
label, such as '//some/toolchain:target'.
EOF
}
# command-stdout-to-array runs a command and stores its standard output
# into an array (expecting one item per line, preserving spaces).
# $1: variable name
# $2+: command arguments.
if [[ "${BASH_VERSION}" =~ ^3 ]]; then
# MacOS comes with Bash 3.x which doesn't have readarray at all
# so read one line at a time.
function command-stdout-to-array {
local varname="$1"
local line output
shift
output="$("$@")"
while IFS= read -r line; do
eval "${varname}+=(\"${line}\")"
done <<< "${output}"
}
else
function command-stdout-to-array {
local varname="$1"
shift
readarray -t "${varname}" <<< "$("$@")"
}
fi
function main {
local switches=() fuchsia_targets=() ninja_switches=()
local log_file is_logging fint_params_path
local has_ninja_options no_checks
is_logging=false
while [[ $# -gt 0 ]]; do
case "$1" in
--log)
switches+=("$1")
if [[ $# -lt 2 ]]; then
fx-command-help
return 1
fi
log_file="$2"
if [[ -f "${log_file}" ]]; then
fx-error "File \"${log_file}\" exists."
return 1
fi
# if ! touch "${log_file}"; then
# fx-error "Cannot create logfile \"${log_file}\""
# return 1
# fi
is_logging=true
shift
;;
--fint-params-path)
switches+=("$1")
if [[ $# -lt 2 ]]; then
fx-command-help
return 1
fi
fint_params_path="$2"
shift
;;
# Use -- as a separator for command-line options for Ninja.
# Anything that follows that goes directly into ninja_switches and the loop exits.
--)
shift
ninja_switches+=("$@")
break
;;
--no-checks)
no_checks=true
;;
# Recognize Ninja options and add them to ninja_switches directly instead of switches.
# Set has_ninja_options to true to indicate that Ninja-specific options were used
# before -- on the command-line.
--version|-v|--verbose|--dry-run|--quiet|-n)
has_ninja_options=true
ninja_switches+=("$1")
;;
# These Ninja options take an argument, with or without a space separator.
-[Cfjkldw])
has_ninja_options=true
ninja_switches+=("$1" "$2")
shift
;;
-[Cfjkldw]*)
has_ninja_options=true
ninja_switches+=("$1")
;;
-h|--help)
fx-print-command-help "${BASH_SOURCE[0]}"
exit 1
;;
--help-toolchains)
print-toolchain-help
exit 1
;;
# The following options are used to specify GN toolchain for future
# labels that appear on the command line.
--host|--fidl|--default|--fuchsia|--toolchain=*)
fuchsia_targets+=("$1")
;;
# Anything that follows the Ninja tool name is passed to the tool itself
# and not processed by Ninja itself, so stop the loop right here. If
# there were fuchsia targets specificied before that, they will be
# ignored.
#
# ninja -t <tool> --help
# ninja -t<tool> --help
#
-t*)
has_ninja_options=true
ninja_switches+=("$@")
if [[ "${#fuchsia_targets[@]}" -gt 0 ]]; then
fx-warn "Fuchsia targets before a Ninja tool name are ignored!"
fuchsia_targets=()
fi
break
;;
-*)
fx-error "Unsupported option $1, see 'fx help build' for details."
exit 1
;;
*)
fuchsia_targets+=("$1")
;;
esac
shift
done
# Set no_checks=true when running a Ninja tool or printing the Ninja help.
for arg in "${ninja_switches[@]}"; do
case "${arg}" in
-h|--help|-t*)
no_checks=true
break
esac
done
if [ -n "${has_ninja_options}" ]; then
fx-warn "Use -- to pass Ninja-specific options, as in:"
fx-warn " fx build ${switches[*]} -- ${ninja_switches[*]}"
fx-warn "This warning will soon become an error."
fi
if [[ "$(uname)" == [Dd]arwin ]]; then
fx-warn "Support for building Fuchsia on macOS will be removed by 2024-06-30."
fx-warn "This warning will soon become an error."
fx-warn "Please see http://go/local-platform-support-prd for details."
fx-warn "Contact dannyrosen@google.com with any concerns."
fi
if [[ -n "${fint_params_path}" ]]; then
if [[ ${#ninja_switches[@]} -gt 0 ]]; then
fx-error "It's invalid to specify extra Ninja flags along with --fint-params-path."
exit 1
elif [[ ${#fuchsia_targets[@]} -gt 0 ]]; then
fx-error "It's invalid to specify targets along with --fint-params-path."
exit 1
fi
fi
if [[ "${is_logging}" = true ]]; then
# log file header with relevant environment information
{
TIMESTAMP="$(date +%Y%m%d_%H%M%S)"
echo "Build initiated at ${TIMESTAMP}"
echo
echo "------ GIT QUICK INFO ------"
echo "$ git status"
git --no-optional-locks --git-dir="${FUCHSIA_DIR}/.git" status
echo
echo "$ git rev-parse JIRI_HEAD"
git --no-optional-locks --git-dir="${FUCHSIA_DIR}/.git" rev-parse JIRI_HEAD
echo
echo "------ CONTENTS OF args.gn ------"
echo "$ cat ${FUCHSIA_BUILD_DIR}/args.gn"
echo
cat "${FUCHSIA_BUILD_DIR}/args.gn"
echo
} >> "${log_file}" 2>&1
# tee stdout and stderr to log_file
exec > >(tee -a "${log_file}") 2>&1
fi
# A change to any of these might mean things are now done differently enough
# that ninja's automatic re-gen rule might not be triggered or might not work
# properly if it is triggered. So preemptively force a re-gen if that seems
# like a plausible possibility.
local -r landmines=("$PREBUILT_GN"
"$FUCHSIA_DIR/tools/devshell/build"
"$FUCHSIA_DIR/tools/devshell/lib/vars.sh"
)
local mine
for mine in "${landmines[@]}"; do
if [[ "$mine" -nt "${FUCHSIA_BUILD_DIR}/build.ninja" && -z "${no_checks}" ]]; then
if [[ "${is_logging}" = true ]]; then
echo -e "\\n------ RUNNING gn gen ------"
fi
echo >&2 "Re-running gn gen first ($mine changed)"
fx-gen || return
break
fi
done
if [[ ! -d "$FUCHSIA_BUILD_DIR" ]]; then
if [[ "${is_logging}" = true ]]; then
echo -e "\\n------ RUNNING gn gen ------"
fi
echo >&2 "Re-running gn gen first (missing $FUCHSIA_BUILD_DIR)"
fx-gen || return
elif [[ -z "${no_checks}" ]]; then
# We have a build directory, execute force clean checker, usually a no-op
"$FUCHSIA_DIR/scripts/fuchsia-vendored-python" \
"$FUCHSIA_DIR/build/force_clean/force_clean_if_needed.py" \
--gn-bin "$PREBUILT_GN" \
--checkout-dir "$FUCHSIA_DIR" \
--build-dir "$FUCHSIA_BUILD_DIR"
fi
local status
if [[ "${is_logging}" = true ]]; then
local tool="ninja"
if [ -n "${fint_params_path}" ]; then
tool="fint build"
fi
echo -e "\\n------ RUNNING ${tool} ------"
fi
# Moving the goma-check before we actually start the build.
# We used to check goma only when build failed but the user pays a few seconds
# to kick off the build and wait for the failure.
if [[ -z "${no_checks}" ]]; then
goma-check
fi
if [ -n "${fint_params_path}" ]; then
readonly fint="${FX_CACHE_DIR}/fint"
"$FUCHSIA_DIR/tools/integration/bootstrap.sh" -o "$fint" || exit $?
local rbe_wrapper=()
if fx-rbe-enabled
then
rbe_wrapper=("${RBE_WRAPPER[@]}")
fi
# It's not ideal that we resort to constructing the textproto file as a
# string, but it's easier than writing a Go tool solely for the purpose of
# constructing a protobuf with a couple top-level string fields set.
"${rbe_wrapper[@]}" "$fint" -log-level=error build -static="${fint_params_path}" -context=<(echo "
checkout_dir: \"${FUCHSIA_DIR}\"
build_dir: \"${FUCHSIA_BUILD_DIR}\"
")
else
# Set FX_BUILD_WITH_LABELS to 0 to disable building with GN labels directly.
if [[ "${FX_BUILD_WITH_LABELS:-1}" == 1 && "${#fuchsia_targets[@]}" -gt 0 ]]; then
# A common mistake is to place a toolchain option after the GN label, thinking it
# applies to all labels. For example `fx build //src:foo --host` instead of
# `fx build --host //src:foo`.
#
# The latter builds //src:foo for the host, but the former builds //src:foo for Fuchsia,
# then ignores the --host option. This results in confusing build error messages
# (see https://fxbug.dev/328421720), so detect this here and make trailing toolchain
# options an error with an explicit message.
local last_index=$((${#fuchsia_targets[@]} - 1))
local last_fuchsia_target="${fuchsia_targets[${last_index}]}"
if [[ "${last_fuchsia_target}" =~ --.* ]]; then
fx-error "Toolchain option ${last_fuchsia_target} must be followed by at least one GN label"
exit 1
fi
# TODO(https://fxbug.dev/328316506): Remove --allow-unknown once GN is fixed.
local -a gn_labels # define variable to quiet shellcheck
command-stdout-to-array gn_labels fx-build-api-client fx_build_args_to_labels --allow-unknown --args "${fuchsia_targets[@]}"
# Convert GN labels to Ninja targets now.
# TODO(https://fxbug.dev/328316506): Remove --allow-unknown once GN is fixed.
local -a ninja_targets # define variable to quiet shellcheck
command-stdout-to-array ninja_targets fx-build-api-client gn_label_to_ninja_paths --allow-unknown "${gn_labels[@]}"
# For invalid labels, the above command will print errors to stderr directly and not list them in the result.
# If ninja_targets is empty, we can stop here.
if [[ -z "${ninja_targets[*]}" ]]; then
exit 1
fi
echo "Building Ninja target(s): ${ninja_targets[*]}"
else
ninja_targets=("${fuchsia_targets[@]}")
fi
(fx-run-ninja "${is_logging}" "$PREBUILT_NINJA" -C "${FUCHSIA_BUILD_DIR}" \
"${ninja_switches[@]}" "${ninja_targets[@]}")
fi
status=$?
exit-with-message
}
function goma-check {
if grep -q "^ *use_goma = true" "${FUCHSIA_BUILD_DIR}/args.gn"; then
cat <<EOF
**************** NOTICE ****************
Goma is deprecated and will be shutting down for Fuchsia soon.
You can replace it with re-client (linux-x64 only right now)
with the following steps.
* In args.gn (fx args), set:
cxx_rbe_enable = true
use_goma = false
* Make sure you have valid 'gcert' credentials.
* Resume your typical workflows.
See the go/fuchsia-goma-shutdown-psa announcement.
****************************************
EOF
if ! fx-command-run goma_ctl status >/dev/null 2>&1; then
fx-warn "Goma is enabled but not started."
fx-info "Starting Goma..."
# We may be able to get away with running `goma_ctl ensure_start`
# but it is safer to run the goma helper script which runs more checks.
fx-command-run goma
fi
fi
}
function exit-with-message {
if [[ "${is_logging}" = true ]]; then
fx-warn "Debug log saved to ${log_file}. Please attach this file when reporting a bug"
elif [[ "${status}" -ne 0 ]]; then
echo >&2 "Hint: run \`fx build\` with the option \`--log LOGFILE\` to generate a debug log if you are reporting a bug."
fi
exit "${status}"
}
main "$@"