2012-10-08 77 views
11

quiero definir una función de shell¿Cómo se definen las funciones de shell globales en un Makefile?

#!/bin/sh 

test() 
{ 
    do_some_complicated_tests $1 $2; 
    if something; then 
    build_thisway $1 $2; 
    else 
    build_otherway $1 $2; 
    fi 
} 

de tal manera que pueda utilizarlo en todas las reglas de mi Makefile, tales como:

foo: bar 
    test foo baz 

Para que quede claro, yo quiero la cáscara función para ser parte del Makefile. ¿Cuál es la forma más elegante de hacer esto? Puntos de bonificación si puede hacerlo sin llamar de forma recursiva.


Antecedentes: Mi problema real es que make -n produce una salida muy larga y es ilegible. Cada regla usa casi la misma secuencia de comandos de shell ilegibles, y hay muchas reglas. La solución anterior haría que la salida de make -n fuera más útil.

+0

he hecho @JonathanLeffler dijo no podía' t hecho?Voy a estar insufriblemente complacido conmigo mismo por al menos un mes. – Beta

+0

¿Por qué no simplemente poner el código en un script de shell y llamar eso? – reinierpost

Respuesta

7

Esta solución no se basa en un archivo temporal externo y no le obliga a jugar con la variable SHELL.

TESTTOOL=sh -c '\ 
    do_some_complicated_tests $$1 $$2; \ 
    if something; then 
    build_thisway $$1 $$2; 
    else 
    build_otherway $$1 $$2; 
    fi' TESTTOOL 

ifneq (,$(findstring n,$(MAKEFLAGS))) 
TESTTOOL=: TESTTOOL 
endif 

foo: bar 
    ${TESTTOOL} foo baz 

Los ifneq…endif cheques de bloque para la bandera -n en la línea de comandos y establece la expansión de TESTTOOL a : TESTTOOL que es fácil de leer y seguro para ejecutar.

La mejor solución podría ser convertir la función de shell en un programa real si esta es una opción para usted.

3

Algo me dice que sería mejor de filtrar la salida de make -n, pero lo que se pide es posible:

define test 
    @echo do some tests with $(1) and $(2); \ 
    SOMETHING=$(1)_3 ; \ 
    if [ $$SOMETHING == foo_3 ]; then \ 
    echo build this way $(1) $(2); \ 
    else \ 
    echo build another way $(1) $(2) ; \ 
    fi 
endef 

someTarget: 
    $(call test,foo,bar) 

someOtherTarget: 
    $(call test,baz,quartz) 
+0

Gracias! Desafortunadamente, esta solución es exactamente la que estaba tratando de evitar. – Holger

+0

@Holger, ¿qué tiene de malo? – Beta

+0

Funciona bien, pero la salida de 'make -n' se volverá ilegible porque el script de shell volverá a aparecer para cada destino al que se llame' test'. Peor aún, el script estará en una línea. Podría usar filtros, pero también tendría que depurar los filtros, lo cual quiero evitar. – Holger

5

Dado que la cuestión se marca gnu-make, es posible ser feliz con una solución de Beta, pero Creo que la mejor solución es 1) renombrar la función a cualquier otra cosa que no sea test (evite nombres como ls y cd también); para el propósito de la discusión supongamos que lo ha cambiado de nombre foo, 2) simplemente escriba un script llamado foo y llámelo del Makefile. Si realmente desea definir una función de shell en el Makefile, simplemente definirlo para cada regla:

F = foo() { \ 
    do_some_complicated_tests $$1 $$2; \ 
    if something; then \ 
    build_thisway $$1 $$2; \ 
    else \ 
    build_otherway $$1 $$2; \ 
    fi \ 
} 

all: bar 
    @$(F); foo baz bar 

No es que debes tener continuaciones de línea en cada línea de la definición de foo, y el $ están todos escapado hasta que se pasan al caparazón.

+1

¡Gracias! Definir la función en cada regla no resuelve el problema de que la salida de 'make -n' es demasiado larga. En este caso, preferiría la solución de Beta. Sin embargo, lo siguiente podría funcionar para mí: podría agregar la dependencia 'all: foo.sh' y una regla' foo.sh: 'que solo genera el script en' foo.sh'. Es lento y me gustaría encontrar algo más elegante, pero realmente quiero que todo esté en un solo archivo. – Holger

+0

Genial; esta es mi favorita. Lo que más me gusta es la siguiente abreviación: 'F_DEF = foo() {como arriba}', y luego 'F = @ $ (F_DEF); foo', y luego puedes reutilizarlo desde cualquier otro lugar como '$ (F) arg1 ... argn'. –

3

No creo que esto califica como "elegante", pero parece que hacer lo que quiere:

