Skip to content

Commit 63dbf2b

Browse files
authored
Merge pull request #1069 from akinomyoga/use-awk-for-make
refactor(make): Use awk to extract target names
2 parents 15f3ecc + 302dc52 commit 63dbf2b

File tree

11 files changed

+137
-98
lines changed

11 files changed

+137
-98
lines changed

bash_completion

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3212,6 +3212,22 @@ _comp_xfunc()
32123212
"$xfunc_name" "${@:3}"
32133213
}
32143214
3215+
# Call a POSIX-compatible awk. Solaris awk is not POSIX-compliant, but Solaris
3216+
# provides a POSIX-compatible version through /usr/xpg4/bin/awk. We switch the
3217+
# implementation to /usr/xpg4/bin/awk in Solaris if any.
3218+
# @since 2.12
3219+
if [[ $OSTYPE == *solaris* && -x /usr/xpg4/bin/awk ]]; then
3220+
_comp_awk()
3221+
{
3222+
/usr/xpg4/bin/awk "$@"
3223+
}
3224+
else
3225+
_comp_awk()
3226+
{
3227+
command awk "$@"
3228+
}
3229+
fi
3230+
32153231
# source compat completion directory definitions
32163232
_comp__init_compat_dirs=()
32173233
if [[ ${BASH_COMPLETION_COMPAT_DIR-} ]]; then

completions/_nmcli

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,25 @@
66
_comp_cmd_nmcli__con_id()
77
{
88
_comp_compgen_split -l -- "$(nmcli con list 2>/dev/null |
9-
tail -n +2 | awk -F ' {2,}' '{print $1 }')"
9+
tail -n +2 | _comp_awk -F ' {2,}' '{print $1}')"
1010
}
1111

1212
_comp_cmd_nmcli__con_uuid()
1313
{
1414
_comp_compgen_split -- "$(nmcli con list 2>/dev/null |
15-
tail -n +2 | awk -F ' {2,}' '{print $2}')"
15+
tail -n +2 | _comp_awk -F ' {2,}' '{print $2}')"
1616
}
1717

1818
_comp_cmd_nmcli__ap_ssid()
1919
{
2020
_comp_compgen_split -l -- "$(nmcli dev wifi list 2>/dev/null |
21-
tail -n +2 | awk -F ' {2,}' '{print $1}')"
21+
tail -n +2 | _comp_awk -F ' {2,}' '{print $1}')"
2222
}
2323

2424
_comp_cmd_nmcli__ap_bssid()
2525
{
2626
_comp_compgen_split -- "$(nmcli dev wifi list 2>/dev/null |
27-
tail -n +2 | awk -F ' {2,}' '{print $2}')"
27+
tail -n +2 | _comp_awk -F ' {2,}' '{print $2}')"
2828
}
2929

3030
_comp_cmd_nmcli()

completions/convert

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ _comp_cmd_convert__common_options()
4949
return
5050
;;
5151
-format)
52-
_comp_compgen_split -- "$(convert -list format | awk \
52+
_comp_compgen_split -- "$(convert -list format | _comp_awk \
5353
'/ [r-][w-][+-] / { sub("[*]$","",$1); print tolower($1) }')"
5454
return
5555
;;

