blob: 67fe1bbbda7d9af7c04ba1bc87377ebbcd7449f8 [file] [log] [blame]
#!/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 {
(fx-build-dir-if-present && echo "${FUCHSIA_BUILD_DIR}")
}
function commands {
local dirs=()
dirs+=("${fuchsia_dir}"/tools/devshell{,/contrib})
local -r build_dir="$(get_build_dir)"
if [[ -n "${build_dir}" ]]; then
dirs+=("${build_dir}/tools")
fi
(
for dir in "${dirs[@]}"; do
for cmd in "${dir}"/*; do
if [[ -x "${cmd}" && -f "${cmd}" ]]; then
basename "${cmd}"
fi
done
done
echo "${buildtools_whitelist}"
) | sort -u
}
function find_command {
local -r cmd=$1
local command_path="${fuchsia_dir}/tools/devshell/${cmd}"
if [[ -x "${command_path}" ]]; then
echo "${command_path}"
return 0
fi
local command_path="${fuchsia_dir}/tools/devshell/contrib/${cmd}"
if [[ -x "${command_path}" ]]; then
echo "${command_path}"
return 0
fi
local -r 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 [--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 Platform Source Tree or the FUCHSIA_DIR
environment variable set to the root of a Platform Source Tree.
commands:
$(help)
optional arguments:
--dir=BUILD_DIR Path to the build directory to use when running COMMAND.
-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 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 $?
while [[ $# -ne 0 ]]; do
case $1 in
--config=*|--dir=*|-d=*)
# 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
;;
-d)
if [[ $# -lt 2 ]]; then
usage
fx-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
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_command ${command_name})"
if [[ $? -ne 0 ]]; then
usage
fx-error "Unknown command ${command_name}"
exit 1
fi
declare -r cmd_and_args="$@"
shift # Removes the command name.
track-command-execution "${command_name}" "$@" &
declare -r start_time="$SECONDS"
"${command_path}" "$@"
declare -r retval=$?
declare -r end_time="$SECONDS"
declare -r ellapsed_time=$(( 1000 * (end_time - start_time) )) # milliseconds
if [ -z "${iterative}" ]; then
track-command-finished "${ellapsed_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}" "$@"