2010-05-15 9 views
25
somevar := apple 
export somevar 
update := $(shell echo "v=$$somevar") 

all: 
    @echo $(update) 

Tenía la esperanza de manzana como la salida del comando, sin embargo es vacía, lo que me hace pensar en la exportación y := la expansión de variables que tienen lugar en las diferentes fases. cómo superar esto?Makefile inicialización de variables y exportación

Respuesta

25

el problema es que export exporta la variable a los subniveles utilizados por los comandos; no está disponible para expansión en otras asignaciones. Por lo tanto, no intente obtenerlo del entorno fuera de una regla.

somevar := apple 
export somevar 

update1 := $(shell perl -e 'print "method 1 $$ENV{somevar}\n"') 
# Make runs the shell command, the shell does not know somevar, so update1 is "method 1 ". 

update2 := perl -e 'print "method 2 $$ENV{somevar}\n"' 
# Now update2 is perl -e 'print "method 2 $$ENV{somevar}\n"' 

# Lest we forget: 
update3 := method 3 $(somevar) 

all: 
    echo $(update1) 
    $(update2) 
    echo $(update3) 
    perl -e 'print method 4 "$$ENV{somevar}\n"' 
+0

De hecho, es bug [http://savannah.gnu.org/bugs/?10593] que no está fijado y tal vez nunca lo será. Gracias, intentaré no molestarte con preguntas extrañas sobre los archivos make. – Pablo

+0

@Michael: de mi lectura del manual de GNUMake no es un error. (Y usted no tiene que poner @Beta delante de un comentario a mi respuesta.) – Beta

+0

Esto no es raro. Necesito este. ¿Por qué necesito esto? Porque el entorno es la única forma a prueba de balas de pasar una cadena arbitraria a un script externo. Por ejemplo, supongamos que tengo una variable V que contiene cosas: citas de varios tipos, metacaracteres de shell significativos como dólares, escapes de barra invertida, etc. Me gustaría que un script de shell usara la variable $ V. – Kaz

9

ejecutar el archivo MAKE

foo:=apple 
export foo 
all: 
     @echo ">"$(shell echo "$$foo") 
     @echo ">""$$foo" 

da para mí (con foo indefinida en el medio ambiente)

$ make 
> 
>apple 

$ make foo=bar 
> 
>apple 

$ export foo=bar; make 
>bar 
>apple 

$ export foo=bar; make foo=bar 
>bar 
>bar 

Trate de usar el formulario citado (update := "v=$$somevar") y dejar que la expansión mango cáscara cuando un comando es ejecutar (que todavía tendrá la exportación)

+0

Necesito mantener la forma que la función de la cáscara se queda fuera de la orden del objetivo, ya que cuenta con sentencia condicional en él en vida real. – Pablo

+1

+1 para mostrar las variaciones; a falta de eso: (a) '$ (shell ...)' solo ve variables que ya estaban presentes en el propio entorno 'make' cuando comenzó; (b) definir variables en la línea de comando 'make' anula las definiciones del mismo nombre dentro del archivo MAKE. – mklement0

+0

¿Qué pasa con la salida de "apple" con la primera expresión, y no hacer nada fuera de makefile? – holms

11

@Beta's answer contiene el puntero crucial: con GNU make, las variables marcados con export sólo están disponibles para [los proyectiles lanzados por] comandos receta (comandos que forman parte de las reglas), lamentablemente no a invocaciones de $(shell ...) (que sólo ven el ambiente que make sí vio cuando fue lanzado).

Hay una solución, sin embargo: explícitamente pasar la variable exportado como una variable de entorno a la función shell:

update := $(shell somevar='$(somevar)' perl -e 'print "$$ENV{somevar}"') 

anteponiendo el comando shell con <var>=<val>, se añade que la definición como una variable de entorno para el entorno que ve el comando, esta es una característica de shell genérica.

Caveat: @Kaz señala en un comentario de que este método se comporta mal si $(somevar) contiene ciertos caracteres, porque la expansión variable es verbatim (sin escape), que puede romper el comando shell resultante, y sugiere la. siguiente variante de trabajar también con incrustados ' casos (rompe el valor de entrada en subcadenas-citado individuales con citado ' empalmados in):

update := $(shell somevar='$(subst ','\'',$(somevar))' perl -e 'print "$$ENV{somevar}"') 

Esto debería funcionar con todos los valores excepto multi-line unos (que son raros; no hay ninguna solución para los valores de líneas múltiples de la que soy consciente).

En una nota lateral, literales $ caracteres. en valores deben ser representados como $$, de lo contrario make las interpretarán como referencias a sus propias variables.

