2012-10-08 13 views
11

voglio definire una funzione di shellCome definire le funzioni globali della shell in un Makefile?

#!/bin/sh 

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

in modo tale che posso usarlo in ogni regola della mia Makefile, come ad esempio:

foo: bar 
    test foo baz 

Per essere chiari, io voglio il guscio funzione per far parte del Makefile. Qual è il modo più elegante per farlo? Punti bonus se puoi farlo senza chiamare ricorsivamente.


Background: Il mio problema reale è che make -n produce un'uscita molto lunga e illeggibile. Ogni regola utilizza quasi la stessa sequenza di comandi di shell non leggibili e ci sono molte regole. La soluzione di cui sopra renderebbe più utile l'output di make -n.

+0

ho fatto @JonathanLeffler detto couldn' essere fatto?Sarò insopportabilmente compiaciuto di me stesso per almeno un mese. – Beta

+0

Perché non inserire il codice in uno script di shell e chiamarlo? – reinierpost

risposta

7

Questa soluzione non si basa su un file temporaneo esterno e non ti costringe ad armeggiare con la variabile 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 

I controlli ifneq…endif blocchi per la bandiera -n sulla riga di comando e imposta l'espansione di TESTTOOL a : TESTTOOL che è facile da leggere e sicuro da eseguire.

La soluzione migliore potrebbe essere trasformare la funzione shell in un programma effettivo se questa è un'opzione per voi.

3

Qualcosa mi dice che sarebbe meglio filtrare l'output di make -n, ma ciò che si chiede è possibile:

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

Grazie! Sfortunatamente, questa soluzione è esattamente quella che stavo cercando di evitare. – Holger

+0

@Holger, cosa c'è che non va? – Beta

+0

Funziona bene, ma l'output di 'make -n' diventerà illeggibile perché lo script della shell riapparirà per ogni target che viene chiamato' test'. Ancora peggio, la sceneggiatura sarà su una riga. Potrei usare i filtri, ma poi avrei bisogno di eseguire il debug dei filtri, cosa che voglio evitare. – Holger

5

Dal momento che la questione è aggiunto gnu-make, si potrebbe essere felici con la soluzione di Beta, ma Penso che la soluzione migliore sia quella di 1) rinominare la funzione diversa da test (evitare nomi come ls e cd); a scopo di discussione, supponiamo di averlo rinominato come foo, 2) semplicemente scrivendo uno script chiamato foo e invocandolo dal Makefile. Se davvero si vuole definire una funzione di shell nel Makefile, basta definire per ogni regola:

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 

Non che è necessario disporre di continuazione di una riga su ogni riga della definizione di foo, e il $ sono tutti scappati così che sono passati alla shell.

+1

Grazie! Definire la funzione in ogni regola non risolve il problema che l'output di 'make -n' è troppo lungo. In questo caso, preferirei la soluzione di Beta. Comunque, il seguente potrebbe funzionare per me: potrei aggiungere la dipendenza 'all: foo.sh' e una regola' foo.sh: 'che restituisce lo script in' foo.sh'. È lento e sarei felice di trovare qualcosa di più elegante, ma voglio davvero che tutto sia in un unico file. – Holger

+0

Grande; questo è il mio preferito Ciò che mi piace ancora di più è la seguente abbreviazione: 'F_DEF = foo() {come sopra}', e quindi 'F = @ $ (F_DEF); foo', e quindi puoi riutilizzarlo da qualsiasi altra parte come '$ (F) arg1 ... argn'. –

3

Io non credo che questo si qualifica come "elegante", ma sembra di fare ciò che si vuole:

## 
## --- 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 

L'esecuzione di questo 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 

eseguendolo con -n produce:

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

Questo si basa sul fatto che le macro definite tra define e endef non vengono interpretati da make finché non vengono effettivamente utilizzati, quindi è possibile utilizzare direttamente la sintassi della shell e non è necessario terminare ogni riga con una barra rovesciata. Ovviamente, bombarderà se chiami la macro shell-functions.

+0

Il tuo metodo di estrazione del file .sh è molto utile, grazie. Questo è vicino a quello che voglio. Se sto scrivendo comunque un file .sh temporaneo, allora non mi interessa che io stia usando f e g come funzioni di shell; Vorrei usare f.sh e g.sh invece. Quindi non è così brutto perché non c'è bisogno di ridefinire '$ (SHELL)'. – Holger

+0

Il vantaggio dell'uso del trucco 'SHELL' è che tutto avviene automaticamente e in modo trasparente. Non è necessario caricare i file '.sh' nella shell esplicitamente ogni volta che ne hai bisogno. – Idelic

+0

Ho pensato che far riavviare $ (SHELL) per ogni comando? In modo efficace 'make all' above, esegue $ (SHELL) e pipe" f 1 2 3 4 "nel suo stdin. Nella riga successiva, make avvia una nuova istanza di $ (SHELL) e pipe "g a b c d" nel suo stdin. È vero? Se è così, non vedo il vantaggio rispetto all'utilizzo di file separati (dato che 'bash --init-file ...' deve rileggere comunque il file .sh). [Per essere chiari, la mia alternativa proposta è quella di usare '@./F.sh 1 2 3 4' invece di' @f 1 2 3 4'.] – Holger

3

Questo è veramente ghetto, ma qualunque cosa. Io uso zsh, ma sono sicuro che ci sono bash e sh equivalenti.

Makefile:

export ZDOTDIR := ./ 

SHELL := /usr/bin/env zsh 

default: 
    f 

.zshenv, stessa directory Makefile:

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

La variabile ZDOTDIR rende sguardo zsh nella directory corrente per dotfile. Quindi tieni semplicemente quello che vuoi in .zshenv.

$ make 
f 
CHECK OUT THIS AWESOME FUNCTION! 
1

TL; DR:

nel makefile:

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

Risposta lunga

che ho fatto qualcosa di simile con bash in un contesto non-fare, che lavora qui .

ho dichiarato le mie funzioni di shell in bash e poi esportati con:

export -f function-name 

Sono poi naturalmente disponibili se la stessa shell viene invocata come un sub-processo, nel tuo caso, dal trucco.

Esempio:

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

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

Prova ora

$ make 
something 
do something here 
$ 

Come funziona questo sguardo nell'ambiente?

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

Quindi la domanda per voi sarebbe come impostare le variabili di ambiente all'interno del Makefile. Il modello sembra essere BASH_FUNC_ nome-funzione %% =() {funzione-corpo}

direttamente all'interno make

Così come fa questo lavoro per voi? Prova questo makefile

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

all: ; something-else 

e provarlo:

$ make 
something-else 
something else 

E 'un po' brutto nel Makefile, ma non presenta alcuna bruttura per fare qualcosa -n

Problemi correlati