completions/dpkg

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ _comp_xfunc_dpkg_compgen_installed_packages()
77
grep-status -P -e "^${cur-}" -a \
88
-FStatus 'ok installed' \
99
-n -s Package 2>/dev/null ||
10-
command awk -F '\n' -v RS="" "
10+
_comp_awk -F '\n' -v RS="" "
1111
index(\$1, \"Package: ${cur-}\") == 1 &&
1212
\$2 ~ /ok installed|half-installed|unpacked|half-configured|^Essential: yes/ {
1313
print(substr(\$1, 10));
@@ -22,7 +22,7 @@ _comp_xfunc_dpkg_compgen_purgeable_packages()
2222
grep-status -P -e "^${cur-}" -a \
2323
-FStatus 'ok installed' -o -FStatus 'ok config-files' \
2424
-n -s Package 2>/dev/null ||
25-
command awk -F '\n' -v RS="" "
25+
_comp_awk -F '\n' -v RS="" "
2626
index(\$1, \"Package: ${cur-}\") == 1 &&
2727
\$2 ~ /ok installed|half-installed|unpacked|half-configured|config-files|^Essential: yes/ {
2828
print(substr(\$1, 10));

completions/iwconfig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ _comp_cmd_iwconfig()
1515
_comp_compgen -- -W 'on off any'
1616
if [[ ${BASH_COMPLETION_CMD_IWCONFIG_SCAN-${COMP_IWLIST_SCAN-}} ]]; then
1717
_comp_compgen -a split -- "$(iwlist "${words[1]}" scan |
18-
awk -F'\"' '/ESSID/ {print $2}')"
18+
_comp_awk -F '\"' '/ESSID/ {print $2}')"
1919
fi
2020
return
2121
;;
@@ -38,7 +38,7 @@ _comp_cmd_iwconfig()
3838
_comp_compgen -- -W 'on off any'
3939
if [[ ${BASH_COMPLETION_CMD_IWCONFIG_SCAN-${COMP_IWLIST_SCAN-}} ]]; then
4040
_comp_compgen -a split -- "$(iwlist "${words[1]}" scan |
41-
awk -F ': ' '/Address/ {print $2}')"
41+
_comp_awk -F ': ' '/Address/ {print $2}')"
4242
fi
4343
return
4444
;;

completions/make

Lines changed: 15 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,21 @@
11
# bash completion for GNU make -*- shell-script -*-
22

