Autocompletar en make basado en posibles objetivos

Autocompletar en make basado en posibles objetivos

El Makefilees:

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

Si hay un doc.texen el directorio, entonces make doc.pdfcompila doc.pdf. El problema es que cuando escribo make, el autocompletado no da nada: ni siquiera permite autocompletar hasta make doc.tex. ¿Qué se puede hacer al respecto?

Respuesta1

El bash-completionpaquete no hace esto, hace algunas acrobacias para manejar ambas opciones de línea de comando y extraer una lista de Makefileobjetivos, pero no intenta generar coincidencias aplicando comodines ni manejando ningún otroreglas de patrón.

Se puede hacer, sin embargo, aquí hay una versión simple con algunas advertencias.

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}

Hay dos partes, una función _mkcacheextrae todas las reglas y objetivos de a Makefiley los almacena en caché. También realiza un poco de procesamiento para que las reglas se simplifiquen a un único target : pre-reqformulario " " en ese caché.

Luego, una función de finalización _bc_maketoma el token en el que intenta completarlo e intenta compararlo con los objetivos, y utiliza las reglas del patrón para expandir un globo en función de los requisitos previos y la palabra para completar. Si se encuentran una o más coincidencias, crea una lista de objetivos basada en las reglas del patrón.

makeSe supone GNU . Debe manejar correctamente:

  • objetivos y reglas de patrones (aunque no todos, ver más abajo)
  • forma nueva y vieja .c.o%.o : %.c
  • rutas en requisitos previos (por ejemplo RCS/)
  • con o sin todas las reglas predeterminadas (agregue -rsi makelo prefiere)

Advertencias y no admitidos:

  • dependencias intermedias o encadenadas, no es tan inteligente comomake
  • VPATHovpath
  • .SUFFIXES
  • make -C dir
  • Objetivos "archivo (miembro)", explícitos o implícitos
  • makeexpansión de opciones
  • basura patológica en el entorno que puede causar problemas de análisis de Makefile ( TERMCAPpor ejemplo)
  • Makefiles con nombres distintos aMakefile

Algunos de los anteriores se pueden agregar de manera relativamente simple, otros, como el manejo de archivos, no son tan simples.

información relacionada