|  | #!/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. | 
|  |  | 
|  | function help_options { | 
|  | cat <<END | 
|  |  | 
|  | fx help flags: fx help [OPTION] | 
|  | --no-contrib      Hide contrib commands (see //tools/devshell/README.md) | 
|  | --deprecated      Do not hide deprecated commands | 
|  | END | 
|  | } | 
|  |  | 
|  | function help_global_options { | 
|  | cat <<END | 
|  |  | 
|  | Global fx options: fx [OPTION] ${cmd} ... | 
|  | --dir=BUILD_DIR       Path to the build directory to use when running COMMAND. | 
|  | --disable=FEATURE     Disable a feature (non-persistent). | 
|  | Valid values for FEATURE: [$(list_optional_features)] | 
|  | -d=DEVICE[:SSH_PORT]  Target a specific device. | 
|  | DEVICE may be a Fuchsia device name to be resolved using | 
|  | device-finder or an IP address. | 
|  | An IPv4 address must be specified directly, while an IPv6 | 
|  | need to be surrounded by brackets. | 
|  | SSH_PORT, if specified, will be used for all commands | 
|  | that rely on SSH to connect to the device instead of the | 
|  | default SSH port (22). | 
|  | Note: See "fx help set-device" for more help and to set | 
|  | the default device for a BUILD_DIR. | 
|  | -i                    Iterative mode.  Repeat the command whenever a file is | 
|  | modified under your Fuchsia directory, not including | 
|  | out/. | 
|  | -x                    Print commands and their arguments as they are executed. | 
|  | -xx                   Print extra logging of the fx tool itself (implies -x) | 
|  | END | 
|  | } | 
|  |  | 
|  | function help_list_commands { | 
|  | local hide_contrib=0 | 
|  | local show_deprecated=0 | 
|  | while [[ $# -ne 0 ]]; do | 
|  | if [[ "$1" == "--deprecated" ]]; then | 
|  | show_deprecated=1 | 
|  | elif [[ "$1" == "--no-contrib" ]]; then | 
|  | hide_contrib=1 | 
|  | fi | 
|  | shift | 
|  | done | 
|  |  | 
|  | # list all subcommands with summaries, grouped by categories | 
|  | commands | xargs awk \ | 
|  | -v hide_contrib=${hide_contrib} \ | 
|  | -v show_deprecated=${show_deprecated} \ | 
|  | -f "${fuchsia_dir}/scripts/fx-help.awk" | 
|  |  | 
|  | # list host tools build artifacts without corresponding metadata | 
|  | host_tools="$(find_host_tools)" | 
|  | if [[ -n "${host_tools}" ]]; then | 
|  | echo "Host binaries produced by the build with no metadata (more info at //tools/devshell/README.md):" | 
|  | for i in ${host_tools}; do | 
|  | echo -n "  " | 
|  | basename $i | 
|  | done | 
|  | fi | 
|  |  | 
|  | help_options | 
|  | help_global_options | 
|  | } | 
|  |  | 
|  | function help_command { | 
|  | local cmd="$@" | 
|  | local cmd_path="$(commands ${cmd} | head -1)" | 
|  | if [[ -z "${cmd_path}" ]]; then | 
|  | local cmd_path="$(find_host_tools ${cmd} | head -1)" | 
|  | if [[ -z "${cmd_path}" ]]; then | 
|  | echo "Command ${cmd} not found. Try \`fx help\`" | 
|  | else | 
|  | echo "'$(_relative "${cmd_path}")' is a host tool and no metadata" \ | 
|  | "was found. Try running \`fx ${cmd} -h\`" | 
|  | fi | 
|  | elif [[ $(file -b --mime "${cmd_path}" | cut -d / -f 1) == "text" ]]; then | 
|  | fx-print-command-help "${cmd_path}" | 
|  | help_global_options | 
|  | else | 
|  | echo "No help found. Try \`fx ${cmd} -h\`" | 
|  | fi | 
|  | } | 
|  |  | 
|  | function usage { | 
|  | cat <<END | 
|  | usage: fx [--dir BUILD_DIR] [-d DEVICE_NAME] [-i] [-x] COMMAND [...] | 
|  |  | 
|  | Run Fuchsia development commands. Must be run either from a directory | 
|  | that is contained in a Platform Source Tree or with the FUCHSIA_DIR | 
|  | environment variable set to the root of a Platform Source Tree. | 
|  |  | 
|  | host shell extensions: (requires "source scripts/fx-env.sh") | 
|  | fx-update-path        add useful tools to the PATH | 
|  | fx-set-prompt         display the current configuration in the shell prompt | 
|  |  | 
|  | END | 
|  |  | 
|  | help_list_commands "$@" | 
|  | } | 
|  |  | 
|  | fuchsia_dir="${FUCHSIA_DIR}" | 
|  | if [[ -z "${fuchsia_dir}" ]]; then | 
|  | # We walk the parent directories looking for .jiri_root rather than using | 
|  | # BASH_SOURCE so that we find the fuchsia_dir enclosing the current working | 
|  | # directory instead of the one containing this file in case the user has | 
|  | # multiple source trees and is picking up this file from another one. | 
|  | fuchsia_dir="$(pwd)" | 
|  | while [[ ! -d "${fuchsia_dir}/.jiri_root" ]]; do | 
|  | fuchsia_dir="$(dirname "${fuchsia_dir}")" | 
|  | if [[ "${fuchsia_dir}" == "/" ]]; then | 
|  | echo "Cannot find Platform Source Tree containing $(pwd)" | 
|  | exit 1 | 
|  | fi | 
|  | done | 
|  | fi | 
|  |  | 
|  | declare -r vars_sh="${fuchsia_dir}/tools/devshell/lib/vars.sh" | 
|  | source "${vars_sh}" || exit $? | 
|  |  | 
|  | declare -r metrics_sh="${fuchsia_dir}/tools/devshell/lib/metrics.sh" | 
|  | source "${metrics_sh}" || exit $? | 
|  |  | 
|  | declare -r cmd_locator_sh="${fuchsia_dir}/tools/devshell/lib/fx-cmd-locator.sh" | 
|  | source "${cmd_locator_sh}" || exit $? | 
|  |  | 
|  | declare -r features_sh="${fuchsia_dir}/tools/devshell/lib/fx-optional-features.sh" | 
|  | source "${features_sh}" || exit $? | 
|  |  | 
|  | while [[ $# -ne 0 ]]; do | 
|  | case $1 in | 
|  | --config=*|--dir=*|-d=*|--disable=*) | 
|  | # Turn --switch=value into --switch value. | 
|  | arg="$1" | 
|  | shift | 
|  | set -- "${arg%%=*}" "${arg#*=}" "$@" | 
|  | continue | 
|  | ;; | 
|  | --config) | 
|  | fx-warn "DEPRECATION NOTICE: --config is deprecated, use --dir instead." | 
|  | sleep 5 | 
|  | if [[ $# -lt 2 ]]; then | 
|  | usage | 
|  | fx-error "Missing path to config file for --config argument" | 
|  | exit 1 | 
|  | fi | 
|  | shift # Removes --config. | 
|  | export _FX_BUILD_DIR="$(source "$1"; echo "${FUCHSIA_BUILD_DIR}")" | 
|  | ;; | 
|  | --dir) | 
|  | if [[ $# -lt 2 ]]; then | 
|  | usage | 
|  | fx-error "Missing path to build directory for --dir argument" | 
|  | exit 1 | 
|  | fi | 
|  | shift # Removes --dir. | 
|  | export _FX_BUILD_DIR="$1" | 
|  | if [[ "$_FX_BUILD_DIR" == //* ]]; then | 
|  | _FX_BUILD_DIR="${fuchsia_dir}/${_FX_BUILD_DIR#//}" | 
|  | fi | 
|  | ;; | 
|  | --disable) | 
|  | if [[ $# -lt 2 ]]; then | 
|  | usage | 
|  | fx-error "Missing argument to --disable" | 
|  | exit 1 | 
|  | fi | 
|  | shift # Removes --disable. | 
|  | feature="$1" | 
|  | if ! is_valid_feature "${feature}"; then | 
|  | fx-error "Unknown feature \"${feature}\". Valid values are: $(list_optional_features)" | 
|  | exit 1 | 
|  | fi | 
|  | env_name="$(get_disable_feature_env_name "${feature}")" | 
|  | export ${env_name}=1 | 
|  | ;; | 
|  | -d) | 
|  | if [[ $# -lt 2 ]]; then | 
|  | usage | 
|  | fx-error "Missing device name for -d argument" | 
|  | exit 1 | 
|  | fi | 
|  | shift # removes -d | 
|  | if ! is-valid-device "$1"; then | 
|  | fx-error "Invalid device: $1. See valid values in 'fx help set-device'" | 
|  | exit 1 | 
|  | fi | 
|  | export FUCHSIA_DEVICE_NAME="$1" | 
|  | ;; | 
|  | -i) | 
|  | declare iterative=1 | 
|  | ;; | 
|  | -x) | 
|  | export FUCHSIA_DEVSHELL_VERBOSITY=1 | 
|  | ;; | 
|  | -xx) | 
|  | set -x | 
|  | ;; | 
|  | --) | 
|  | shift | 
|  | break | 
|  | ;; | 
|  | help|--help) | 
|  | if [[ $# -lt 2 || "$2" =~ ^\-\-.* ]]; then | 
|  | shift | 
|  | usage "$@" | 
|  | else | 
|  | shift | 
|  | help_command "$@" | 
|  | fi | 
|  | exit 0 | 
|  | ;; | 
|  | -*) | 
|  | usage | 
|  | fx-error "Unknown global argument $1" | 
|  | exit 1 | 
|  | ;; | 
|  | *) | 
|  | break | 
|  | ;; | 
|  | esac | 
|  | shift | 
|  | done | 
|  |  | 
|  | if [[ $# -lt 1 ]]; then | 
|  | usage | 
|  | fx-error "Missing command name" | 
|  | exit 1 | 
|  | fi | 
|  |  | 
|  | command_name="$1" | 
|  | command_path="$(find_executable ${command_name})" | 
|  |  | 
|  | if [[ $? -ne 0 || ! -x "${command_path}" ]]; then | 
|  | if [[ -n "${command_path}" ]]; then | 
|  | _path_message=" in the expected location $(_relative "${command_path}")" | 
|  | fi | 
|  | fx-error "Cannot find executable for ${command_name}${_path_message}."\ | 
|  | "If this is a tool produced by the build, make sure your"\ | 
|  | "\`fx set\` config produces it in the $(_relative "$(get_host_tools_dir)") directory." | 
|  | exit 1 | 
|  | fi | 
|  |  | 
|  | declare -r cmd_and_args="$@" | 
|  | shift # Removes the command name. | 
|  |  | 
|  | if [[ "${command_name}" != "vendor" || $# -lt 2 ]]; then | 
|  | metric_name="${command_name}" | 
|  | else | 
|  | metric_name="vendor/$1/$2" | 
|  | fi | 
|  |  | 
|  | track-command-execution "${metric_name}" "$@" & | 
|  | declare -r start_time="$SECONDS" | 
|  | "${command_path}" "$@" | 
|  | declare -r retval=$? | 
|  | declare -r end_time="$SECONDS" | 
|  | declare -r elapsed_time=$(( 1000 * (end_time - start_time) )) # milliseconds | 
|  |  | 
|  | if [ -z "${iterative}" ]; then | 
|  | track-command-finished "${elapsed_time}" "${retval}" "${command_name}" "$@" & | 
|  | exit ${retval} | 
|  | elif which inotifywait >/dev/null; then | 
|  | monitor_source_changes() { | 
|  | # Watch everything except out/ and files/directories beginning with "." | 
|  | # such as lock files, swap files, .git, etc'. | 
|  | inotifywait -qrme modify \ | 
|  | --exclude "/(\.|lock|compile_commands.json)" \ | 
|  | "${fuchsia_dir}" \ | 
|  | @"${fuchsia_dir}"/out \ | 
|  | @"${fuchsia_dir}"/zircon/public | 
|  | } | 
|  | elif which apt-get >/dev/null; then | 
|  | echo "Missing inotifywait" | 
|  | echo "Try: sudo apt-get install inotify-tools" | 
|  | exit 1 | 
|  | elif which fswatch >/dev/null; then | 
|  | monitor_source_changes() { | 
|  | fswatch --one-per-batch --event=Updated \ | 
|  | -e "${fuchsia_dir}"/out/ \ | 
|  | -e "${fuchsia_dir}"/zircon/public/ \ | 
|  | -e "/\." \ | 
|  | -e "/lock" \ | 
|  | -e "/compile_commands.json" \ | 
|  | . | 
|  | } | 
|  | else | 
|  | echo "Missing fswatch" | 
|  | echo "Try: brew install fswatch" | 
|  | exit 1 | 
|  | fi | 
|  |  | 
|  | monitor_and_run() { | 
|  | local -r event_pipe="$1" | 
|  | local -r display_name="$2" | 
|  | shift 2 | 
|  |  | 
|  | # Explicitly bind $event_pipe to a numbered FD so read behaves consistently | 
|  | # on Linux and Mac shells ("read <$event_pipe" closes $event_pipe after the | 
|  | # first read on Mac bash). | 
|  | exec 3<"${event_pipe}" | 
|  |  | 
|  | while read -u 3; do | 
|  | if [[ "$(uname -s)" != "Darwin" ]]; then | 
|  | # Drain all subsequent events in a batch. | 
|  | # Otherwise when multiple files are changes at once we'd run multiple | 
|  | # times. | 
|  | read -u 3 -d "" -t .01 | 
|  | fi | 
|  | # Allow at most one fx -i invocation per Fuchsia dir at a time. | 
|  | # Otherwise multiple concurrent fx -i invocations can trigger each other | 
|  | # and cause a storm. | 
|  | echo "---------------------------------- fx -i ${display_name} ---------------------------------------" | 
|  | "$@" | 
|  | echo "--- Done!" | 
|  | done | 
|  | } | 
|  |  | 
|  | monitor_and_run <(monitor_source_changes) "${cmd_and_args}" "${command_path}" "$@" |