blob: ed870c2cd15d31fb14791b6f37520f57bb56f6cc [file] [log] [blame]
Damien Martin-Guillerez5f9c6ba2015-04-09 21:10:33 +00001# -*- sh -*- (Bash only)
2#
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00003# Copyright 2015 The Bazel Authors. All rights reserved.
Damien Martin-Guillerez5f9c6ba2015-04-09 21:10:33 +00004#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
nharmata1694de62018-11-12 09:22:23 -080016
Damien Martin-Guillerez5f9c6ba2015-04-09 21:10:33 +000017# The template is expanded at build time using tables of commands/options
18# derived from the bazel executable built in the same client; the expansion is
19# written to bazel-complete.bash.
20#
nharmata1694de62018-11-12 09:22:23 -080021# Don't use this script directly. Generate the final script with
22# bazel build //scripts:bash_completion instead.
23
24# This script expects a header to be prepended to it that defines the following
25# nullary functions:
26#
27# _bazel_completion_use_query - Has a successful exit code if
28# BAZEL_COMPLETION_USE_QUERY is "true".
29#
30# _bazel_completion_allow_tests_for_run - Has a successful exit code if
31# BAZEL_COMPLETION_ALLOW_TESTS_FOR_RUN is "true".
Damien Martin-Guillerez5f9c6ba2015-04-09 21:10:33 +000032
33# The package path used by the completion routines. Unfortunately
34# this isn't necessarily the same as the actual package path used by
35# Bazel, but that's ok. (It's impossible for us to reliably know what
36# the relevant package-path, so this is just a good guess. Users can
37# override it if they want.)
Damien Martin-Guillerez5f9c6ba2015-04-09 21:10:33 +000038: ${BAZEL_COMPLETION_PACKAGE_PATH:=%workspace%}
39
Damien Martin-Guillerez5f9c6ba2015-04-09 21:10:33 +000040# Some commands might interfer with the important one, so don't complete them
41: ${BAZEL_IGNORED_COMMAND_REGEX:="__none__"}
42
Martin Probstf84650c2017-11-29 02:09:42 -080043# bazel & ibazel commands
Damien Martin-Guillerez5f9c6ba2015-04-09 21:10:33 +000044: ${BAZEL:=bazel}
Martin Probstf84650c2017-11-29 02:09:42 -080045: ${IBAZEL:=ibazel}
Damien Martin-Guillerez5f9c6ba2015-04-09 21:10:33 +000046
47# Pattern to match for looking for a target
48# BAZEL_BUILD_MATCH_PATTERN__* give the pattern for label-*
Benjamin Petersondac096c2019-03-22 10:29:40 -070049# when looking in the build file.
Damien Martin-Guillerez5f9c6ba2015-04-09 21:10:33 +000050# BAZEL_QUERY_MATCH_PATTERN__* give the pattern for label-*
51# when using 'bazel query'.
nharmata1694de62018-11-12 09:22:23 -080052# _RUNTEST is a special case for _bazel_completion_allow_tests_for_run.
Damien Martin-Guillerez5f9c6ba2015-04-09 21:10:33 +000053: ${BAZEL_BUILD_MATCH_PATTERN__test:='(.*_test|test_suite)'}
54: ${BAZEL_QUERY_MATCH_PATTERN__test:='(test|test_suite)'}
55: ${BAZEL_BUILD_MATCH_PATTERN__bin:='.*_binary'}
56: ${BAZEL_QUERY_MATCH_PATTERN__bin:='(binary)'}
57: ${BAZEL_BUILD_MATCH_PATTERN_RUNTEST__bin:='(.*_(binary|test)|test_suite)'}
58: ${BAZEL_QUERY_MATCH_PATTERN_RUNTEST__bin:='(binary|test)'}
59: ${BAZEL_BUILD_MATCH_PATTERN__:='.*'}
60: ${BAZEL_QUERY_MATCH_PATTERN__:=''}
61
62# Usage: _bazel__get_rule_match_pattern <command>
63# Determine what kind of rules to match, based on command.
64_bazel__get_rule_match_pattern() {
65 local var_name pattern
nharmata1694de62018-11-12 09:22:23 -080066 if _bazel_completion_use_query; then
Damien Martin-Guillerez5f9c6ba2015-04-09 21:10:33 +000067 var_name="BAZEL_QUERY_MATCH_PATTERN"
68 else
69 var_name="BAZEL_BUILD_MATCH_PATTERN"
70 fi
71 if [[ "$1" =~ ^label-?([a-z]*)$ ]]; then
72 pattern=${BASH_REMATCH[1]:-}
nharmata1694de62018-11-12 09:22:23 -080073 if _bazel_completion_allow_tests_for_run; then
Damien Martin-Guillerez5f9c6ba2015-04-09 21:10:33 +000074 eval "echo \"\${${var_name}_RUNTEST__${pattern}:-\$${var_name}__${pattern}}\""
75 else
76 eval "echo \"\$${var_name}__${pattern}\""
77 fi
78 fi
79}
80
81# Compute workspace directory. Search for the innermost
82# enclosing directory with a WORKSPACE file.
83_bazel__get_workspace_path() {
84 local workspace=$PWD
85 while true; do
86 if [ -f "${workspace}/WORKSPACE" ]; then
87 break
88 elif [ -z "$workspace" -o "$workspace" = "/" ]; then
89 workspace=$PWD
90 break;
91 fi
92 workspace=${workspace%/*}
93 done
94 echo $workspace
95}
96
97
98# Find the current piece of the line to complete, but only do word breaks at
99# certain characters. In particular, ignore these: "':=
100# This method also takes into account the current cursor position.
101#
102# Works with both bash 3 and 4! Bash 3 and 4 perform different word breaks when
103# computing the COMP_WORDS array. We need this here because Bazel options are of
104# the form --a=b, and labels of the form //some/label:target.
105_bazel__get_cword() {
106 local cur=${COMP_LINE:0:$COMP_POINT}
107 # This expression finds the last word break character, as defined in the
108 # COMP_WORDBREAKS variable, but without '=' or ':', which is not preceeded by
109 # a slash. Quote characters are also excluded.
110 local wordbreaks="$COMP_WORDBREAKS"
111 wordbreaks="${wordbreaks//\'/}"
112 wordbreaks="${wordbreaks//\"/}"
113 wordbreaks="${wordbreaks//:/}"
114 wordbreaks="${wordbreaks//=/}"
115 local word_start=$(expr "$cur" : '.*[^\]['"${wordbreaks}"']')
116 echo "${cur:$word_start}"
117}
118
119
120# Usage: _bazel__package_path <workspace> <displacement>
121#
122# Prints a list of package-path root directories, displaced using the
123# current displacement from the workspace. All elements have a
124# trailing slash.
125_bazel__package_path() {
126 local workspace=$1 displacement=$2 root
127 IFS=:
128 for root in ${BAZEL_COMPLETION_PACKAGE_PATH//\%workspace\%/$workspace}; do
129 unset IFS
130 echo "$root/$displacement"
131 done
132}
133
134# Usage: _bazel__options_for <command>
135#
136# Prints the set of options for a given Bazel command, e.g. "build".
137_bazel__options_for() {
138 local options
139 if [[ "${BAZEL_COMMAND_LIST}" =~ ^(.* )?$1( .*)?$ ]]; then
Philip Patsch27be7092018-08-28 02:30:51 -0700140 # assumes option names only use ASCII characters
141 local option_name=$(echo $1 | tr a-z A-Z | tr "-" "_")
Damien Martin-Guillerez5f9c6ba2015-04-09 21:10:33 +0000142 eval "echo \${BAZEL_COMMAND_${option_name}_FLAGS}" | tr " " "\n"
143 fi
144}
145# Usage: _bazel__expansion_for <command>
146#
147# Prints the completion pattern for a given Bazel command, e.g. "build".
148_bazel__expansion_for() {
149 local options
150 if [[ "${BAZEL_COMMAND_LIST}" =~ ^(.* )?$1( .*)?$ ]]; then
Philip Patsch27be7092018-08-28 02:30:51 -0700151 # assumes option names only use ASCII characters
152 local option_name=$(echo $1 | tr a-z A-Z | tr "-" "_")
Damien Martin-Guillerez5f9c6ba2015-04-09 21:10:33 +0000153 eval "echo \${BAZEL_COMMAND_${option_name}_ARGUMENT}"
154 fi
155}
156
157# Usage: _bazel__matching_targets <kind> <prefix>
158#
159# Prints target names of kind <kind> and starting with <prefix> in the BUILD
160# file given as standard input. <kind> is a basic regex (BRE) used to match the
161# bazel rule kind and <prefix> is the prefix of the target name.
162_bazel__matching_targets() {
163 local kind_pattern="$1"
164 local target_prefix="$2"
165 # The following commands do respectively:
166 # Remove BUILD file comments
167 # Replace \n by spaces to have the BUILD file in a single line
168 # Extract all rule types and target names
169 # Grep the kind pattern and the target prefix
170 # Returns the target name
171 sed 's/#.*$//' \
172 | tr "\n" " " \
173 | sed 's/\([a-zA-Z0-9_]*\) *(\([^)]* \)\{0,1\}name *= *['\''"]\([a-zA-Z0-9_/.+=,@~-]*\)['\''"][^)]*)/\
174type:\1 name:\3\
175/g' \
Googler86a40472016-09-06 18:01:11 +0000176 | "grep" -E "^type:$kind_pattern name:$target_prefix" \
Damien Martin-Guillerez5f9c6ba2015-04-09 21:10:33 +0000177 | cut -d ':' -f 3
178}
179
180
181# Usage: _bazel__is_true <string>
182#
183# Returns true or false based on the input string. The following are
184# valid true values (the rest are false): "1", "true".
185_bazel__is_true() {
186 local str="$1"
187 [[ "$str" == "1" || "$str" == "true" ]]
188}
189
190# Usage: _bazel__expand_rules_in_package <workspace> <displacement>
191# <current> <label-type>
192#
193# Expands rules in specified packages, exploring all roots of
194# $BAZEL_COMPLETION_PACKAGE_PATH, not just $(pwd). Only rules
195# appropriate to the command are printed. Sets $COMPREPLY array to
196# result.
197#
nharmata1694de62018-11-12 09:22:23 -0800198# If _bazel_completion_use_query has a successful exit code, 'bazel query' is
199# used instead, with the actual Bazel package path;
200# $BAZEL_COMPLETION_PACKAGE_PATH is ignored in this case, since the actual Bazel
201# value is likely to be more accurate.
Damien Martin-Guillerez5f9c6ba2015-04-09 21:10:33 +0000202_bazel__expand_rules_in_package() {
203 local workspace=$1 displacement=$2 current=$3 label_type=$4
204 local package_name=$(echo "$current" | cut -f1 -d:)
205 local rule_prefix=$(echo "$current" | cut -f2 -d:)
206 local root buildfile rule_pattern r result
207
208 result=
209 pattern=$(_bazel__get_rule_match_pattern "$label_type")
nharmata1694de62018-11-12 09:22:23 -0800210 if _bazel_completion_use_query; then
Damien Martin-Guillerez5f9c6ba2015-04-09 21:10:33 +0000211 package_name=$(echo "$package_name" | tr -d "'\"") # remove quotes
212 result=$(${BAZEL} --output_base=/tmp/${BAZEL}-completion-$USER query \
Googler29765762019-03-06 01:41:43 -0800213 --keep_going --noshow_progress --output=label \
Damien Martin-Guillerez5f9c6ba2015-04-09 21:10:33 +0000214 "kind('$pattern rule', '$package_name:*')" 2>/dev/null |
Googler86a40472016-09-06 18:01:11 +0000215 cut -f2 -d: | "grep" "^$rule_prefix")
Damien Martin-Guillerez5f9c6ba2015-04-09 21:10:33 +0000216 else
217 for root in $(_bazel__package_path "$workspace" "$displacement"); do
Ian Cottrella7c54722017-08-24 11:20:43 +0200218 buildfile="$root/$package_name/BUILD.bazel"
219 if [ ! -f "$buildfile" ]; then
220 buildfile="$root/$package_name/BUILD"
221 fi
Damien Martin-Guillerez5f9c6ba2015-04-09 21:10:33 +0000222 if [ -f "$buildfile" ]; then
223 result=$(_bazel__matching_targets \
224 "$pattern" "$rule_prefix" <"$buildfile")
225 break
226 fi
227 done
228 fi
229
230 index=$(echo $result | wc -w)
231 if [ -n "$result" ]; then
232 echo "$result" | tr " " "\n" | sed 's|$| |'
233 fi
234 # Include ":all" wildcard if there was no unique match. (The zero
235 # case is tricky: we need to include "all" in that case since
236 # otherwise we won't expand "a" to "all" in the absence of rules
237 # starting with "a".)
238 if [ $index -ne 1 ] && expr all : "\\($rule_prefix\\)" >/dev/null; then
239 echo "all "
240 fi
241}
242
243# Usage: _bazel__expand_package_name <workspace> <displacement> <current-word>
244# <label-type>
245#
246# Expands directories, but explores all roots of
247# BAZEL_COMPLETION_PACKAGE_PATH, not just $(pwd). When a directory is
248# a bazel package, the completion offers "pkg:" so you can expand
249# inside the package.
250# Sets $COMPREPLY array to result.
251_bazel__expand_package_name() {
252 local workspace=$1 displacement=$2 current=$3 type=${4:-} root dir index
253 for root in $(_bazel__package_path "$workspace" "$displacement"); do
254 found=0
255 for dir in $(compgen -d $root$current); do
256 [ -L "$dir" ] && continue # skip symlinks (e.g. bazel-bin)
257 [[ "$dir" =~ ^(.*/)?\.[^/]*$ ]] && continue # skip dotted dir (e.g. .git)
258 found=1
259 echo "${dir#$root}/"
Ian Cottrella7c54722017-08-24 11:20:43 +0200260 if [ -f $dir/BUILD.bazel -o -f $dir/BUILD ]; then
Damien Martin-Guillerez5f9c6ba2015-04-09 21:10:33 +0000261 if [ "${type}" = "label-package" ]; then
262 echo "${dir#$root} "
263 else
264 echo "${dir#$root}:"
265 fi
266 fi
267 done
268 [ $found -gt 0 ] && break # Stop searching package path upon first match.
269 done
270}
271
272# Usage: _bazel__expand_target_pattern <workspace> <displacement>
273# <word> <label-syntax>
274#
275# Expands "word" to match target patterns, using the current workspace
276# and displacement from it. "command" is used to filter rules.
277# Sets $COMPREPLY array to result.
278_bazel__expand_target_pattern() {
279 local workspace=$1 displacement=$2 current=$3 label_syntax=$4
280 case "$current" in
281 //*:*) # Expand rule names within package, no displacement.
282 if [ "${label_syntax}" = "label-package" ]; then
283 compgen -S " " -W "BUILD" "$(echo current | cut -f ':' -d2)"
284 else
285 _bazel__expand_rules_in_package "$workspace" "" "$current" "$label_syntax"
286 fi
287 ;;
288 *:*) # Expand rule names within package, displaced.
289 if [ "${label_syntax}" = "label-package" ]; then
290 compgen -S " " -W "BUILD" "$(echo current | cut -f ':' -d2)"
291 else
292 _bazel__expand_rules_in_package \
293 "$workspace" "$displacement" "$current" "$label_syntax"
294 fi
295 ;;
296 //*) # Expand filenames using package-path, no displacement
297 _bazel__expand_package_name "$workspace" "" "$current" "$label_syntax"
298 ;;
299 *) # Expand filenames using package-path, displaced.
300 if [ -n "$current" ]; then
301 _bazel__expand_package_name "$workspace" "$displacement" "$current" "$label_syntax"
302 fi
303 ;;
304 esac
305}
306
307_bazel__get_command() {
308 for word in "${COMP_WORDS[@]:1:COMP_CWORD-1}"; do
Googler86a40472016-09-06 18:01:11 +0000309 if echo "$BAZEL_COMMAND_LIST" | "grep" -wsq -e "$word"; then
Damien Martin-Guillerez5f9c6ba2015-04-09 21:10:33 +0000310 echo $word
311 break
312 fi
313 done
314}
315
316# Returns the displacement to the workspace given in $1
317_bazel__get_displacement() {
318 if [[ "$PWD" =~ ^$1/.*$ ]]; then
319 echo ${PWD##$1/}/
320 fi
321}
322
323
324# Usage: _bazel__complete_pattern <workspace> <displacement> <current>
325# <type>
326#
327# Expand a word according to a type. The currently supported types are:
328# - {a,b,c}: an enum that can take value a, b or c
329# - label: a label of any kind
330# - label-bin: a label to a runnable rule (basically to a _binary rule)
331# - label-test: a label to a test rule
332# - info-key: an info key as listed by `bazel help info-keys`
333# - command: the name of a command
334# - path: a file path
335# - combinaison of previous type using | as separator
336_bazel__complete_pattern() {
337 local workspace=$1 displacement=$2 current=$3 types=$4
338 for type in $(echo $types | tr "|" "\n"); do
339 case "$type" in
340 label*)
341 _bazel__expand_target_pattern "$workspace" "$displacement" \
342 "$current" "$type"
343 ;;
344 info-key)
345 compgen -S " " -W "${BAZEL_INFO_KEYS}" -- "$current"
346 ;;
347 "command")
348 local commands=$(echo "${BAZEL_COMMAND_LIST}" \
Googler86a40472016-09-06 18:01:11 +0000349 | tr " " "\n" | "grep" -v "^${BAZEL_IGNORED_COMMAND_REGEX}$")
Damien Martin-Guillerez5f9c6ba2015-04-09 21:10:33 +0000350 compgen -S " " -W "${commands}" -- "$current"
351 ;;
352 path)
Yuval055c93d2021-03-16 02:18:53 -0700353 for file in $(compgen -f -- "$current"); do
354 if [[ -d "$file" ]]; then
355 echo "$file/"
356 else
357 echo "$file "
358 fi
359 done
Damien Martin-Guillerez5f9c6ba2015-04-09 21:10:33 +0000360 ;;
361 *)
362 compgen -S " " -W "$type" -- "$current"
363 ;;
364 esac
365 done
366}
367
368# Usage: _bazel__expand_options <workspace> <displacement> <current-word>
369# <options>
370#
371# Expands options, making sure that if current-word contains an equals sign,
372# it is handled appropriately.
373_bazel__expand_options() {
374 local workspace="$1" displacement="$2" cur="$3" options="$4"
375 if [[ $cur =~ = ]]; then
376 # also expands special labels
377 current=$(echo "$cur" | cut -f2 -d=)
378 _bazel__complete_pattern "$workspace" "$displacement" "$current" \
379 "$(compgen -W "$options" -- "$cur" | cut -f2 -d=)" \
380 | sort -u
381 else
382 compgen -W "$(echo "$options" | sed 's|=.*$|=|')" -- "$cur" \
383 | sed 's|\([^=]\)$|\1 |'
384 fi
385}
386
Eric Klein2b708fb2020-02-06 13:23:25 -0800387# Usage: _bazel__abspath <file>
388#
389#
390# Returns the absolute path to a file
391_bazel__abspath() {
392 echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"
393 }
394
395# Usage: _bazel__rc_imports <workspace> <rc-file>
396#
397#
398# Returns the list of other RC imported (or try-imported) by a given RC file
399# Only return files we can actually find, and only return absolute paths
400_bazel__rc_imports() {
401 local workspace="$1" rc_dir rc_file="$2" import_files
402 rc_dir=$(dirname $rc_file)
403 import_files=$(cat $rc_file \
404 | sed 's/#.*//' \
405 | sed -E "/^(try-){0,1}import/!d" \
406 | sed -E "s/^(try-){0,1}import ([^ ]*).*$/\2/" \
407 | sort -u)
408
409 local confirmed_import_files=()
410 for import in $import_files; do
411 # rc imports can use %workspace% to refer to the workspace, and we need to substitute that here
412 import=${import//\%workspace\%/$workspace}
413 if [[ "${import:0:1}" != "/" ]] ; then
414 import="$rc_dir/$import"
415 fi
416 import=$(_bazel__abspath $import)
417 # Don't bother dealing with it further if we can't find it
418 if [ -f "$import" ] ; then
419 confirmed_import_files+=($import)
420 fi
421 done
422 echo "${confirmed_import_files[@]}"
423}
424
425# Usage: _bazel__rc_expand_imports <workspace> <processed-rc-files ...> __new__ <new-rc-files ...>
426#
427#
428# Function that receives a workspace and two lists. The first list is a list of RC files that have
429# already been processed, and the second list contains new RC files that need processing. Each new file will be
430# processed for "{try-}import" lines to discover more RC files that need parsing.
431# Any lines we find in "{try-}import" will be checked against known files (and not processed again if known).
432_bazel__rc_expand_imports() {
433 local workspace="$1" rc_file new_found="no" processed_files=() to_process_files=() discovered_files=()
434 # We've consumed workspace
435 shift
436 # Now grab everything else
437 local all_files=($@)
438 for rc_file in ${all_files[@]} ; do
439 if [ "$rc_file" == "__new__" ] ; then
440 new_found="yes"
441 continue
442 elif [ "$new_found" == "no" ] ; then
443 processed_files+=($rc_file)
444 else
445 to_process_files+=($rc_file)
446 fi
447 done
448
449 # For all the non-processed files, get the list of imports out of each of those files
450 for rc_file in "${to_process_files[@]}"; do
451 local potential_new_files+=($(_bazel__rc_imports "$workspace" "$rc_file"))
452 processed_files+=($rc_file)
453 for potential_new_file in ${potential_new_files[@]} ; do
454 if [[ ! " ${processed_files[@]} " =~ " ${potential_new_file} " ]] ; then
455 discovered_files+=($potential_new_file)
456 fi
457 done
458 done
459
460 # Finally, return two lists (separated by __new__) of the files that have been fully processed, and
461 # the files that need processing.
462 echo "${processed_files[@]}" "__new__" "${discovered_files[@]}"
463}
464
465# Usage: _bazel__rc_files <workspace>
466#
467#
468# Returns the list of RC files to examine, given the current command-line args.
469_bazel__rc_files() {
470 local workspace="$1" new_files=() processed_files=()
471 # Handle the workspace RC unless --noworkspace_rc says not to.
472 if [[ ! "${COMP_LINE}" =~ "--noworkspace_rc" ]]; then
473 local workspacerc="$workspace/.bazelrc"
474 if [ -f "$workspacerc" ] ; then
475 new_files+=($(_bazel__abspath $workspacerc))
476 fi
477 fi
478 # Handle the $HOME RC unless --nohome_rc says not to.
479 if [[ ! "${COMP_LINE}" =~ "--nohome_rc" ]]; then
480 local homerc="$HOME/.bazelrc"
481 if [ -f "$homerc" ] ; then
482 new_files+=($(_bazel__abspath $homerc))
483 fi
484 fi
485 # Handle the system level RC unless --nosystem_rc says not to.
486 if [[ ! "${COMP_LINE}" =~ "--nosystem_rc" ]]; then
487 local systemrc="/etc/bazel.bazelrc"
488 if [ -f "$systemrc" ] ; then
489 new_files+=($(_bazel__abspath $systemrc))
490 fi
491 fi
492 # Finally, if the user specified any on the command-line, then grab those
493 # keeping in mind that there may be several.
494 if [[ "${COMP_LINE}" =~ "--bazelrc=" ]]; then
495 # There's got to be a better way to do this, but... it gets the job done,
496 # even if there are multiple --bazelrc on the command line. The sed command
497 # SHOULD be simpler, but capturing several instances of the same pattern
498 # from the same line is tricky because of the greedy nature of .*
499 # Instead we transform it to multiple lines, and then back.
500 local cmdlnrcs=$(echo ${COMP_LINE} | sed -E "s/--bazelrc=/\n--bazelrc=/g" | sed -E "/--bazelrc/!d;s/^--bazelrc=([^ ]*).*$/\1/g" | tr "\n" " ")
501 for rc_file in $cmdlnrcs; do
502 if [ -f "$rc_file" ] ; then
503 new_files+=($(_bazel__abspath $rc_file))
504 fi
505 done
506 fi
507
508 # Each time we call _bazel__rc_expand_imports, it may find new files which then need to be expanded as well,
509 # so we loop until we've processed all new files.
510 while (( ${#new_files[@]} > 0 )) ; do
511 local all_files=($(_bazel__rc_expand_imports "$workspace" "${processed_files[@]}" "__new__" "${new_files[@]}"))
512 local new_found="no"
513 new_files=()
514 processed_files=()
515 for file in ${all_files[@]} ; do
516 if [ "$file" == "__new__" ] ; then
517 new_found="yes"
518 continue
519 elif [ "$new_found" == "no" ] ; then
520 processed_files+=($file)
521 else
522 new_files+=($file)
523 fi
524 done
525 done
526
527 echo "${processed_files[@]}"
528}
529
530# Usage: _bazel__all_configs <workspace> <command>
531#
532#
533# Gets contents of all RC files and searches them for config names
534# that could be used for expansion.
535_bazel__all_configs() {
536 local workspace="$1" command="$2" rc_files
537
538 # Start out getting a list of all RC files that we can look for configs in
539 # This respects the various command line options documented at
540 # https://docs.bazel.build/versions/2.0.0/guide.html#bazelrc
541 rc_files=$(_bazel__rc_files "$workspace")
542
543 # Commands can inherit configs from other commands, so build up command_match, which is
544 # a match list of the various commands that we can match against, given the command
545 # specified by the user
546 local build_inherit=("aquery" "clean" "coverage" "cquery" "info" "mobile-install" "print_action" "run" "test")
547 local test_inherit=("coverage")
548 local command_match="$command"
549 if [[ "${build_inherit[@]}" =~ "$command" ]]; then
550 command_match="$command_match|build"
551 fi
552 if [[ "${test_inherit[@]}" =~ "$command" ]]; then
553 command_match="$command_match|test"
554 fi
555
556 # The following commands do respectively:
557 # Gets the contents of all relevant/allowed RC files
558 # Remove file comments
559 # Filter only the configs relevant to the current command
560 # Extract the config names
561 # Filters out redundant names and returns the results
562 cat $rc_files \
563 | sed 's/#.*//' \
564 | sed -E "/^($command_match):/!d" \
565 | sed -E "s/^($command_match):([^ ]*).*$/\2/" \
566 | sort -u
567}
568
569# Usage: _bazel__expand_config <workspace> <command> <current-word>
570#
571#
572# Expands configs, checking through the allowed rc files and parsing for configs
573# relevant to the current command
574_bazel__expand_config() {
575 local workspace="$1" command="$2" cur="$3" rc_files all_configs
576 all_configs=$(_bazel__all_configs "$workspace" "$command")
577 compgen -S " " -W "$all_configs" -- "$cur"
578}
Damien Martin-Guillerez5f9c6ba2015-04-09 21:10:33 +0000579
Yuval055c93d2021-03-16 02:18:53 -0700580_bazel__is_after_doubledash() {
581 for word in "${COMP_WORDS[@]:1:COMP_CWORD-1}"; do
582 if [[ "$word" == "--" ]]; then
583 return 0
584 fi
585 done
586 return 1
587}
588
Damien Martin-Guillerez5f9c6ba2015-04-09 21:10:33 +0000589_bazel__complete_stdout() {
590 local cur=$(_bazel__get_cword) word command displacement workspace
591
592 # Determine command: "" (startup-options) or one of $BAZEL_COMMAND_LIST.
593 command="$(_bazel__get_command)"
594
595 workspace="$(_bazel__get_workspace_path)"
596 displacement="$(_bazel__get_displacement ${workspace})"
597
Yuval055c93d2021-03-16 02:18:53 -0700598 if _bazel__is_after_doubledash && [[ "$command" == "run" ]]; then
599 _bazel__complete_pattern "$workspace" "$displacement" "${cur#*=}" "path"
600 else
601 case "$command" in
602 "") # Expand startup-options or commands
603 local commands=$(echo "${BAZEL_COMMAND_LIST}" \
604 | tr " " "\n" | "grep" -v "^${BAZEL_IGNORED_COMMAND_REGEX}$")
605 _bazel__expand_options "$workspace" "$displacement" "$cur" \
606 "${commands}\
607 ${BAZEL_STARTUP_OPTIONS}"
608 ;;
Damien Martin-Guillerez5f9c6ba2015-04-09 21:10:33 +0000609
Yuval055c93d2021-03-16 02:18:53 -0700610 *)
611 case "$cur" in
612 --config=*) # Expand options:
613 _bazel__expand_config "$workspace" "$command" "${cur#"--config="}"
614 ;;
615 -*) # Expand options:
616 _bazel__expand_options "$workspace" "$displacement" "$cur" \
617 "$(_bazel__options_for $command)"
618 ;;
619 *) # Expand target pattern
620 expansion_pattern="$(_bazel__expansion_for $command)"
621 NON_QUOTE_REGEX="^[\"']"
622 if [[ $command = query && $cur =~ $NON_QUOTE_REGEX ]]; then
623 : # Ideally we would expand query expressions---it's not
624 # that hard, conceptually---but readline is just too
625 # damn complex when it comes to quotation. Instead,
626 # for query, we just expand target patterns, unless
627 # the first char is a quote.
628 elif [ -n "$expansion_pattern" ]; then
629 _bazel__complete_pattern \
630 "$workspace" "$displacement" "$cur" "$expansion_pattern"
631 fi
632 ;;
633 esac
634 ;;
635 esac
636 fi
Damien Martin-Guillerez5f9c6ba2015-04-09 21:10:33 +0000637}
638
639_bazel__to_compreply() {
640 local replies="$1"
641 COMPREPLY=()
642 # Trick to preserve whitespaces
643 while IFS="" read -r reply; do
644 COMPREPLY+=("${reply}")
645 done < <(echo "${replies}")
Zach Pomerantzea6d12f2019-08-20 01:30:23 -0700646 # Null may be set despite there being no completions
Googler532778d2019-08-26 06:18:22 -0700647 if [ ${#COMPREPLY[@]} -eq 1 ] && [ -z ${COMPREPLY[0]} ]; then
Zach Pomerantzea6d12f2019-08-20 01:30:23 -0700648 COMPREPLY=()
649 fi
Damien Martin-Guillerez5f9c6ba2015-04-09 21:10:33 +0000650}
651
652_bazel__complete() {
653 _bazel__to_compreply "$(_bazel__complete_stdout)"
654}
655
656# Some users have aliases such as bt="bazel test" or bb="bazel build", this
657# completion function allows them to have auto-completion for these aliases.
658_bazel__complete_target_stdout() {
659 local cur=$(_bazel__get_cword) word command displacement workspace
660
661 # Determine command: "" (startup-options) or one of $BAZEL_COMMAND_LIST.
662 command="$1"
663
664 workspace="$(_bazel__get_workspace_path)"
665 displacement="$(_bazel__get_displacement ${workspace})"
666
667 _bazel__to_compreply "$(_bazel__expand_target_pattern "$workspace" "$displacement" \
Damien Martin-Guillerez60fde442016-07-22 15:47:25 +0000668 "$cur" "$(_bazel__expansion_for $command)")"
Damien Martin-Guillerez5f9c6ba2015-04-09 21:10:33 +0000669}
670
671# default completion for bazel
672complete -F _bazel__complete -o nospace "${BAZEL}"
Martin Probstf84650c2017-11-29 02:09:42 -0800673complete -F _bazel__complete -o nospace "${IBAZEL}"