3-
# TODO: rename per API conventions, rework to use vars rather than outputting(?)
4-
_make_target_extract_script()
3+
# Extract the valid target names starting with PREFIX from the output of
4+
# `make -npq'
5+
# @param mode If this is `-d', the directory names already specified in
6+
# PREFIX are omitted in the output
7+
# @param prefix Prefix of the target names
8+
_comp_cmd_make__extract_targets()
59
{
6-
local mode="$1"
7-
shift
8-
9-
local prefix="$1"
10-
local prefix_pat=$(command sed 's/[][\,.*^$(){}?+|/]/\\&/g' <<<"$prefix")
11-
local basename=${prefix##*/}
12-
local dirname_len=$((${#prefix} - ${#basename}))
13-
# Avoid expressions like '\(.\{0\}\)', FreeBSD sed doesn't like them:
14-
# > sed: 1: ...: RE error: empty (sub)expression
15-
local dirname_re=
16-
((dirname_len > 0)) && dirname_re="\(.\{${dirname_len}\}\)"
17-
18-
if [[ ! $dirname_re ]]; then
19-
local output="\1"
20-
elif [[ $mode == -d ]]; then
21-
# display mode, only output current path component to the next slash
22-
local output="\2"
23-
else
24-
# completion mode, output full path to the next slash
25-
local output="\1\2"
26-
fi
27-
28-
cat <<EOF
29-
1,/^# * Make data base/ d; # skip any makefile output
30-
/^# * Finished Make data base/,/^# * Make data base/{
31-
d; # skip any makefile output
32-
}
33-
/^# * Variables/,/^# * Files/ d; # skip until files section
34-
/^# * Not a target/,/^$/ d; # skip not target blocks
35-
/^${prefix_pat}/,/^$/! d; # skip anything user dont want
36-
37-
# The stuff above here describes lines that are not
38-
# explicit targets or not targets other than special ones
39-
# The stuff below here decides whether an explicit target
40-
# should be output.
41-
42-
/^# * File is an intermediate prerequisite/ {
43-
s/^.*$//;x; # unhold target
44-
d; # delete line
45-
}
46-
47-
/^$/ { # end of target block
48-
x; # unhold target
49-
/^$/d; # dont print blanks
50-
s|^${dirname_re-}\(.\{${#basename}\}[^:]*\):.*$|${output}|p;
51-
d; # hide any bugs
52-
}
53-
54-
# This pattern includes a literal tab character as \t is not a portable
55-
# representation and fails with BSD sed
56-
/^[^# :%]\{1,\}:/ { # found target block
57-
/^\.PHONY:/ d; # special target
58-
/^\.SUFFIXES:/ d; # special target
59-
/^\.DEFAULT:/ d; # special target
60-
/^\.PRECIOUS:/ d; # special target
61-
/^\.INTERMEDIATE:/ d; # special target
62-
/^\.SECONDARY:/ d; # special target
63-
/^\.SECONDEXPANSION:/ d; # special target
64-
/^\.DELETE_ON_ERROR:/ d; # special target
65-
/^\.IGNORE:/ d; # special target
66-
/^\.LOW_RESOLUTION_TIME:/ d; # special target
67-
/^\.SILENT:/ d; # special target
68-
/^\.EXPORT_ALL_VARIABLES:/ d; # special target
69-
/^\.NOTPARALLEL:/ d; # special target
70-
/^\.ONESHELL:/ d; # special target
71-
/^\.POSIX:/ d; # special target
72-
/^\.NOEXPORT:/ d; # special target
73-
/^\.MAKE:/ d; # special target
74-
EOF
75-
76-
# don't complete with hidden targets unless we are doing a partial completion
77-
if [[ ! ${prefix_pat} || ${prefix_pat} == */ ]]; then
78-
cat <<EOF
79-
/^${prefix_pat}[^a-zA-Z0-9]/d; # convention for hidden tgt
80-
EOF
81-
fi
10+
local mode=$1
11+
local -x prefix=$2
8212

83-
cat <<EOF
84-
h; # hold target
85-
d; # delete line
86-
}
13+
# display mode, only output current path component to the next slash
14+
local -x prefix_replace=$prefix
15+
[[ $mode == -d && $prefix == */* ]] &&
16+
prefix_replace=${prefix##*/}
8717

88-
EOF
18+
_comp_awk -f "${BASH_SOURCE[0]%/*}/../helpers/make-extract-targets.awk"
8919
}
9020

9121
# Truncate the non-unique filepaths in COMPREPLY to only generate unique
@@ -224,11 +154,11 @@ _comp_cmd_make()
224154
# mode=-d # display-only mode
225155
# fi
226156

227-
local IFS=$' \t\n' script=$(_make_target_extract_script $mode "$cur")
157+
local IFS=$' \t\n'
228158
COMPREPLY=($(LC_ALL=C \
229159
$1 -npq __BASH_MAKE_COMPLETION__=1 \
230160
${makef+"${makef[@]}"} "${makef_dir[@]}" .DEFAULT 2>/dev/null |
231-
command sed -ne "$script"))
161+
_comp_cmd_make__extract_targets "$mode" "$cur"))
232162

233163
_comp_cmd_make__truncate_non_unique_paths
234164

completions/perl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ _comp_cmd_perldoc()
132132
if [[ $cur == p* ]]; then
133133
_comp_compgen -a split -- "$(PERLDOC_PAGER=cat "$1" -u perl |
134134
command sed -ne '/perl.*Perl overview/,/perlwin32/p' |
135-
awk '$NF=2 && $1 ~ /^perl/ { print $1 }')"
135+
awk 'NF >= 2 && $1 ~ /^perl/ { print $1 }')"
136136
fi
137137
fi
138138
_comp_compgen -a filedir 'p@([lm]|od)'

completions/portinstall

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ _comp_cmd_portinstall()
1616
indexfile=$portsdir/INDEX
1717
[[ -f $indexfile && -r $indexfile ]] || return
1818

19-
_comp_compgen_split -l -- "$(awk -F '|' '
19+
_comp_compgen_split -l -- "$(_comp_awk -F '|' '
2020
BEGIN { portsdir = ENVIRON["portsdir"]; len = length(portsdir) }
2121
{ print $1 }
2222
substr($2, 1, len) == portsdir { print substr($2, len + 1) }

helpers/Makefile.am

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
helpersdir = $(datadir)/$(PACKAGE)/helpers
2-
helpers_DATA = perl python
2+
helpers_DATA = perl python make-extract-targets.awk
33

44
EXTRA_DIST = $(helpers_DATA)

helpers/make-extract-targets.awk

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# helper AWK script for GNU make -*- awk -*-
2+
3+
# This AWK script is used by the function `_comp_cmd_make__extract_targets` in
4+
# `completions/make`. This script receives the output of `make -npq' as the
5+
# input file or stdin and outputs the list of targets matching the prefix.
6+
#
7+
# @env prefix Specifies the prefix to match.
8+
# @env prefix_replace Specifies the string that replaces the prefix in the
9+
# output. This is used when we want to omit the directory name in showing
10+
# the list of the completions.
11+
#
12+
13+
BEGIN {
14+
prefix = ENVIRON["prefix"];
15+
prefix_replace = ENVIRON["prefix_replace"];
16+
is_target_block = 0;
17+
target = "";
18+
}
19+
20+
function starts_with(str, prefix) {
21+
return substr(str, 1, length(prefix)) == prefix;
22+
}
23+
24+
# skip any makefile outputs
25+
NR == 1, /^# +Make data base/ { next; }
26+
/^# +Finished Make data base/,/^# +Make data base/ { next; }
27+
28+
# skip until files section
29+
/^# +Variables/, /^# +Files/ { next; }
30+
31+
# skip not-target blocks
32+
/^# +Not a target/, /^$/ { next; }
33+
34+
# The stuff above here describes lines that are not
35+
# explicit targets or not targets other than special ones
36+
# The stuff below here decides whether an explicit target
37+
# should be output.
38+
39+
# only process the targets the user wants.
40+
starts_with($0, prefix) { is_target_block = 1; }
41+
is_target_block == 0 { next; }
42+
43+
/^# +File is an intermediate prerequisite/ { # cancel the block
44+
is_target_block = 0;
45+
target = "";
46+
next;
47+
}
48+
49+
# end of target block
50+
/^$/ {
51+
is_target_block = 0;
52+
if (target != "") {
53+
print target;
54+
target = "";
55+
}
56+
next;
57+
}
58+
59+
# found target block
60+
/^[^#\t:%]+:/ {
61+
# special targets
62+
if (/^\.PHONY:/ ) next;
63+
if (/^\.SUFFIXES:/ ) next;
64+
if (/^\.DEFAULT:/ ) next;
65+
if (/^\.PRECIOUS:/ ) next;
66+
if (/^\.INTERMEDIATE:/ ) next;
67+
if (/^\.SECONDARY:/ ) next;
68+
if (/^\.SECONDEXPANSION:/ ) next;
69+
if (/^\.DELETE_ON_ERROR:/ ) next;
70+
if (/^\.IGNORE:/ ) next;
71+
if (/^\.LOW_RESOLUTION_TIME:/ ) next;
72+
if (/^\.SILENT:/ ) next;
73+
if (/^\.EXPORT_ALL_VARIABLES:/) next;
74+
if (/^\.NOTPARALLEL:/ ) next;
75+
if (/^\.ONESHELL:/ ) next;
76+
if (/^\.POSIX:/ ) next;
77+
if (/^\.NOEXPORT:/ ) next;
78+
if (/^\.MAKE:/ ) next;
79+
80+
# dont complete with hidden targets unless we are doing a partial completion
81+
if (prefix == "" || prefix ~ /\/$/)
82+
if (substr($0, length(prefix) + 1, 1) ~ /[^a-zA-Z0-9]/)
83+
next;
84+
85+
target = $0;
86+
sub(/:.*/, "", target);
87+
if (prefix_replace != prefix)
88+
target = prefix_replace substr(target, 1 + length(prefix));
89+
90+
next;
91+
}
92+
93+
# ex: filetype=awk

0 commit comments

Comments
 (0)