Preenchimento automático em make com base em possíveis alvos

Preenchimento automático em make com base em possíveis alvos

O Makefileé:

%.pdf: %.tex
    rubber -d $<

Se houver um doc.texno diretório, então make doc.pdfbuilds 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-completionpacote não faz isso, ele faz algumas acrobacias para lidar com ambas as opções de linha de comando e extrair uma lista de Makefilealvos, 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 _mkcacheextrai todas as regras e alvos de a Makefilee 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_makepega 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 -rse makepreferir)

Advertências e não suportados:

  • dependências intermediárias ou encadeadas, não é tão inteligente quantomake
  • VPATHouvpath
  • .SUFFIXES
  • make -C dir
  • Destinos "archive(member)", explícitos ou implícitos
  • makeexpansão de opções
  • lixo patológico no ambiente que pode causar problemas de análise do Makefile ( TERMCAPpor exemplo)
  • Makefiles com nomes diferentes deMakefile

Alguns dos itens acima podem ser adicionados de forma relativamente simples, outros, como o manuseio de arquivos, não são tão simples.

informação relacionada