| #!/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 get_build_dir { | |
| (source "${vars_sh}" && fx-config-read-if-present && echo "${FUCHSIA_BUILD_DIR}") | |
| } | |
| function commands { | |
| local cmds="$(ls "${fuchsia_dir}/scripts/devshell" | grep -v -e '^lib$' -e '^tests$')" | |
| local newline=$'\n' | |
| local build_dir=$(get_build_dir) | |
| if [[ -n "${build_dir}" ]]; then | |
| cmds="${cmds}${newline}$(ls "${build_dir}/tools" 2>/dev/null)" | |
| fi | |
| for tool in ${buildtools_whitelist}; do | |
| cmds="${cmds}${newline}${tool}" | |
| done | |
| echo "$(echo "${cmds}" | sort)" | |
| } | |
| function find_command { | |
| local cmd=$1 | |
| local command_path="${fuchsia_dir}/scripts/devshell/${cmd}" | |
| if [[ -x "${command_path}" ]]; then | |
| echo "${command_path}" | |
| return 0 | |
| fi | |
| local build_dir=$(get_build_dir) | |
| if [[ -n "${build_dir}" ]]; then | |
| local command_path="${build_dir}/tools/${cmd}" | |
| if [[ -x "${command_path}" ]]; then | |
| echo "${command_path}" | |
| return 0 | |
| fi | |
| fi | |
| for tool in ${buildtools_whitelist}; do | |
| if [[ "$cmd" != "$tool" ]]; then | |
| continue | |
| fi | |
| local command_path="${fuchsia_dir}/buildtools/${tool}" | |
| if [[ -x "${command_path}" ]]; then | |
| echo "${command_path}" | |
| return 0 | |
| fi | |
| done | |
| return 1 | |
| } | |
| function help { | |
| local cmd="$1" | |
| if [[ -z "${cmd}" ]]; then | |
| for cmd in $(commands); do | |
| local cmd_path="$(find_command "${cmd}")" | |
| if [[ $(file -b --mime "${cmd_path}" | cut -d / -f 1) == "text" ]]; then | |
| echo "${cmd} | $(sed -n '1,/^###/s/^### //p' < "${cmd_path}")" | |
| else | |
| echo "${cmd}" | |
| fi | |
| done | column -t -s '|' -c 2 | |
| else | |
| local cmd_path="$(find_command "${cmd}")" | |
| if [[ -z "$cmd_path" ]]; then | |
| echo "Command not found" | |
| elif [[ $(file -b --mime "${cmd_path}" | cut -d / -f 1) == "text" ]]; then | |
| fx-print-command-help "${cmd_path}" | |
| else | |
| echo "No help found. Try \`fx ${cmd} -h\`" | |
| fi | |
| fi | |
| } | |
| function usage { | |
| cat <<END | |
| usage: fx [--config CONFIG_FILE | --dir BUILD_DIR] [-d DEVICE_NAME] [-i] [-x] COMMAND [...] | |
| Run Fuchsia development commands. Must be run with either a current working | |
| directory that is contained in a Fuchsia source tree or the FUCHSIA_DIR | |
| environment variable set to the root of a Fuchsia source tree. | |
| commands: | |
| $(help) | |
| optional arguments: | |
| --config=CONFIG_FILE Path to the config file use when running COMMAND. | |
| Defaults to FUCHSIA_CONFIG if set in the | |
| environment and //.config otherwise. The config | |
| file determines which build directory (and therefore | |
| build configuration) is used by COMMAND. | |
| --dir=BUILD_DIR Path to the build directory to use when running COMMAND. | |
| If specified, FILE is ignored. | |
| -d=DEVICE_NAME Target a specific device. DEVICE_NAME may be a Fuchsia | |
| device name. Note: "fx set-device" can be used to set a | |
| default DEVICE_NAME 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. | |
| optional shell extensions: | |
| fx-go | |
| fx-update-path | |
| fx-set-prompt | |
| To use these shell extensions, first source fx-env.sh into your shell: | |
| $ source scripts/fx-env.sh | |
| END | |
| } | |
| buildtools_whitelist="gn ninja" | |
| 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 Fuchsia 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 >& 2 "error: Cannot find Fuchsia source tree containing $(pwd)" | |
| exit 1 | |
| fi | |
| done | |
| fi | |
| declare -r vars_sh="${fuchsia_dir}/scripts/devshell/lib/vars.sh" | |
| source "${vars_sh}" || exit $? | |
| while [[ $# -ne 0 ]]; do | |
| case $1 in | |
| --config=*|--dir=*|-d=*) | |
| # Turn --switch=value into --switch value. | |
| arg="$1" | |
| shift | |
| set -- "${arg%%=*}" "${arg#*=}" "$@" | |
| continue | |
| ;; | |
| --config) | |
| if [[ $# -lt 2 ]]; then | |
| usage | |
| echo >&2 "ERROR: Missing path to config file for --config argument" | |
| exit 1 | |
| fi | |
| shift # Removes --config. | |
| export FUCHSIA_CONFIG="$1" | |
| ;; | |
| --dir) | |
| if [[ $# -lt 2 ]]; then | |
| usage | |
| echo >&2 "ERROR: Missing path to build directory for --dir argument" | |
| exit 1 | |
| fi | |
| shift # Removes --dir. | |
| export FUCHSIA_BUILD_DIR="$1" | |
| if [[ "$FUCHSIA_BUILD_DIR" == //* ]]; then | |
| FUCHSIA_BUILD_DIR="${fuchsia_dir}/${FUCHSIA_BUILD_DIR#//}" | |
| fi | |
| if [[ ! -d "$FUCHSIA_BUILD_DIR" ]]; then | |
| echo >&2 "ERROR: Build directory $FUCHSIA_BUILD_DIR does not exist" | |
| exit 1 | |
| fi | |
| # This tells fx-config-read not to use the file. | |
| export FUCHSIA_CONFIG=- | |
| ;; | |
| -d) | |
| if [[ $# -lt 2 ]]; then | |
| usage | |
| echo >&2 "ERROR: Missing device name for -d argument" | |
| exit 1 | |
| fi | |
| shift # removes -d | |
| export FUCHSIA_DEVICE_NAME="$1" | |
| ;; | |
| -i) | |
| declare iterative=1 | |
| ;; | |
| -x) | |
| export FUCHSIA_DEVSHELL_VERBOSITY=1 | |
| ;; | |
| --) | |
| shift | |
| break | |
| ;; | |
| help) | |
| if [[ $# -gt 1 ]]; then | |
| shift | |
| help "$@" | |
| exit | |
| else | |
| usage | |
| exit 1 | |
| fi | |
| ;; | |
| -*) | |
| usage | |
| echo >& 2 "error: Unknown global argument $1" | |
| exit 1 | |
| ;; | |
| *) | |
| break | |
| ;; | |
| esac | |
| shift | |
| done | |
| if [[ $# -lt 1 ]]; then | |
| usage | |
| echo >& 2 "error: Missing command name" | |
| exit 1 | |
| fi | |
| command_name="$1" | |
| command_path="$(find_command ${command_name})" | |
| if [[ $? -ne 0 ]]; then | |
| usage | |
| echo >& 2 "error: Unknown command ${command_name}" | |
| exit 1 | |
| fi | |
| declare -r cmd_and_args="$@" | |
| shift # Removes the command name. | |
| "${command_path}" "$@" | |
| declare -r retval=$? | |
| if [ -z "${iterative}" ]; then | |
| exit ${retval} | |
| elif which inotifywait >/dev/null; then | |
| # 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 | while read; do | |
| # Drain all subsequent events in a batch. | |
| # Otherwise when multiple files are changes at once we'd run multiple | |
| # times. | |
| read -d "" -t .01 | |
| # 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 ${cmd_and_args} ---------------------------------------" | |
| "${command_path}" "$@" | |
| echo "--- Done!" | |
| done | |
| elif which apt-get >/dev/null; then | |
| echo "Missing inotifywait" | |
| echo "Try: sudo apt-get install inotify-tools" | |
| elif which fswatch >/dev/null; then | |
| fswatch --one-per-batch --event=Updated -e "${fuchsia_dir}"/out/ -e "${fuchsia_dir}"/zircon/public/ -e "/\." -e "lock" -e "/compile_commands.json" . | while read; do | |
| echo "---------------------------------- fx -i ${cmd_and_args} ---------------------------------------" | |
| "${command_path}" "$@" | |
| echo "--- Done!" | |
| done | |
| else | |
| echo "Missing fswatch" | |
| echo "Try: brew install fswatch" | |
| fi |