| #!/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. | |
| ### set up a build directory | |
| ## usage: fx set TARGET [[--build-dir] OUTDIR] | |
| ## [--boards B1,B2,...] | |
| ## [--products P1,P2,...] | |
| ## [--packages P1,P2,...] | |
| ## [--netboot] | |
| ## [--args ARG] [--help-args [ARG]] [--variant VARIANT] | |
| ## [--goma|--no-goma] [--no-ensure-goma] | |
| ## [--goma-dir DIR] | |
| ## [--ccache|--no-ccache] | |
| ## [--release] | |
| ## [--zircon-arg ARG] | |
| ## | |
| ## where TARGET is x64 or arm64 | |
| ## | |
| ## OUTDIR 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/TARGET`. | |
| ## | |
| ## This is a wrapper around running `gn gen --check OUTDIR --args ...`. | |
| ## If GN fails with an error, `fx set` does not change anything. | |
| ## If GN succeeds, this also points subsequent `fx` commands at OUTDIR, | |
| ## just as `fx use` does and ensures Goma is ready (if enabled). | |
| ## | |
| ## optional arguments: | |
| ## --build-dir OUTDIR Using this option is deprecated. It's the same | |
| ## as just giving OUTDIR as a non-option argument. | |
| ## --board Use the listed board target definition. | |
| ## --product Include the listed product in the build. Defaults to | |
| ## the default product for the current layer (e.g., | |
| ## "garnet/products/default.gni" for the Garnet layer). | |
| ## --available Additional packages to be built and included in the | |
| ## set of packages available for pushing dynamically. | |
| ## If the --available argument is given multiple times, | |
| ## all the specified available are included in this set. | |
| ## These packages are added to the available set defined | |
| ## by the board and product specifications. | |
| ## --preinstall Additional packages to be built and included in the | |
| ## system image alongside the monolithic system image. | |
| ## If the --preinstall argument is given multiple times, | |
| ## all the specified packages are included in this set. | |
| ## These packages are added to the available set defined | |
| ## by the board and product specifications. | |
| ## --monolith Additional packages to be built and included in the | |
| ## monolithic system image. | |
| ## If the --monolith argument is given multiple times, | |
| ## all the specified packages are included in this set. | |
| ## These packages are added to the available set defined | |
| ## by the board and product specifications. | |
| ## --variant Pass a `select_variant=[VARIANT*,...]` GN build argument | |
| ## collecting all the --variant arguments in order. | |
| ## --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". | |
| ## --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 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. | |
| ## --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. | |
| ## --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. | |
| ## --no-ensure-goma Skip ensuring that goma is started when using goma. | |
| ## --goma-dir The directory where goma is installed. Defaults to | |
| ## ~/goma. | |
| ## --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. | |
| ## --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. | |
| ## --release an alias for "--args=is_debug=false" | |
| ## --zircon-arg ARG Additional arguments to pass to Zircon make. Can be given | |
| ## multiple times. | |
| ## | |
| ## Deprecated flags: | |
| ## | |
| ## --boards Use the listed board target definition (seperated by | |
| ## comas) in the build, each board results in an import | |
| ## statement and MUST define the `fuchsia_packages` | |
| ## variable as well as deal with variable re-definition. | |
| ## This flag is deprecated, please use --board instead. | |
| ## --products Include the listed products (separated by commas) in | |
| ## the build. Defaults to the default product for the | |
| ## current layer (e.g., "garnet/packages/default.gni" for | |
| ## the Garnet layer). If the --products argument is | |
| ## given multiple times, the products configurations | |
| ## are merged. | |
| ## This flag is deprecated, please use exactly one | |
| ## --product and specify packages to add in addition to | |
| ## the product with --packages. | |
| ## --packages Additional packages to be built and made available. | |
| ## If the --packages argument is given multiple times, | |
| ## all the specified packages are included in the | |
| ## build. | |
| ## This flag is deprecated, please use one of | |
| ## --available, --preinstall, or --monolith. | |
| source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"/lib/vars.sh | |
| function guess_config_within_layer { | |
| local config_type="$1" | |
| local config_name="$2" | |
| local __resultvar="$3" | |
| # Guess the petal we're using. We'll search at this petal and below for | |
| # config matching the type and short name | |
| local current_petal | |
| current_petal="$(${FUCHSIA_DIR}/build/gn/guess_layer.py)" || return 1 | |
| readonly current_petal | |
| # Compute a lookup order starting at the current petal going down. | |
| local petal_order="${current_petal}" | |
| case $current_petal in | |
| vendor/*) | |
| petal_order="${petal_order},topaz,peridot,garnet" | |
| ;; | |
| "topaz") | |
| petal_order="${petal_order},peridot,garnet" | |
| ;; | |
| "peridot") | |
| petal_order="${petal_order},garnet" | |
| ;; | |
| esac | |
| readonly petal_order | |
| # Look through petals in this order to find configs with matching names. | |
| IFS=, | |
| local petal | |
| for petal in $petal_order; do | |
| guessed_config="${petal}/${config_type}/${config_name}.gni" | |
| if [[ -a "${FUCHSIA_DIR}/${guessed_config}" ]]; then | |
| echo "Guessing ${config_type} config ${guessed_config}" | |
| eval "${__resultvar}"="${guessed_config}" | |
| return | |
| fi | |
| done | |
| echo "Could not guess a ${config_type} configuration matching \"${config_name}\"" | |
| echo "Please specify the full path from the root of the checkout such as" | |
| echo "garnet/${config_type}/base.gni" | |
| exit 1 | |
| } | |
| function main { | |
| fx-standard-switches "$@" | |
| set -- "${FX_ARGV[@]}" | |
| if [[ $# -lt 1 ]]; then | |
| fx-command-help | |
| return 1 | |
| fi | |
| local arch= | |
| case $1 in | |
| x64 | x86 | x64-64) | |
| arch=x64 | |
| ;; | |
| arm64 | aarch64) | |
| arch=arm64 | |
| ;; | |
| *) | |
| # TODO(alainv): Add support for extracting arch from board configs. | |
| echo Unknown target \"$1\" | |
| fx-command-help | |
| return 1 | |
| ;; | |
| esac | |
| shift | |
| local gn_cmd='gen' | |
| local -a gn_switches=(--check) | |
| local gn_args="target_cpu=\"${arch}\"" | |
| local boards=() | |
| local products=() | |
| local available=() | |
| local preinstall=() | |
| local monolith=() | |
| local packages=() | |
| local zircon_args=() | |
| local extra_packages=() | |
| local build_dir= | |
| local variant= | |
| local use_goma | |
| local goma_dir | |
| local ensure_goma=1 | |
| local ccache | |
| while [[ $# -ne 0 ]]; do | |
| case "$1" in | |
| --board) | |
| if [[ $# -lt 2 ]]; then | |
| fx-command-help | |
| return 1 | |
| fi | |
| boards+=("$2") | |
| shift | |
| ;; | |
| --product) | |
| if [[ $# -lt 2 ]]; then | |
| fx-command-help | |
| return 1 | |
| fi | |
| products+=("$2") | |
| shift | |
| ;; | |
| --available) | |
| if [[ $# -lt 2 ]]; then | |
| fx-command-help | |
| return 1 | |
| fi | |
| IFS=, available+=("$2") | |
| shift | |
| ;; | |
| --preinstall) | |
| if [[ $# -lt 2 ]]; then | |
| fx-command-help | |
| return 1 | |
| fi | |
| IFS=, preinstall+=("$2") | |
| shift | |
| ;; | |
| --monolith) | |
| if [[ $# -lt 2 ]]; then | |
| fx-command-help | |
| return 1 | |
| fi | |
| IFS=, monolith+=("$2") | |
| shift | |
| ;; | |
| --boards) | |
| if [[ $# -lt 2 ]]; then | |
| fx-command-help | |
| return 1 | |
| fi | |
| # Multiple boards may be comma separated | |
| IFS=, boards+=("$2") | |
| shift | |
| ;; | |
| --products) | |
| if [[ $# -lt 2 ]]; then | |
| fx-command-help | |
| return 1 | |
| fi | |
| # Multiple products may be comma separated | |
| IFS=, products+=("$2") | |
| shift | |
| ;; | |
| --packages) | |
| if [[ $# -lt 2 ]]; then | |
| fx-command-help | |
| return 1 | |
| fi | |
| # Multiple packages may be comma separated | |
| IFS=, packages+=("$2") | |
| shift | |
| ;; | |
| --zircon-arg) | |
| if [[ $# -lt 2 ]]; then | |
| fx-command-help | |
| return 1 | |
| fi | |
| zircon_args+=("$2") | |
| shift | |
| ;; | |
| --netboot) | |
| extra_packages+=('build/packages/netboot') | |
| ;; | |
| # TODO(mcgrathr): Remove this after a deprecation period. | |
| --build-dir) | |
| if [[ $# -lt 2 ]]; then | |
| fx-command-help | |
| return 1 | |
| fi | |
| echo >&2 "NOTE: The --build-dir switch is deprecated; give $2 alone." | |
| build_dir="$2" | |
| shift | |
| ;; | |
| --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 | |
| echo -e "error: GOMA directory does not exist: "${goma_dir}"" | |
| return 1 | |
| fi | |
| shift | |
| ;; | |
| --release) | |
| gn_args+=" is_debug=false" | |
| ;; | |
| --variant) | |
| if [[ $# -lt 2 ]]; then | |
| fx-command-help | |
| return 1 | |
| fi | |
| variant+="\"$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 | |
| ;; | |
| --*) | |
| fx-command-help | |
| return 1 | |
| ;; | |
| *) | |
| # A non-option argument is the build_dir, but there can be only one. | |
| if [[ -n "$build_dir" ]]; then | |
| fx-command-help | |
| return 1 | |
| fi | |
| build_dir="$1" | |
| ;; | |
| esac | |
| shift | |
| done | |
| if [[ -z "${products}${packages}" ]]; then | |
| # This is the default logic GN would use, but if a user specified --netboot | |
| # we would short-circuit the logic, so repeat it here. | |
| local layers | |
| layers="$(${FUCHSIA_DIR}/build/gn/guess_layer.py)" || return 1 | |
| readonly layers | |
| local layer | |
| for layer in $layers; do | |
| products+="${products:+,}$layer/products/default.gni" | |
| done | |
| fi | |
| if [[ -z "${boards}" ]]; then | |
| # Import the board target definition from the current layer if it exists. | |
| local layers | |
| if [[ -z "${layers}" ]]; then | |
| layers="$(${FUCHSIA_DIR}/build/gn/guess_layer.py)" || return 1 | |
| fi | |
| readonly layers | |
| local layer | |
| for layer in $layers; do | |
| if [[ -e "$layer/boards/$arch.gni" ]]; then | |
| boards+="${boards:+,}$layer/boards/$arch.gni" | |
| fi | |
| done | |
| fi | |
| # Remove any trailing slash from build directory name. | |
| build_dir="${build_dir%/}" | |
| local config_build_dir | |
| case "$build_dir" in | |
| '') | |
| # Default is "//out/$target_cpu". Store it as relative. | |
| config_build_dir="out/${arch}" | |
| build_dir="${FUCHSIA_DIR}/${config_build_dir}" | |
| ;; | |
| //*|out/*) | |
| # GN-style "source-relative" path or relative out/something. | |
| config_build_dir="${build_dir#//}" | |
| build_dir="${FUCHSIA_DIR}/${config_build_dir}" | |
| ;; | |
| *) | |
| # Absolute or relative path. Canonicalize it to source-relative. | |
| local abs_build_dir | |
| abs_build_dir="$(cd "${build_dir%/*}"; pwd)/${build_dir##*/}" || { | |
| echo >&2 "ERROR: Missing parent directories for ${build_dir}" | |
| return 1 | |
| } | |
| if [[ "$abs_build_dir" == "${FUCHSIA_DIR}"/out/* ]]; then | |
| config_build_dir="${abs_build_dir#${FUCHSIA_DIR}/}" | |
| else | |
| echo >&2 "WARNING: ${abs_build_dir} is not a subdirectory of ${FUCHSIA_DIR}/out" | |
| config_build_dir="$abs_build_dir" | |
| fi | |
| ;; | |
| esac | |
| # 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 | |
| for board in ${boards[@]}; do | |
| if [[ ! -a "${FUCHSIA_DIR}/${board}" ]]; then | |
| local guessed_board="" | |
| guess_config_within_layer "boards" "${board}" guessed_board | |
| board="${guessed_board}" | |
| fi | |
| gn_args+=" import(\"//${board}\")" | |
| done | |
| # 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 | |
| for product in ${products[@]}; do | |
| if [[ ! -a "${FUCHSIA_DIR}/${product}" ]]; then | |
| local guessed_product="" | |
| guess_config_within_layer "products" "${product}" guessed_product | |
| product="${guessed_product}" | |
| fi | |
| gn_args+=" import(\"//${product}\")" | |
| done | |
| if [[ -z "${boards}" ]]; then | |
| gn_args+=" fuchsia_packages=[" | |
| else | |
| # Board configs MUST declare fuchsia_packages. | |
| gn_args+=" fuchsia_packages+=[" | |
| fi | |
| for package in ${packages[@]} ${extra_packages[@]}; do | |
| gn_args+="\"${package}\"," | |
| done | |
| gn_args+="]" | |
| gn_args+=" if (!defined(available)) { available = [] }" | |
| gn_args+=" available+=[" | |
| for package in ${available[@]}; do | |
| gn_args+="\"${package}\"," | |
| done | |
| gn_args+="]" | |
| gn_args+=" if (!defined(preinstall)) { preinstall = [] }" | |
| gn_args+=" preinstall+=[" | |
| for package in ${preinstall[@]}; do | |
| gn_args+="\"${package}\"," | |
| done | |
| gn_args+="]" | |
| gn_args+=" if (!defined(monolith)) { monolith = [] }" | |
| gn_args+=" monolith+=[" | |
| for package in ${monolith[@]}; do | |
| gn_args+="\"${package}\"," | |
| done | |
| gn_args+="]" | |
| if [[ -n "${variant}" ]]; then | |
| gn_args+=" select_variant=[${variant}]" | |
| fi | |
| mkdir -p "${build_dir}" | |
| echo "${zircon_args[@]}" > "${build_dir}.zircon-args" | |
| # Using a subshell with -x prints out the gn command precisely with shell | |
| # quoting so a cut&paste to the command line works. Always show the real | |
| # meaning of what this script does so everyone learns how GN works. | |
| ( | |
| set -x | |
| "${FUCHSIA_DIR}/buildtools/gn" ${gn_cmd} "${build_dir}" \ | |
| "${gn_switches[@]}" --args="${gn_args}" "$@" | |
| # If GN failed, don't update .config. | |
| ) || return | |
| fx-config-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 "$@" |