## 
## --- Start of ugly hack 
## 

THIS_FILE := $(lastword $(MAKEFILE_LIST)) 

define shell-functions 
: BEGIN 
    # Shell syntax here 
    f() 
    { 
    echo "Here you define your shell function. This is f(): [email protected]" 
    } 

    g() 
    { 
    echo "Another shell function. This is g(): [email protected]" 
    } 
: END 
endef 

# Generate the file with function declarations for the shell 
$(shell sed -n '/^: BEGIN/,/^: END/p' $(THIS_FILE) > .functions.sh) 

# The -i is necessary to use --init-file 
SHELL := /bin/bash --init-file .functions.sh -i 

## 
## -- End of ugly hack 
## 

all: 
    @f 1 2 3 4 
    @g a b c d 

La ejecución de este produce:

$ make -f hack.mk 
Here you define your shell function. This if f(): 1 2 3 4 
Another shell function. This is g(): a b c d 

Correr con -n produce:

$ make -f hack.mk -n 
f 1 2 3 4 
g a b c d 

Esto se basa en el hecho de que las macros se definen entre define y endef no son interpretados por make en absoluto hasta que realmente se utilizan, por lo que puede usar sintaxis de shell directamente y no es necesario que finalice cada línea con una barra diagonal inversa. Por supuesto, bombardeará si llama a la macro shell-functions.

+0

Su método para extraer el archivo .sh es muy útil, gracias. Esto está cerca de lo que quiero. Si escribo un archivo .sh temporal de todos modos, no me importa si estoy usando f y g como funciones de shell; Yo usaría f.sh y g.sh en su lugar. Entonces no es tan feo porque no hay necesidad de redefinir '$ (SHELL)'. – Holger

+0

La ventaja de utilizar el truco 'SHELL' es que todo sucede de forma automática y transparente. No tiene que cargar los archivos '.sh' en el shell explícitamente cada vez que los necesite. – Idelic

+0

Pensé que make reinicia $ (SHELL) para cada comando? Así que efectivamente 'make all' arriba, ejecuta $ (SHELL) y las tuberías" f 1 2 3 4 "en su stdin. En la línea siguiente, make inicia una nueva instancia de $ (SHELL) y canaliza "g a b c d" en su stdin. ¿Es eso cierto? Si es así, no veo la ventaja sobre el uso de archivos separados (ya que 'bash --init-file ...' tiene que volver a leer el archivo .sh de todos modos). [Para que quede claro, mi alternativa propuesta es usar '@./F.sh 1 2 3 4' en lugar de' @f 1 2 3 4'.] – Holger

3

Esto es realmente ghetto, pero lo que sea. Yo uso zsh, pero estoy seguro de que hay equivalentes de bash y sh.

Makefile:

export ZDOTDIR := ./ 

SHELL := /usr/bin/env zsh 

default: 
    f 

.zshenv, mismo directorio que Makefile:

f() { echo 'CHECK OUT THIS AWESOME FUNCTION!' } 

La variable ZDOTDIR hace mirada zsh en el directorio actual para dotfiles. Luego, simplemente pegue lo que quiera en .zshenv.

$ make 
f 
CHECK OUT THIS AWESOME FUNCTION! 
1

TL; DR:

En el makefile:

SHELL=bash 
BASH_FUNC_command%%=() { ... } 
export BASH_FUNC_command%% 

Respuesta larga

que he hecho algo similar con bash, en un contexto no hacer que trabaja aquí .

declaré mis funciones de shell de bash y luego los exportan con:

export -f function-name 

Son entonces, naturalmente disponible si el mismo intérprete se invoca como un sub-proceso, en su caso, de maquillaje.

Ejemplo:

$ # define the function 
$ something() { echo do something here ; } 
$ export -f something 

$ # the Makefile 
$ cat > Makefile <<END 
SHELL=bash 
all: ; something 
END 
$ 

¡Pruébalo

$ make 
something 
do something here 
$ 

¿Cómo funciona este aspecto en el medio ambiente?

$ env | grep something 
BASH_FUNC_something%%=() { echo do something here 

Así que la pregunta para usted sería cómo establecer las variables de entorno desde el Makefile. El patrón parece estar BASH_FUNC_ nombre-función %% =() {función corporal}

directamente dentro maquillaje

Entonces, ¿cómo funciona esto para usted? Prueba este makefile

SHELL=bash 
define BASH_FUNC_something-else%% 
() { 
    echo something else 
} 
endef 
export BASH_FUNC_something-else%% 

all: ; something-else 

y probarlo:

$ make 
something-else 
something else 

Es un poco feo en el Makefile, pero no presenta la fealdad de hacer algo -n

Cuestiones relacionadas