Skip to content

Commit 87a9e9c

Browse files
marxinscop
authored andcommitted
gcc: support new --completion option (#222)
1 parent 2b81989 commit 87a9e9c

File tree

2 files changed

+105
-37
lines changed

2 files changed

+105
-37
lines changed

completions/gcc

+45-37
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,58 @@
11
# gcc(1) completion -*- shell-script -*-
2-
#
3-
# The only unusual feature is that we don't parse "gcc --help -v" output
4-
# directly, because that would include the options of all the other backend
5-
# tools (linker, assembler, preprocessor, etc) without any indication that
6-
# you cannot feed such options to the gcc driver directly. (For example, the
7-
# linker takes a -z option, but you must type -Wl,-z for gcc.) Instead, we
8-
# ask the driver ("g++") for the name of the compiler ("cc1"), and parse the
9-
# --help output of the compiler.
102

113
_gcc()
124
{
13-
local cur prev words cword
5+
local cur prev prev2 words cword argument prefix prefix_length
146
_init_completion || return
157

16-
local cc backend
8+
# Test that GCC is recent enough and if not fallback to
9+
# parsing of --completion option.
10+
if ! $1 --completion=" " 2>/dev/null; then
11+
if [[ "$cur" == -* ]]; then
12+
local cc=$($1 -print-prog-name=cc1 2>/dev/null)
13+
[[ $cc ]] || return
14+
COMPREPLY=($( compgen -W "$($cc --help 2>/dev/null | tr '\t' ' ' |\
15+
command sed -e '/^ *-/!d' -e 's/ *-\([^][ <>]*\).*/-\1/')" \
16+
-- "$cur" ))
17+
[[ $COMPREPLY == *= ]] && compopt -o nospace
18+
else
19+
_filedir
20+
fi
21+
return
22+
fi
1723

18-
case $1 in
19-
gcj)
20-
backend=jc1
21-
;;
22-
gpc)
23-
backend=gpc1
24-
;;
25-
*77)
26-
backend=f771
27-
;;
28-
*95)
29-
backend=f951
30-
;;
31-
*)
32-
backend=cc1 # (near-)universal backend
33-
;;
34-
esac
24+
# extract also for situations like: -fsanitize=add
25+
if [[ $cword -gt 2 ]]; then
26+
prev2="${COMP_WORDS[$cword - 2]}"
27+
fi
3528

29+
# sample: -fsan
3630
if [[ "$cur" == -* ]]; then
37-
cc=$($1 -print-prog-name=$backend 2>/dev/null)
38-
[[ $cc ]] || return
39-
# sink stderr:
40-
# for C/C++/ObjectiveC it's useless
41-
# for FORTRAN/Java it's an error
42-
COMPREPLY=( $(compgen -W "$($cc --help 2>/dev/null | tr '\t' ' ' |\
43-
command sed -e '/^ *-/!d' -e 's/ *-\([^][ <>]*\).*/-\1/')" \
44-
-- "$cur") )
45-
[[ $COMPREPLY == *= ]] && compopt -o nospace
46-
else
31+
argument=$cur
32+
prefix=""
33+
# sample: -fsanitize=
34+
elif [[ "$cur" == "=" && $prev == -* ]]; then
35+
argument=$prev$cur
36+
prefix=$prev$cur
37+
# sample: -fsanitize=add
38+
elif [[ "$prev" == "=" && $prev2 == -* ]]; then
39+
argument=$prev2$prev$cur
40+
prefix=$prev2$prev
41+
# sample: --param lto-
42+
elif [[ "$prev" == --param ]]; then
43+
argument="$prev $cur"
44+
prefix="$prev "
45+
fi
46+
47+
if [[ -z $argument ]]; then
4748
_filedir
49+
else
50+
# In situation like '-fsanitize=add' $cur is equal to last token.
51+
# Thus we need to strip the beginning of suggested option.
52+
prefix_length=$((${#prefix}+1))
53+
local flags=$($1 --completion="$argument" | cut -c $prefix_length-)
54+
[[ "${flags}" == "=*" ]] && compopt -o nospace 2>/dev/null
55+
COMPREPLY=( $(compgen -W "$flags" -- "") )
4856
fi
4957
} &&
5058
complete -F _gcc gcc{,-5,-6,-7,-8} g++{,-5,-6,-7,-8} g77 g95 \

test/t/test_gcc.py

+60
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,67 @@
11
import pytest
22

3+
from conftest import assert_bash_exec
4+
35

46
class TestGcc:
7+
@pytest.fixture(scope="class")
8+
def gcc_with_completion(self, bash):
9+
got = assert_bash_exec(
10+
bash, "gcc --help=common || :", want_output=True
11+
)
12+
if "--completion" not in got:
13+
pytest.skip("GCC does not support --completion")
14+
15+
@pytest.fixture(scope="class")
16+
def gcc_x86(self, bash):
17+
got = assert_bash_exec(bash, "gcc -v || :", want_output=True)
18+
if "Target: x86" not in got:
19+
pytest.skip("Not a x86 GCC")
20+
521
@pytest.mark.complete("gcc ")
622
def test_1(self, completion):
723
assert completion
24+
25+
@pytest.mark.complete("gcc -fsanitize=add")
26+
def test_enum_value(self, completion, gcc_with_completion):
27+
assert completion == "-fsanitize=address"
28+
29+
@pytest.mark.complete("gcc -fsanitize=")
30+
def test_enum_value_with_eq(self, completion, gcc_with_completion):
31+
assert "address" in completion
32+
33+
@pytest.mark.complete("gcc -fno-ipa-ic")
34+
def test_negative_option(self, completion, gcc_with_completion):
35+
assert "-fno-ipa-icf" in completion
36+
37+
@pytest.mark.complete("gcc -fxyz-abc")
38+
def test_no_completion(self, completion):
39+
assert not completion
40+
41+
@pytest.mark.complete("gcc --param ")
42+
def test_param_with_space(self, completion, gcc_with_completion):
43+
assert len(completion) > 50
44+
# starting with GCC 10.1 param end with =
45+
assert (
46+
"lto-partitions" in completion or "lto-partitions=" in completion
47+
)
48+
49+
@pytest.mark.complete("gcc --param=lto-max-p")
50+
def test_param_with_eq(self, completion, gcc_with_completion):
51+
# starting with GCC 10.1 param end with =
52+
assert (
53+
completion == "--param=lto-max-partition"
54+
or completion == "--param=lto-max-partition="
55+
)
56+
57+
@pytest.mark.complete("gcc -march=amd")
58+
def test_march(self, completion, gcc_with_completion, gcc_x86):
59+
assert completion == "-march=amdfam10"
60+
61+
@pytest.mark.complete("gcc -march=")
62+
def test_march_native(self, completion, gcc_with_completion):
63+
assert "native" in completion
64+
65+
@pytest.mark.complete("gcc -mtune=")
66+
def test_mtune_generic(self, completion, gcc_with_completion):
67+
assert "generic" in completion

0 commit comments

Comments
 (0)