![根据可能的目标在 make 中自动完成](https://linux22.com/image/97294/%E6%A0%B9%E6%8D%AE%E5%8F%AF%E8%83%BD%E7%9A%84%E7%9B%AE%E6%A0%87%E5%9C%A8%20make%20%E4%B8%AD%E8%87%AA%E5%8A%A8%E5%AE%8C%E6%88%90.png)
是Makefile
:
%.pdf: %.tex
rubber -d $<
doc.tex
如果目录中有 a ,则make doc.pdf
构建doc.pdf
.问题是,当我输入 时make
,自动补全什么也没给出:它甚至不允许自动补全到make doc.tex
。对此我们能做些什么呢?
答案1
该bash-completion
包不会执行此操作,它会执行一些杂技来处理命令行选项并提取目标列表Makefile
,但它不会尝试通过应用通配符或以其他方式处理任何内容来生成匹配项模式规则。
不过这是可以做到的,这是一个简单的版本,有一些注意事项。
function _mkcache() {
local _file="$1"
# add "-r" to omit defaults (60+ rules)
${MAKE:-make} ${_file:+-f "$_file"} -qp 2>/dev/null |
gawk '/^# *Make data base/,/^# *Finished Make data base/{
if (/^# Not a target/) { getline; next }
## handle "target: ..."
if (match($0,/^([^.#% ][^:%=]+) *:($|[^=])(.*)/,bits)) {
#if (bits[3]=="") next # OPT: skip phony
printf("%s\n",bits[1])
}
## handle "%.x [...]: %.y [| x]", split into distinct targets/prereqs
else if (match($0,/^([^:]*%[^:]*) *(::?) *(.*%.*) *(\| *(.*))?/,bits)) {
#if (bits[3]=="%") next # OPT: skip wildcard ones
nb1=split(bits[1],bb1)
nb3=split(bits[3],bb3)
for (nn=1; nn<=nb1; nn++)
for (mm=1; mm<=nb3; mm++)
printf("%s : %s\n",bb1[nn],bb3[mm])
}
## handle fixed (no %) deps
else if (match($0,/^([^:]*%[^:]*) *(::?) *([^%]*)$/,bits)) {
if (bits[3]=="") next # phony
printf("%s : %s\n",bits[1],bits[3])
}
## handle old form ".c.o:" rewrite to new form "%.o: %.c"
else if (match($0,/^\.([^.]+)\.([^.]+): *(.*)/,bits)) {
printf("%%.%s : %%.%s\n", bits[2],bits[1])
}
}' > ".${_file:-Makefile}.targets"
}
function _bc_make() {
local ctok=${COMP_WORDS[COMP_CWORD]} # curr token
local ptok=${COMP_WORDS[COMP_CWORD-1]} # prev token
local -a mkrule maybe
local try rr lhs rhs rdir pat makefile=Makefile
## check we're not doing any make options
[[ ${ctok:0:1} != "-" && ! $ptok =~ ^-[fCIjloW] ]] && {
COMPREPLY=()
[[ "$makefile" -nt .${makefile}.targets ]] &&
_mkcache "$makefile"
mapfile -t mkrule < ".${makefile}.targets"
# mkrule+=( "%.o : %.c" ) # stuff in extra rules
for rr in "${mkrule[@]}"; do
IFS=": " read lhs rhs <<< $rr
## special "archive(member):"
[[ "$lhs" =~ ^(.*)?\((.+)\) ]] && {
continue # not handled
}
## handle simple targets
[[ "$rhs" == "" ]] && {
COMPREPLY+=( $(compgen -W "$lhs" -- "$ctok" ) )
continue
}
## rules with a path, like "% : RCS/%,v"
rdir=""
[[ "$rhs" == */* ]] && rdir="${rhs/%\/*/}/"
rhs=${rhs/#*\//}
## expand (glob) that matches RHS
## if current token already ends in a "." strip it
## match by replacing "%" stem with "*"
[[ $ctok == *. ]] && try="${rdir}${rhs/\%./$ctok*}" \
|| try="${rdir}${rhs/\%/$ctok*}"
maybe=( $(compgen -G "$try") ) # try must be quoted
## maybe[] is an array of filenames from expanded prereq globs
(( ${#maybe[*]} )) && {
[[ "$rhs" =~ % ]] && {
## promote rhs glob to a regex: % -> (.*)
rhs="${rhs/./\\.}"
pat="${rdir}${rhs/\%/(.*)}"
## use regex to extract stem from RHS, sub "%" on LHS
for nn in "${maybe[@]}"; do
[[ $nn =~ $pat ]] && {
COMPREPLY+=( "${lhs/\%/${BASH_REMATCH[1]}}" )
}
done
} || {
# fixed prereqs (no % on RHS)
COMPREPLY+=( "${lhs/\%/$ctok}" )
}
}
done
return
}
COMPREPLY=() #default
}
complete -F _bc_make ${MAKE:-make}
有两个部分,一个函数_mkcache
从 a 中提取所有规则和目标Makefile
并缓存它们。它还进行了一些处理,因此规则被简化target : pre-req
为该缓存中的单个“”形式。
然后,完成函数_bc_make
获取您尝试完成的标记并尝试与目标进行匹配,并使用模式规则根据先决条件和完成的单词来扩展 glob。如果找到一个或多个匹配项,它会根据模式规则构建目标列表。
make
假定为GNU 。它应该正确处理:
- 目标和模式规则(虽然不是全部,见下文)
- 新旧形式
.c.o
→%.o : %.c
- 先决条件中的路径(例如
RCS/
) - 有或没有所有默认规则(如果愿意,可以
-r
添加)make
警告,不支持:
- 中间或链式依赖项,它不如
make
VPATH
或者vpath
.SUFFIXES
make -C dir
- “archive(member)”目标,显式或隐式
make
选项扩展- 环境中的病态垃圾可能会导致 Makefile 解析问题(
TERMCAP
例如) - Makefiles 命名为除
Makefile
上面的一些内容可以相对简单地添加,而其他内容(例如存档处理)则不是那么简单。