Enumere los objetivos / metas en GNU que contienen variables en su definición


100

Tengo un archivo MAKE bastante grande que crea varios objetivos sobre la marcha calculando nombres a partir de variables. (por ejemplo, foo $ (VAR): $ (PREREQS)). ¿Hay alguna forma de convencer a gnu make de que arroje una lista de objetivos después de haber expandido estas variables?

Me gustaría poder obtener los objetivos para un archivo MAKE arbitrario. Estoy tratando de escribir una función de finalización para mi shell.

Respuestas:


79

¿Puede analizar la salida de make -pn(es decir make --print-data-base --dry-run)? Imprime todas las variables, reglas, reglas implícitas y qué comandos se ejecutarán con laborioso detalle.


Esta respuesta se ve mucho mejor que la otra.
Matt Joiner

Estoy de acuerdo en que esto es mucho mejor. Aún más trabajo del que esperaba, pero lo marcaré como la respuesta ya que nada más parece ser razonable y GNU make no tiene una bandera conveniente para lo que quiero.
BitShifter

6
Tenga en cuenta que la salida puede depender del objetivo que especifique, si su Makefile genera objetivos de forma dinámica. La salida también puede depender de los valores de las variables de entorno o hacer variables especificadas con el comando make.
reinierpost

6
Me gusta la brevedad. +1 Agregué mi propio giro (descrito en una respuesta a continuación):make -pn | sed -rn '/^[^# \t\.%].*:[^=]?/p'
Jamie

Sugerir make -pnr. La -rquita reglas implícitas.
sjbx

114
make -qp | awk -F':' '/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ {split($1,A,/ /);for(i in A)print A[i]}'     

Tomado de la finalización de make arg, que funciona a la perfección.


2
Intenté crear un alias para esto, poniéndolo entre comillas dobles, pero solo da como resultado que awk tenga un error de sintaxis en la primera coma ... ¿alguna idea sobre cómo escapar correctamente de esto para un alias de shell (bash)?
drfrogsplat

3
Creo que tiene algo que ver con dónde está definido. Creo que si se define como un alias, su directorio de trabajo se convierte en el directorio donde está definido, y aparentemente a la parte awk no le gusta el punto en los directorios ocultos. Definir una función funciona.
Ibrahim

Simplemente lo puse en un script de shell en ~/binlugar de usar un alias. Funciona genial; ¡Gracias!
mpontillo

3
El alias alias mkl="make -qp | awk -F':' '/^[a-zA-Z0-9][^\$#\/\t=]*:([^=]|\$)/ {split(\$1,A,/ /);for(i in A)print A[i]}' | sort"que te ahorrará 2 minutos de citas de juegos :-)
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

1
| sort -uparece más útil :)
sherpya

14

No estoy seguro de si esto es solo una cosa de gnu make, pero esto funciona bien:

make help


Esto es bastante útil, mucho más fácil de recordar que un alias personalizado para uno de los otros métodos.
Ibrahim