Tenga en cuenta que he deliberadamente sin elegir originales la declaración de la OP, update := $(shell echo "v=$$somevar"), para la demostración, ya que contiene una trampa que confunde el problema: debido a la forma en la cáscara evalúa una línea de comandos, somevar=apple echo v=$somevar no evalúa a v=apple, porque la referencia $somevar se amplía antes desomevar=apple entra en vigencia. Para conseguir el efecto deseado en este caso, habría que utilizar declaraciones: update := $(shell export somevar="$(somevar)"; echo "v=$$somevar")


En cuanto al debate-vs-función de error:

Si bien se puede argumentar que la función shelldebe ver en el mismo entorno que los comandos de receta, la documentación no hace tal promesa - ver http://www.gnu.org/software/make/manual/make.html#Shell-Function. Por el contrario, http://www.gnu.org/software/make/manual/make.html#Variables_002fRecursion solo menciona hacer que las variables exportadas estén disponibles para los comandos receta.

+0

Esta solución no es una solución. El paso de material a través del entorno ** ya es una solución ** a los problemas de cotización y escape. Al construir la sintaxis "VAR = val command ..." *, volvemos a introducir esos problemas. – Kaz

+0

@Kaz: Gracias por señalar el problema: he actualizado la respuesta con una advertencia. Resolver el problema de forma genérica no es trivial, pero si se puede vivir con las limitaciones (por ejemplo, cuando se asigna un valor limitado como '$ (PATH)'), el enfoque en la mano todavía puede ser una opción. – mklement0

+0

La solución más simple para esto es usar '$ (subst ...)' para reemplazar las ocurrencias de ' ''' con' \ '''. Luego, interpola los datos resultantes entre comillas simples en el lado de la carcasa. (Luché con esto hoy y resuelto de esa manera.) – Kaz

1

Aunque export no funciona muy bien con $(shell ...), hay una solución simple. Podemos pasar los datos al script de shell a través de la línea de comando.

Ahora, por supuesto, el paso medio ambiente es robusto frente a los problemas de escape y citando. Sin embargo, el lenguaje de shell tiene un único método de cotización de cotización '...' que maneja todo. El único problema es que no hay forma de obtener una sola cotización allí; pero por supuesto que se resuelve mediante la terminación de la cita, la barra invertida-escape de la comilla simple necesaria y comenzar una nueva cita: En otras palabras:

ab'cd -> 'ab'\''cd' 

En el script ejecutado por $(shell ...) que acabamos de generar una asignación variable de la formulario var='$(...)', donde $(...) es alguna expresión de expresión que interpola material escapado de forma adecuada. Por lo tanto, Makefile:

somevar := apple with 'quoted' "stuff" and dollar $$signs 

shell_escape = $(subst ','\'',$(1)) 

update := $(shell v='$(call shell_escape,$(somevar))'; echo $$v > file.txt) 

.phony: all 

all: 
    cat file.txt 

de ejecución de la muestra:

$ make 
cat file.txt 
apple with 'quoted' "stuff" and dollar $signs 

Si queremos comunicar una variable de entorno a un comando, podemos hacer que el uso de la sintaxis del shell VAR0=val0 VAR1=val1 ... VARn=valn command arg .... Esto se puede ilustrar con algunas alteraciones menores de lo anterior Makefile:

somevar := apple with 'quoted' "stuff" and dollar $$signs 

shell_escape = $(subst ','\'',$(1)) 

update := $(shell somevar='$(call shell_escape,$(somevar))' env > file.txt) 

.phony: all 

all: 
     grep somevar file.txt 

Run:

$ make 
grep somevar file.txt 
somevar=apple with 'quoted' "stuff" and dollar $signs 

file.txt contiene un volcado de variables de entorno, en el que podemos ver somevar. Si export en GNU Make hicieron lo correcto, hubiéramos podido acaba de hacer:

export somevar 
update := $(shell env > file.txt) 

pero el resultado final es el mismo.

Dado que el resultado final que desea es echo $(update), podría shell_escape de todos modos, incluso si pasa GNU exportado vars a $(shell ...).Es decir, mirar a una más Makefile:

somevar := apple with 'quoted' "stuff" and dollar $$signs 

shell_escape = $(subst ','\'',$(1)) 

update := $(shell v='$(call shell_escape,$(somevar))'; echo $$v) 

.phony: all 

all: 
    @echo '$(call shell_escape,$(update))' 
    @echo $(update) 

Salida:

apple with 'quoted' "stuff" and dollar $signs 
apple with quoted stuff and dollar 
Cuestiones relacionadas