O Makefile
é:
%.pdf: %.tex
rubber -d $<
Se houver um doc.tex
no diretório, então make doc.pdf
builds doc.pdf
. O problema é que quando digito make
, o autocompletar não dá nada: nem permite o autocompletar para make doc.tex
. O que pode ser feito a respeito?
Responder1
O bash-completion
pacote não faz isso, ele faz algumas acrobacias para lidar com ambas as opções de linha de comando e extrair uma lista de Makefile
alvos, mas não tenta gerar correspondências aplicando curingas ou de outra forma manipulando qualquerregras de padrão.
No entanto, isso pode ser feito. Aqui está uma versão simples com algumas ressalvas.
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}
Existem duas partes: uma função _mkcache
extrai todas as regras e alvos de a Makefile
e os armazena em cache. Ele também faz um pouco de processamento para que as regras sejam simplificadas para um único " target : pre-req
" formulário nesse cache.
Em seguida, uma função de conclusão _bc_make
pega o token que você tenta completar e tenta combiná-lo com os alvos, e usa as regras padrão para expandir um glob com base nos pré-requisitos e na palavra para conclusão. Se uma ou mais correspondências forem encontradas, ele cria uma lista de alvos com base nas regras padrão.
GNU make
é assumido. Deve tratar corretamente:
- metas e regras de padrão (embora não todas, veja abaixo)
- forma nova e antiga
.c.o
→%.o : %.c
- caminhos em pré-requisitos (por exemplo
RCS/
) - com ou sem todas as regras padrão (adicione
-r
semake
preferir)
Advertências e não suportados:
- dependências intermediárias ou encadeadas, não é tão inteligente quanto
make
VPATH
ouvpath
.SUFFIXES
make -C dir
- Destinos "archive(member)", explícitos ou implícitos
make
expansão de opções- lixo patológico no ambiente que pode causar problemas de análise do Makefile (
TERMCAP
por exemplo) - Makefiles com nomes diferentes de
Makefile
Alguns dos itens acima podem ser adicionados de forma relativamente simples, outros, como o manuseio de arquivos, não são tão simples.