8
Realmente no funciona en GNU Make 3.81, probablemente un objetivo en tus archivos MAKE: "make: *** No hay regla para hacer que el objetivo
sea`

8
Esto solo invoca el objetivo de "ayuda" en Makefile. Si existe, imprimirá algo (no las listas completas de objetivos), si no existe (situación típica), saldrá de apuros.
pfalcon

2
La buena noticia es que cmake parece generar un objetivo de "ayuda" agradable y fácil de leer.
Stéphane

¡Esto es asombroso! :) Go cmake
Jeef

10

Varios respondedores han sugerido usar make -pn, que imprimirá la base de datos de reglas pero no ejecutará nada, más o menos. El problema con este enfoque es que -naún invoca todas las marcas recursivas, y aún hace mucho más trabajo del necesario, porque imprime todos los comandos que habría invocado en una compilación normal. Una solución más eficiente sería crear un archivo MAKE trivial, dummy.mk, con estos contenidos:

__all_targets__: ; #no-op

Ahora invoque make as make -p -f Makefile -f dummy.mk __all_targets__. En cualquier construcción sustancial, la diferencia en la cantidad de salida generada por la marca es significativa. Por ejemplo:

$ gmake -pn | wc
 138985 2632330 69612711
$ gmake -f Makefile -f /tmp/dummy.mk -pn __all_targets__ | wc
  21673   93437  878985

El tiempo de ejecución también fue dramáticamente mejor: 2.063 segundos para la primera versión, 0.059 segundos para la segunda.


1
Buena idea, todos los que trabajan con grandes sistemas de compilación la apreciarán. Estadísticas para Android Gingerbread tree (no usa make recursivo, por lo que los ahorros no son enormes, solo buenos): " time make -pn | wc -l": 1338738 real 0m47.754s." time make -f Makefile -f dummy.mk -pn __all_targets__ | wc -l": 938621 real 0m40.219s.
pfalcon

Buen punto. La página de manual de makeparece sugerir algo similar pero amigable, a sabermake -p -f/dev/null
Davide

@Davide que no lo hará bastante trabajo: especificando -f /dev/nullimpedirá makea partir de análisis de los archivos make regulares, de manera que obtendrá una impresión única de los incorporados en las reglas. Por eso mi solución lo especifica -f Makefile -f dummy.mk. Pero eso tampoco es suficiente, porque con -n, makecontinuará y realmente comenzará a ejecutar las reglas en el archivo MAKE también. Desafortunadamente, no conozco ninguna modificación que pueda simplificar la solución tal como se presentó originalmente.
Eric Melski

Por supuesto que tienes razón. Sin embargo, la página de manual make tiene un error, porque dice: "Para imprimir la base de datos sin intentar rehacer ningún archivo, use make -p -f / dev / null"
Davide

Ligera corrección a mi comentario anterior: debería leer "... porque sin -n ..."
Eric Melski


7

Editar: Para su información, el repositorio de git de finalización de bash de Debian en otra respuesta ahora incorpora una versión mejorada de este script adaptada a los casos de uso de finalización de bash.

#!/bin/bash

SCRIPT='
  /^# Make data base/,/^# Files/d             # skip until files section
  /^# Not a target/,+1          d             # following target isnt
  /^\.PHONY:/                   d             # special target
  /^\.SUFFIXES:/                d             # special target
  /^\.DEFAULT:/                 d             # special target
  /^\.PRECIOUS:/                d             # special target
  /^\.INTERMEDIATE:/            d             # special target
  /^\.SECONDARY:/               d             # special target
  /^\.SECONDEXPANSION/          d             # special target
  /^\.DELETE_ON_ERROR:/         d             # special target
  /^\.IGNORE:/                  d             # special target
  /^\.LOW_RESOLUTION_TIME:/     d             # special target
  /^\.SILENT:/                  d             # special target
  /^\.EXPORT_ALL_VARIABLES:/    d             # special target
  /^\.NOTPARALLEL:/             d             # special target
  /^\.ONESHELL:/                d             # special target
  /^\.POSIX:/                   d             # special target
  /^\.NOEXPORT:/                d             # special target
  /^\.MAKE:/                    d             # special target

# The stuff above here describes lines that are not
#  explicit targets or not targets other than special ones
# The stuff below here decides whether an explicit target
#  should be output.

  /^[^#\t:=%]+:([^=]|$)/ {                    # found target block
    h                                         # hold target
    d                                         # delete line
  }
  /^# File is an intermediate prerequisite/ { # nope
    s/^.*$//;x                                # unhold target
    d                                         # delete line
  }
  /^([^#]|$)/ {                               # end of target block
    s/^.*$//;x                                # unhold target
    s/:.*$//p                                 # write current target
    d                                         # hide any bugs
  }
'

make -npq .DEFAULT 2>/dev/null | sed -n -r "$SCRIPT" \
  | sort | uniq

Este es un script mucho más completo que el script de finalización de debian bash porque proporciona resultados para las reglas generadas que es lo que pide la pregunta y la respuesta más votada (script de finalización de debian bash en el servidor git de debian) no va lo suficientemente lejos.

Este no es el guión original al que me vinculé, pero es mucho más simple y un poco más rápido.


Por cierto, mods, si esta respuesta no está completamente en línea con todas las políticas debido a los matices de su texto, comente antes de eliminar. Haré los ajustes legales necesarios para que esta pregunta obtenga una respuesta completa y válida.
codeshot

Como prueba rápida, tomé el código fuente de gcc y ejecuté ./configure && time ~ / makecomp.sh | wc -l donde ~ / makecomp.sh es mi respuesta. ... configurar salida ... config.status: creando Makefile 3312 real 0m0.401s usuario 0m0.412s sys 0m0.028s
codeshot

Como otra prueba, utilicé el archivo de demostración makefile en codeshot.blogspot.co.uk/2012/08/… que genera pseudo-reglas para construir y verificar pases de prueba unitaria. Este ejemplo usa más características de make que un proyecto de autoconfiguración multiplataforma típico, por lo que el script en esta respuesta devuelve 14 objetivos reales, mientras que la finalización de bash para make en ubuntu devuelve solo dos reglas útiles y luego 5 archivos fuente.
codeshot

4

Este es el código para alias basado en la solución de Todd Hodes

alias mtargets='make -qp | awk -F":" "/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ {split(\$1,A,/ /);for(i in A)print A[i]}"'

3

Esto dará un buen resultado:

make -pn | perl -F: -ane 'print "$F[0]\n" if /^\w+\s*:/' | sort


2

Supongo que llego un poco tarde a esta fiesta, pero si estás buscando un comando específico, puedes probar

make -qp | grep -v '^# ' | grep -v '^[[:space:]]' | grep --only-matching '^.*:'

Esto funciona principalmente, aunque aún puede incluir algunas cosas que no son de destino, como una vpathdirectiva. Si no depende de makelas reglas integradas de ', puede utilizarlo make -qpRcomo primer comando de la canalización.


Lo probé con freetz (.org). Muestra una gran cantidad de archivos MAKE incluidos y no enumera todos los objetivos.
Robert Siemer

1

Solución de rubí:

`make -qp`.split("\n").select{|l| l =~ /^\w/ && l !~ /=/}.map{|l| l.sub(/:.*/,'')}

1

Estoy trabajando en solaris 10 y turbo C shell. La solución dada no funciona para mi proyecto makefile. incluso después de alterar la línea de comando anterior a la sintaxis tcsh. Sin embargo, descubrí que puede obtener una solución fácil usando

remake --tasks | grep -v "clean some static output or just grep tabs on start of line" | awk ´{print $1}´

versión de remake:

remake --version

es

GNU Make 3.82+dbg0.8
Built for sparc-sun-sparc2-9

y algunos otros datos de versión sin importancia


0

Fui a buscar la misma pregunta y se me ocurrió este giro:

make -pn | sed -rn '/^[^# \t\.%].*:/p'

Esto elimina todas las líneas de comentarios, reglas de patrones (líneas que comienzan con tabulaciones), todos los elementos intrínsecos (ejemplo .c.oy %.o: %.cpatrones).


no leí el comentario de la respuesta aceptada: +1 para la respuesta de Jack ... gist.github.com/777954 - pvandenberk 13 de enero a las 14:47
Jamie

0

Encontré esta solución en otro hilo:

sh -c "cat Makefile | egrep \"^[[:alnum:][:punct:]]{0,}:[[:space:]]{0,}[[:alnum:][:punct:][:space:]]{0,}$\""

También puede agregarlo a su Makefile:

list:
    sh -c "cat Makefile | egrep \"^[[:alnum:][:punct:]]{0,}:[[:space:]]{0,}[[:alnum:][:punct:][:space:]]{0,}$\\""

Y ejecutar make list.


-1

Claro, pero ¿cuándo quieres que los escupe?

Para informar el nombre del objetivo cuando ejecuta la regla, coloque una línea en la regla:

foo$(VAR): $(PREREQS)
    @echo now making the foo target: $@
    do_other_stuff...

Para escupirlos todos a la vez, puede hacer un objetivo PHONY separado:

.PHONY: show_vars
show_vars:
    @echo foo$(VAR)
    @echo bar$(PARAM) blah$(FLAG)
    # and so on

Y esto puede convertirse en un requisito previo de su objetivo predeterminado:

all: show_vars
    ...

EDITAR:
desea una forma de mostrar todos los objetivos posibles de un archivo MAKE arbitrario, lo que supongo que significa de forma no intrusiva. Bien...

Para hacerlo exactamente, y ser capaz de manejar archivos MAKE sofisticados, por ejemplo, que involucren reglas construidas por evaldeclaraciones, tendría que escribir algo parecido a un emulador Make. Poco práctico.

Para ver los objetivos de las reglas simples, puede escribir un archivo MAKE que actuaría como un escáner de archivos MAKE, operando en un archivo MAKE arbitrario:

  1. Obtenga todos los nombres de destino del archivo MAKE usando sed.
  2. `incluir` el archivo MAKE para usarlo para expandir variables.
  3. Utilice `show_%:; echo $$ * `para imprimir todos los objetivos

Este sería un trabajo impresionante. ¿Estás seguro de que el objetivo merece la pena?


No es exactamente lo que quiero. Me gustaría poder hacer escupir una lista de objetivos para un archivo MAKE arbitrario que pueda tener. Estoy tratando de escribir una función de finalización de argumentos para mi shell y no puedo confiar en que los makefiles tengan alguna ayuda sensata.
BitShifter

-4
grep ^[a-z].*\:$ Makefile | sed s,:,,g

1
-1: este enfoque produce falsos positivos incluso para asignaciones de variables simples ( :=).
thiton

Deberías haberlo probado primero. Aunque es bueno que lo intentes.
Konrad Viltersten
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.