| #!/bin/bash | |
| # Copyright 2019 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 | |
| ### set up a build directory | |
| ## usage: fx set PRODUCT.BOARD [--with GNLABEL1,GNLABEL2,...] | |
| ## [--release] [--auto-dir | --build-dir BUILDDIR] | |
| ## [--args ARG] [--help-args [ARG]] [--variant VARIANT] | |
| ## [--with-base GNLABEL1,GNLABEL2,...] | |
| ## | |
| ## where PRODUCT is an entry from `//products` or `//vendor/*/products` and | |
| ## BOARD is an entry from `//boards` or `//vendor/*/boards`. Use the | |
| ## `fx list-products` and `fx list-boards` commands to see a list of | |
| ## possible products and boards (respectively). See the README.md in those | |
| ## directories for a description of the various options. | |
| ## | |
| ## BUILDDIR is the directory where the build output goes. | |
| ## If it begins with `//` or `out/` then it's taken as relative to FUCHSIA_DIR. | |
| ## Otherwise it should be an absolute path or a path relative to the current | |
| ## working directory that winds up in `FUCHSIA_DIR/out`. | |
| ## It defaults to `out/default`. | |
| ## | |
| ## This command stores the location of the build directory in the //.fx-build-dir | |
| ## file, which causes subsequent `fx` commands to use that build directory. Use | |
| ## `fx use` to switch build directories. | |
| ## | |
| ## Ensures Goma is ready (if Goma is enabled). | |
| ## | |
| ## This is a wrapper around running `gn gen --check BUILDDIR --args ...`. | |
| ## If GN fails with an error, `fx set` does not change anything. | |
| ## | |
| ## optional arguments: | |
| ## --args Additional argument to pass to gn. If the --args | |
| ## argument is given multiple times, all the specified | |
| ## arguments are passed to gn. | |
| ## N.B. Arguments must be expressed using GN's syntax. | |
| ## In particular this means that for strings they must | |
| ## be quoted with double-quotes, and the quoting must | |
| ## survive, for example, the shell. Thus when passing | |
| ## an argument that takes a string, pass it with | |
| ## something like --args=foo='"bar"'. E.g., | |
| ## bash$ fx set core.x64 --args=foo='"bar"' | |
| ## More complicated arguments, e.g., lists, require | |
| ## their own special syntax. See GN documentation | |
| ## for the syntax of each kind of argument. | |
| ## --auto-dir Act like `fx --dir out/PRODUCT.BOARD set ...`. | |
| ## --ccache|--no-ccache Whether to use ccache during the build. Ccache attempts | |
| ## to make builds faster by caching build artifacts. | |
| ## Defaults to detecting whether the CCACHE_DIR environment | |
| ## variable is set to a directory. | |
| ## --fuzz-with Pass a sanitizer name, e.g. "--fuzz-with asan" to | |
| ## enable ALL supporting fuzzers. Use --variant for | |
| ## individual fuzzers, e.g. "--variant asan-fuzzer/foo". | |
| ## --goma|--no-goma Whether to use the goma service during the build. Goma | |
| ## attempts to make builds faster using remote build | |
| ## servers. Defaults to detecting whether goma is installed | |
| ## on your machine. | |
| ## --goma-dir The directory where goma is installed. Defaults to | |
| ## ~/goma. | |
| ## --help-args Display GN arguments documentation. If --help-args | |
| ## is followed by a GN build argument identifier, just | |
| ## that argument's documentation is displayed. | |
| ## If --help-args is used alone, all GN build arguments | |
| ## are displayed (lots of output). | |
| ## This option requires an existing build directory. | |
| ## --ide Pass --ide=VALUE to gn when generating to create project | |
| ## files usable with that IDE. Useful values include "vs" | |
| ## for Visual Studio or "xcode" for Xcode. | |
| ## --json-ide-script Pass --json-ide-script=python_script to gn which runs | |
| ## the given python script after the JSON project file is | |
| ## generated when using --ide json. The path to the project | |
| ## file is given as the first argument to the script. | |
| ## The script may be a path or a gn label. | |
| ## --netboot Ensure that a network ramboot image is always built. | |
| ## --no-ensure-goma Skip ensuring that goma is started when using goma. | |
| ## --release an alias for "--args=is_debug=false" | |
| ## --variant Selects which runtime variant to use (e.g., asan) by | |
| ## passing a `select_variant=[VARIANT*,...]` argument | |
| ## to gn that collects all the --variant arguments in | |
| ## order. Variants are normal builds in most respects, | |
| ## but allow users to test different runtime settings | |
| ## for either the whole build or for a specific target. | |
| ## This can be specified by passing the variant name | |
| ## to this argument (e.g. `--variant asan`) for the | |
| ## former, or the variant name and the target name | |
| ## separated by a slash (e.g. `--variant asan/my_test`) | |
| ## for the latter. | |
| ## --with GNLABEL Labels of additional packages to include in the | |
| ## universe of available packages. These packages can | |
| ## be run ephemerally. Multiple labels can be provided | |
| ## delimited by commas or the --with argument can be | |
| ## provided multiple times. | |
| ## --with-base GNLABEL Labels of additional packages to include in the | |
| ## base set of packages. These packages are included in | |
| ## the system image and can be updated only with an OTA. | |
| ## Multiple labels can be provided delimited by commas | |
| ## or the --with-base argument can be provided multiple | |
| ## times. | |
| ## | |
| ## Example: | |
| ## | |
| ## $ fx set core.x64 --with //bundles:tests | |
| ## -> build directory: out/default | |
| ## board: //boards/x64.gni | |
| ## product: //products/core.gni | |
| ## universe: //bundles:tests (all test packages) | |
| source "$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"/lib/vars.sh || exit $? | |
| set -e | |
| function find_config { | |
| local config_type="$1" | |
| local config_name="$2" | |
| for directory in vendor/*/"${config_type}" "${config_type}"; do | |
| local guessed_config="${directory}/${config_name}.gni" | |
| if [[ -a "${guessed_config}" ]]; then | |
| echo "${guessed_config}" | |
| return | |
| fi | |
| done | |
| fx-error "Could not find a ${config_type} configuration matching \"${config_name}\"" | |
| fx-error "Checked:" | |
| for directory in vendor/*/"${config_type}" "${config_type}"; do | |
| local guessed_config="${directory}/${config_name}.gni" | |
| fx-error " ${guessed_config}" | |
| done | |
| exit 1 | |
| } | |
| function explain-goal-argument { | |
| fx-error You must specify which product and which board you wish to build. | |
| fx-error Example: | |
| fx-error " fx set core.x64" | |
| fx-error | |
| fx-error Run \"fx list-products\" to see the available products. | |
| fx-error Run \"fx list-boards\" to see the available boards. | |
| fx-error Run \"fx set --help\" to see full usage. | |
| fx-error Run \"fx status\" to see the current developer settings. | |
| return 0 | |
| } | |
| function main { | |
| fx-standard-switches "$@" | |
| set -- "${FX_ARGV[@]}" | |
| if [[ $# -lt 1 ]]; then | |
| fx-error Missing an explicit PRODUCT.BOARD goal. | |
| explain-goal-argument | |
| return 1 | |
| fi | |
| cd "${FUCHSIA_DIR}" | |
| local gn_args="" | |
| case "$1" in | |
| *.*) | |
| local product_name="${1%.*}" | |
| local board_name="${1##*.}" | |
| local product="$(find_config "products" "${product_name}")" | |
| local board="$(find_config "boards" "${board_name}")" | |
| if [[ "${product}" == "" ]] || [[ "${board}" == "" ]]; then | |
| exit 1 | |
| fi | |
| gn_args+=" import(\"//${board}\") import(\"//${product}\")" | |
| ;; | |
| *) | |
| fx-error Unable to parse PRODUCT.BOARD goal \"$1\" | |
| explain-goal-argument | |
| return 1 | |
| ;; | |
| esac | |
| shift | |
| local gn_cmd='gen' | |
| local -a gn_switches=(--ide=json --check --export-compile-commands=default) | |
| local gn_after_args="" | |
| local base=() | |
| local universe=() | |
| local auto_dir_variants= | |
| local auto_dir=false | |
| local is_release=false | |
| local build_dir= | |
| local variant= | |
| local use_goma | |
| local goma_dir | |
| local ensure_goma=1 | |
| local ccache | |
| while [[ $# -ne 0 ]]; do | |
| case "$1" in | |
| --auto-dir) | |
| if [[ -n "${_FX_BUILD_DIR}" ]]; then | |
| fx-error "fx --dir and fx set --auto-dir are mutually exclusive, pick one." | |
| return 1 | |
| fi | |
| auto_dir=true | |
| ;; | |
| --with-base) | |
| if [[ $# -lt 2 ]]; then | |
| fx-command-help | |
| return 1 | |
| fi | |
| IFS=, base+=("$2") | |
| shift | |
| ;; | |
| --with) | |
| if [[ $# -lt 2 ]]; then | |
| fx-command-help | |
| return 1 | |
| fi | |
| # deny list for very specific packages that are | |
| # deprecated_system_image=true. This list should not grow. | |
| case "$2" in | |
| *tests/zircon:zircon_tests) | |
| fx-error "zircon tests are deprecated_system_image and must be given to --with-base" | |
| return 1;; | |
| esac | |
| IFS=, universe+=("$2") | |
| shift | |
| ;; | |
| --netboot) | |
| gn_after_args+=" enable_netboot=true" | |
| ;; | |
| --goma) | |
| use_goma=1 | |
| ;; | |
| --no-goma) | |
| use_goma=0 | |
| ;; | |
| --no-ensure-goma) | |
| ensure_goma=0 | |
| ;; | |
| --goma-dir) | |
| if [[ $# -lt 2 ]]; then | |
| fx-command-help | |
| return 1 | |
| fi | |
| goma_dir=$2 | |
| if [[ ! -d "${goma_dir}" ]]; then | |
| fx-error "GOMA directory does not exist: ${goma_dir}" | |
| return 1 | |
| fi | |
| shift | |
| ;; | |
| --build-dir) | |
| if [[ $# -lt 2 ]]; then | |
| fx-command-help | |
| return 1 | |
| fi | |
| if [[ -n "${_FX_BUILD_DIR}" ]]; then | |
| fx-error "fx --dir and fx set --build-dir are mutually exclusive, pick one." | |
| return 1 | |
| fi | |
| fx-warn "DEPRECATED: \"fx set --build-dir DIR\"" | |
| fx-warn " NEW: \"fx --dir DIR set\"" | |
| build_dir="$2" | |
| shift | |
| ;; | |
| --release) | |
| gn_args+=" is_debug=false" | |
| is_release=true | |
| ;; | |
| --variant) | |
| if [[ $# -lt 2 ]]; then | |
| fx-command-help | |
| return 1 | |
| fi | |
| variant+="\"$2\"," | |
| auto_dir_variants+="-$2" | |
| shift | |
| ;; | |
| --fuzz-with) | |
| if [[ $# -lt 2 ]]; then | |
| fx-command-help | |
| return 1 | |
| fi | |
| variant+="{variant=\"$2-fuzzer\" target_type=[\"fuzzed_executable\"]}," | |
| shift | |
| ;; | |
| --args) | |
| if [[ $# -lt 2 ]]; then | |
| fx-command-help | |
| return 1 | |
| fi | |
| gn_args+=" $2" | |
| shift | |
| ;; | |
| --help-args) | |
| gn_cmd=args | |
| if [[ $# -ge 2 ]] && [[ "$2" != '--*' ]]; then | |
| gn_switches+=("--list=$2") | |
| shift | |
| else | |
| gn_switches+=(--list) | |
| fi | |
| ;; | |
| --ccache) | |
| ccache=1 | |
| ;; | |
| --no-ccache) | |
| ccache=0 | |
| ;; | |
| --ide) | |
| if [[ $# -lt 2 ]]; then | |
| fx-command-help | |
| return 1 | |
| fi | |
| gn_switches+=("--ide=$2") | |
| shift | |
| ;; | |
| --json-ide-script) | |
| if [[ $# -lt 2 ]]; then | |
| fx-command-help | |
| return 1 | |
| fi | |
| gn_switches+=("--json-ide-script=$2") | |
| shift | |
| ;; | |
| *) | |
| fx-error "Unknown argument \"$1\"" | |
| fx-command-help | |
| return 1 | |
| ;; | |
| esac | |
| shift | |
| done | |
| if [[ -z "${build_dir}" ]]; then | |
| build_dir="${_FX_BUILD_DIR}" | |
| fi | |
| # Remove any trailing slash from build directory name. | |
| build_dir="${build_dir%/}" | |
| local config_build_dir | |
| if $auto_dir; then | |
| if [[ -n "$build_dir" ]]; then | |
| fx-error "--auto-dir and --build-dir are mutually exclusive, pick one." | |
| return 1 | |
| fi | |
| if [[ "$auto_dir_variants" == */* ]]; then | |
| fx-error "--auto-dir only works with simple catch-all --variant switches; choose your own directory name with fx --dir for a complex configuration" | |
| return 1 | |
| fi | |
| config_build_dir="out/${product_name}.${board_name}${auto_dir_variants}" | |
| if $is_release; then | |
| config_build_dir+="-release" | |
| fi | |
| else | |
| case "$build_dir" in | |
| '') | |
| # Default is "//out/default". Store it as relative. | |
| config_build_dir="out/default" | |
| ;; | |
| //*|out/*) | |
| # GN-style "source-relative" path or relative out/something. | |
| config_build_dir="${build_dir#//}" | |
| ;; | |
| *) | |
| fx-error "Invalid build directory: ${build_dir}" | |
| fx-error "Please specify a build directory as \"out/something\"." | |
| exit 1 | |
| ;; | |
| esac | |
| fi | |
| build_dir="${FUCHSIA_DIR}/${config_build_dir}" | |
| # If a goma directory wasn't specified explicitly then default to "~/goma". | |
| if [[ -z "${goma_dir}" ]]; then | |
| goma_dir="$HOME/goma" | |
| fi | |
| # Automatically detect goma and ccache if not specified explicitly. | |
| if [[ -z "${use_goma}" ]] && [[ -z "${ccache}" ]]; then | |
| if [[ -d "${goma_dir}" ]]; then | |
| use_goma=1 | |
| elif [[ -n "${CCACHE_DIR}" ]] && [[ -d "${CCACHE_DIR}" ]]; then | |
| ccache=1 | |
| fi | |
| fi | |
| # Add goma or ccache settings as appropriate. | |
| if [[ "${use_goma}" -eq 1 ]]; then | |
| gn_args+=" use_goma=true goma_dir=\"${goma_dir}\"" | |
| elif [[ "${ccache}" -eq 1 ]]; then | |
| gn_args+=" use_ccache=true" | |
| fi | |
| gn_args+=" | |
| # See: fx args --list=base_package_labels | |
| base_package_labels+=[" | |
| for package in ${base[@]}; do | |
| gn_args+="\"${package}\"," | |
| done | |
| gn_args+="] | |
| # See: fx args --list=cache_package_labels | |
| cache_package_labels+=[] | |
| # See: fx args --list=universe_package_labels | |
| universe_package_labels+=[" | |
| for package in ${universe[@]}; do | |
| gn_args+="\"${package}\"," | |
| done | |
| gn_args+="]" | |
| if [[ -n "${variant}" ]]; then | |
| gn_args+=" select_variant=[${variant}]" | |
| fi | |
| gn_args+="${gn_after_args}" | |
| fx-gn ${gn_cmd} "${build_dir}" "${gn_switches[@]}" --args="${gn_args}" "$@" | |
| fx-build-dir-write "${config_build_dir}" | |
| if [[ "${use_goma}" -eq 1 ]] && [[ "${ensure_goma}" -eq 1 ]]; then | |
| if ! [[ $("${goma_dir}/gomacc" port) =~ ^[0-9]+$ ]]; then | |
| "${goma_dir}/goma_ctl.py" ensure_start || return $? | |
| fi | |
| fi | |
| } | |
| main "$@" |