blob: c51c02fe7f3f9126377fcc96b038b92c84ff353a [file] [log] [blame]
#!/bin/bash
# Copyright 2018 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.
### runs source formatters on modified files
## Usage: fx format-code
## [--help] [--dry-run] [--verbose] [--all]
## [--files=FILES,[FILES ...]]
## [--target=GN_TARGET]
## [--git]
##
## --help Prints this message
## --dry-run Stops the program short of running the formatters
## --all Formats all code in the git repo under the current working
## directory.
## --files Allows the user to specify files. Files are comma separated.
## Globs are dealt with by bash; fx format-code "--files=foo/*" will
## work as expected.
## --target Allows the user to specify a gn target.
## --git The default; it uses `git diff-index` against the newest parent
## commit in the upstream branch (or against HEAD if no such commit
## is found). Files that are locally modified, staged or touched by
## any commits introduced on the local branch are formatted.
## --remove-ordinals (temporary)
## Removes ordinals from FIDL files. This option will be removed
## when ordinals are no longer legal in FIDL files.
set -e
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"/lib/vars.sh || exit $?
function usage() {
fx-command-help
}
function zap-commas() {
echo $1 | tr ',' '\n'
}
function get-diff-base() {
local upstream=$(git rev-parse --abbrev-ref --symbolic-full-name "@{u}" 2>/dev/null)
if [[ -z "${upstream}" ]]; then
upstream="origin/master"
fi
local local_commit=$(git rev-list HEAD ^${upstream} -- 2>/dev/null | tail -1)
if [[ -z "${local_commit}" ]]; then
printf "HEAD"
else
git rev-parse "${local_commit}"^
fi
}
function format-cmd() {
if [ -f $1 ]; then
case $1 in
*.cc) printf "${CLANG_CMD}" ;;
*.cmx) printf "${JSON_FMT_CMD}" ;;
*.cpp) printf "${CLANG_CMD}" ;;
*.dart) printf "${DART_CMD}" ;;
*.fidl) printf "${FIDL_CMD}" ;;
*.gn) printf "${GN_CMD}" ;;
*.gni) printf "${GN_CMD}" ;;
*.go) printf "${GO_CMD}" ;;
*.h) printf "${CLANG_CMD}" ;;
*.hh) printf "${CLANG_CMD}" ;;
*.hpp) printf "${CLANG_CMD}" ;;
*.ts) printf "${CLANG_CMD}" ;;
*.rs) printf "${RUST_FMT_CMD}" ;;
esac
fi
}
function hg-cmd() {
[[ $1 =~ .*\.h ]] && printf "${FIX_HEADER_GUARDS_CMD}"
}
# Removes leading //, resolves to absolute path, and resolves globs. The first
# argument is a path prefix, and the remaining arguments are relative to that
# path prefix.
function canonicalize() {
local root_dir="$1"
shift
for fileglob in "${@}"; do
# // means it comes from gn, [^/]* means it is relative
if [[ "${fileglob}" = //* || "${fileglob}" = [^/]* ]]; then
local dir="${root_dir}"/
else
local dir=""
fi
for file in "${dir}"${fileglob#"//"}; do
echo "${file}"
done
done
}
DRY_RUN=
VERBOSE=
REMOVE_ORDINALS=
fx-config-read
for ARG in "$@"; do
case "${ARG}" in
--verbose) VERBOSE="1" ;;
--dry-run) DRY_RUN="1" ;;
--all) FILES=$(canonicalize "${PWD}" $(git ls-files)) ;;
--git) ;; # We'll figure this out later
--files=*) FILES=$(canonicalize "${PWD}" $(zap-commas "${ARG#--files=}")) ;;
--target=*) FILES=$(canonicalize "${FUCHSIA_DIR}" \
$(fx-buildtool-run gn desc \
"${FUCHSIA_OUT_DIR}/${FUCHSIA_ARCH}" "${ARG#--target=}" sources)) &&
RUST_ENTRY_POINT=$(canonicalize "${FUCHSIA_DIR}" \
$(fx rustfmt --print-sources ${ARG#--target=})) ;;
# TODO(FIDL-372): Remove support for --remove-ordinals
--remove-ordinals) REMOVE_ORDINALS="1" ;;
--help) usage && exit 1 ;;
*) usage && printf "Unknown flag %s\n" "${ARG}" && exit 1 ;;
esac
done
if [ -z "${FILES+x}" ]; then
FILES=$(canonicalize $(git rev-parse --show-toplevel) \
$(git diff-index --name-only $(get-diff-base)))
fi
if [[ -n "${VERBOSE}" ]]; then
printf "Files to be formatted:\n%s\n" "${FILES}"
fi
declare BUILDTOOLS_ROOT="${FUCHSIA_DIR}"/buildtools
declare HOST_OS=$(uname | tr '[:upper:]' '[:lower:]')
[[ "${HOST_OS}" == "darwin" ]] && HOST_OS="mac"
case $(uname -m) in
x86_64) HOST_ARCH="x64" ;;
aarch64) HOST_ARCH="arm64" ;;
*) echo "Unknown arch $(uname -m)" && exit 1 ;;
esac
declare HOST_PLATFORM="${HOST_OS}-${HOST_ARCH}"
declare CLANG_CMD="${BUILDTOOLS_ROOT}/${HOST_PLATFORM}/clang/bin/clang-format -style=file -fallback-style=Google -sort-includes -i"
declare DART_CMD="${FUCHSIA_DIR}/topaz/tools/prebuilt-dart-sdk/${HOST_PLATFORM}/bin/dartfmt -w"
declare FIDL_BIN="${ZIRCON_TOOLS_DIR}"/fidl-format
if [[ -z "${REMOVE_ORDINALS}" ]]; then
declare FIDL_CMD="${FIDL_BIN} -i"
else
declare FIDL_CMD="${FIDL_BIN} -i --remove-ordinals"
fi
declare GN_CMD="${BUILDTOOLS_ROOT}/gn format"
declare GO_CMD="${BUILDTOOLS_ROOT}/${HOST_PLATFORM}/go/bin/gofmt -w"
declare JSON_FMT_CMD="${FUCHSIA_DIR}"/scripts/style/json-fmt.py
declare RUST_FMT_CMD="${BUILDTOOLS_ROOT}/${HOST_PLATFORM}/rust/bin/rustfmt --config-path=${FUCHSIA_DIR}/garnet/rustfmt.toml --unstable-features --skip-children"
declare RUST_ENTRY_POINT_FMT_CMD="${BUILDTOOLS_ROOT}/${HOST_PLATFORM}/rust/bin/rustfmt --config-path=${FUCHSIA_DIR}/garnet/rustfmt.toml"
declare FIX_HEADER_GUARDS_CMD="${FUCHSIA_DIR}/scripts/style/check-header-guards.py --fix"
# If there is a FIDL file to fix, and we don't have a copy of fidl-format,
# generate one.
for file in ${FILES}; do
if [[ ${file} =~ .*\.fidl ]]; then
if [[ ! -x "${FIDL_BIN}" ]]; then
printf "fidl-format not found: attempting to build it\n"
fx-command-run build-zircon -l
break
fi
fi
done
[[ -n "${DRY_RUN}" ]] && exit
[[ -n "${RUST_ENTRY_POINT}" ]] && ${RUST_ENTRY_POINT_FMT_CMD} "${RUST_ENTRY_POINT}"
for file in ${FILES}; do
# Git reports deleted files, which we don't want to try to format
[[ ! -f "${file}" ]] && continue
# Format the file
declare fcmd=$(format-cmd ${file})
[[ -n "${fcmd}" ]] && ${fcmd} "${file}"
declare hgcmd=$(hg-cmd ${file})
[[ -n "${hgcmd}" ]] && ${hgcmd} "${file}"
done