| #!/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 "$@" |