Wie beziehe ich ein Skript in ein Makefile?

77

Gibt es eine bessere Möglichkeit, ein Skript, das env vars festlegt, aus einem Makefile heraus zu beziehen?

FLAG ?= 0
ifeq ($(FLAG),0)
export FLAG=1
/bin/myshell -c '<source scripts here> ; $(MAKE) $@'
else
...targets...
endif
brooksbp
quelle
2
Klingt so, als ob Sie wirklich ein Skript schreiben möchten, das Ihr Skript als Quelle für das Setzen von Envvars verwendet und dann make ausführt, anstatt make source zum Skript selbst zu machen ...
Chris Dodd
Ich habe hier eine großartige Antwort gesehen: unix.stackexchange.com/a/235254/18152 und hier: blog.153.io/2016/04/18/source-a-shell-script-in-make
zx1986

Antworten:

46

Um die gestellte Frage zu beantworten: Sie können nicht .

Das grundlegende Problem ist, dass ein untergeordneter Prozess die Umgebung des Elternteils nicht ändern kann. Die Schale wird um diesen durch keinen neuen Prozess gabeln , wenn source‚ing, aber nur diese Befehle in der aktuellen Inkarnation der Shell ausgeführt wird . Das funktioniert gut, ist aber makenicht /bin/sh(oder für welche Shell auch immer Ihr Skript gedacht ist) und versteht diese Sprache nicht (abgesehen von den Teilen, die sie gemeinsam haben).

Chris Dodd und Foo Bah haben eine mögliche Problemumgehung angesprochen, daher schlage ich eine andere vor (vorausgesetzt, Sie führen GNU make aus): Verarbeiten Sie das Shell-Skript in make-kompatiblen Text und fügen Sie das Ergebnis hinzu:

shell-variable-setter.make: shell-varaible-setter.sh
    postprocess.py @^

# ...
else
include shell-variable-setter.make
endif

unordentliche Details als Übung hinterlassen.

dmckee --- Ex-Moderator Kätzchen
quelle
2
Sie können kein Skript für ein ganzes Makefile "erstellen", aber Sie können dies für ein einzelnes Rezept tun.
Jaroslaw Nikitenko
Ja source compilationEnv.sh; $(CC) -c -o $< $^oder ähnliches funktioniert als Aktionslinie.
dmckee --- Ex-Moderator Kätzchen
1
Sie können include .env(oder eine beliebige Schlüsselwertdatei) verwenden, um Variablen für das gesamte Makefile zugänglich zu machen und dann nur die erforderlichen Variablen an das jeweilige Rezept zu übergeben.
Rypel
94

Die Standard-Shell für Makefile wird /bin/shnicht implementiert source.

Das Ändern der Shell auf /bin/bashmacht es möglich:

# Makefile

SHELL := /bin/bash

rule:
    source env.sh && YourCommand
PureW
quelle
4
@VivekAditya Dies ist natürlich eine äußerst nützliche Sache. Beachten Sie jedoch, dass das OP darum gebeten hat, eine Variable für die Verwendung durch makeund nicht nur für die Verwendung in Aktionszeilen festzulegen. So macht man Letzteres, aber es gibt keinen direkten Weg, Ersteres zu tun.
dmckee --- Ex-Moderator Kätzchen
1
Sie können dies einfach SHELLauf Ihren eigenen Wrapper einstellen . Hier gezeigt gist.github.com/ingydotnet/99f7e259319f6b90e50a754f3053be7f
ingydotnet
22

Wenn Sie lediglich Umgebungsvariablen für Make festlegen möchten, behalten Sie diese in der Makefile-Syntax bei und verwenden Sie den includeBefehl.

include other_makefile

Wenn Sie das Shell-Skript aufrufen müssen, erfassen Sie das Ergebnis in einem shellBefehl:

JUST_DO_IT=$(shell source_script)

Der Shell-Befehl sollte vor den Zielen ausgeführt werden. Dadurch werden die Umgebungsvariablen jedoch nicht festgelegt.

Wenn Sie Umgebungsvariablen im Build festlegen möchten, schreiben Sie ein separates Shell-Skript, das Ihre Umgebungsvariablen und Aufrufe erstellt. Lassen Sie dann im Makefile die Ziele das neue Shell-Skript aufrufen.

Wenn Ihr ursprüngliches Makefile beispielsweise ein Ziel hat a, möchten Sie Folgendes tun:

# mysetenv.sh
#!/bin/bash
. <script to source>
export FLAG=1
make "$@" 

# Makefile
ifeq($(FLAG),0)
export FLAG=1
a: 
    ./mysetenv.sh a
else
a:
    .. do it
endif
Foo Bah
quelle
4
Ich habe keine Kontrolle über das Skript. Muss es beschaffen.
Brooksbp
Erfasst dies die Umgebungsvariablen, wenn sie nicht exportiert werden? Auf den ersten Blick scheint es so, als würde eine neue Shell aufgerufen, ein Befehl ausgeführt, dann beendet und in die alte Umgebung zurückgekehrt.
Brooksbp
@brooksbp Der Shell-Befehl erfasst die Umgebungsvariablen nicht. Wenn Sie jedoch ein Shell-Skript schreiben, das das Original enthält, wird dies der Fall sein. Ich werde es weiter erklären
Foo Bah
17

Mit GNU Make 3.81 kann ich ein Shell-Skript aus make beziehen, indem ich:

rule:
<tab>source source_script.sh && build_files.sh

build_files.sh "ruft" die von source_script.sh exportierten Umgebungsvariablen ab.

Beachten Sie, dass mit:

rule:
<tab>source source_script.sh
<tab>build_files.sh

wird nicht funktionieren. Jede Zeile wird in einer eigenen Unterschale ausgeführt.

Samuel
quelle
1
Beachten Sie, dass sourcemit GNU Make 4.1 (und möglicherweise auch 4.0) das Beibehalten und Befehlen in derselben Zeile erforderlich wird. Mit 3.81 kann ich verschiedene Zeilen verwenden. Aus Gründen der Portabilität funktioniert eine Zeile in diesen Versionen. Das Aufrufen eines externen Skripts ist jedoch besser lesbar, wenn die Zeile zu lang wird!
Eric Platon
Bist du sicher? Mehrere Zeilen haben in 3.81 bei mir nicht funktioniert, wie ich in der Antwort erwähnt habe. Make führt jede Zeile in einer eigenen Unterschale aus, sodass mehrere Zeilen in keiner Version funktionieren sollten. Vielleicht haben Sie ein ungewöhnliches Betriebssystem oder machen Build?
Samuel
1
Mehrere
@ user5359531 Ja, das stimmt.
Samuel
10

Das funktioniert bei mir. Ersetzen Sie env.shdurch den Namen der Datei, die Sie beschaffen möchten. Es funktioniert, indem die Datei in Bash bezogen und die geänderte Umgebung nach dem Formatieren in eine aufgerufene Datei makeenvausgegeben wird, die dann vom Makefile bezogen wird.

IGNORE := $(shell bash -c "source env.sh; env | sed 's/=/:=/' | sed 's/^/export /' > makeenv")                         
include makeenv   
gdw2
quelle
1
ifdef _intel64onosx<br/> GCC=/opt/intel/bin/icc<br/> CFLAGS= -m64 -g -std=c99 IGNORE :=$(shell bash -c "source /opt/intel/bin/iccvars.sh intel64; env | sed 's/=/:=/' | sed 's/^/export /' > makeenv") include makeenv
Revher
Nizza zum Einstellen der richtigen Compiler-Umgebungen (hier 64-Bit). Vielen Dank.
Revher
Ist die Syntax, um dies auszuführen: "make IGNORE"?
John
@ John, nein. Einfach make.
GDW2
Was passiert , wenn die Werte der Variablen in Ihrem env.shspeziellen Make - Zeichen enthalten, wie #oder $. Wenn Sie dies tun, werden include makeenvdiese Werte alle durcheinander gebracht.
Donhector
6

Einige Konstrukte sind in shellund in gleich GNU Make.

var=1234
text="Some text"

Sie können Ihr Shell-Skript ändern, um die Definitionen zu erstellen. Sie müssen alle einfach seinname=value Typen sein.

Dh

[script.sh]

. ./vars.sh

[Makefile]

include vars.sh

Dann können das Shell-Skript und das Makefile dieselbe Informationsquelle gemeinsam nutzen. Ich habe diese Frage gefunden, weil ich nach einem Manifest allgemeiner Syntax gesucht habe, das in Gnu Make- und Shell-Skripten verwendet werden kann (es ist mir egal, welche Shell).

Edit: Shells und verständlich machen $ {var} . Dies bedeutet, dass Sie usw. verketten können. Var = "Eine Zeichenfolge" var = $ {var} "Zweite Zeichenfolge"

Kunstloser Lärm
quelle
Ähm, ein Shell-Skript wie das oben genannte erzeugt keine unordentlichen Details wie in der akzeptierten Antwort. Am Ende ähnelt es einer kbuild- Datei.
Kunstloser Lärm
1
shellVariablen und makeVariablen sind nicht immer transparent austauschbar. Wenn Sie includesie in Ihrer haben Makefile, müssen sie der makeSyntax entsprechen, die vorschreibt, dass bestimmte Zeichen maskiert werden. Zum Beispiel ein Shell - Variablenwert wie SECRET='12#34$56'in Ihrem .vars.shwerden Sie Probleme verursachen , wenn sie unterwegs verwenden Sie es nachinclude vars.sh
donhector
1
Ich denke, es gibt zwei Anwendungsfälle; Das OP hat keine Kontrolle über das Skript. Ihr Standpunkt ist also sehr gut, da Sie höchstwahrscheinlich Daten erhalten, die maskiert werden müssen. Dies sind "% $ #". Wenn der Anwendungsfall darin besteht, Variablen zwischen Tools zu teilen, die Umgebungsvariablen verwenden und make erstellen, kann diese Lösung funktionieren. Es hat den Vorteil, dass nur eine Informationsquelle und keine zusätzlichen Prozesse vorhanden sind. Wenn Sie keine Kontrolle über das Shell-Skript haben, ist diese Antwort nicht so nützlich. Auch fraglich, warum Sie "$% #" in einem Variablennamen wollen würden. Wenn Sie Elemente berechnen, müssen Sie dem Abschnitt "Bearbeiten:" folgen.
Kunstloses Rauschen
3

Ich mag die Antwort von Foo Bah sehr, bei der make das Skript aufruft und das Skript zurückruft, um make zu erstellen. Um diese Antwort zu erweitern, habe ich Folgendes getan:

# Makefile
.DEFAULT_GOAL := all 

ifndef SOME_DIR

%:
<tab>. ./setenv.sh $(MAKE) $@

else

all:
<tab>...

clean:
<tab>...

endif

- -

# setenv.sh
export SOME_DIR=$PWD/path/to/some/dir

if [ -n "$1" ]; then
    # The first argument is set, call back into make.
    $1 $2
fi

Dies hat den zusätzlichen Vorteil, dass $ (MAKE) verwendet wird, falls jemand ein eindeutiges make-Programm verwendet, und dass auch alle in der Befehlszeile angegebenen Regeln verarbeitet werden, ohne dass der Name jeder Regel dupliziert werden muss, wenn SOME_DIR nicht definiert ist .

Samuel
quelle
Wenn Sie eine ältere Version von make haben und .DEFAULT_GOAL nicht unterstützt wird, schlägt der erste Zweig des ifndef mit "make: *** Keine Ziele. Stop." Fehl. Sie können dies beheben, indem Sie der impliziten Regel "all" hinzufügen, und zwar wie folgt: "all%:". Beachten Sie, dass dies bei neueren Versionen von make zu einer Warnung "*** gemischte implizite und normale Regeln: veraltete Syntax" führt.
Samuel
1

Meine Lösung hierfür: (vorausgesetzt, Sie haben bash, die Syntax für $@ist tcshzum Beispiel anders )

Haben Sie ein Skript sourceThenExec.shals solches:

#!/bin/bash
source whatever.sh
$@

Stellen Sie Ihren Zielen in Ihrem Makefile bash sourceThenExec.shbeispielsweise Folgendes vor:

ExampleTarget:
    bash sourceThenExec.sh gcc ExampleTarget.C

Sie können natürlich so etwas wie STE=bash sourceThenExec.shoben in Ihr Makefile einfügen und dies kürzen:

ExampleTarget:
    $(STE) gcc ExampleTarget.C

All dies funktioniert, weil sourceThenExec.sheine Unterschale geöffnet wird, die Befehle dann jedoch in derselben Unterschale ausgeführt werden.

Der Nachteil dieser Methode ist, dass die Datei für jedes Ziel bezogen wird, was möglicherweise unerwünscht ist.

Chris
quelle
1

Wenn Sie die Variablen in die Umgebung übertragen möchten , damit sie an untergeordnete Prozesse übergeben werden, können Sie die Bashs set -aund verwenden set +a. Ersteres bedeutet: "Wenn ich eine Variable setze, setze auch die entsprechende Umgebungsvariable." Das funktioniert also für mich:

check:
    bash -c "set -a && source .env.test && set +a && cargo test"

Das wird alles in Pass .env.testauf cargo testals Umgebungsvariablen.

Beachten Sie, dass Sie auf diese Weise eine Umgebung an Unterbefehle weitergeben können, aber keine Makefile-Variablen festlegen können (die sowieso anders sind). Wenn Sie Letzteres benötigen, sollten Sie hier einen der anderen Vorschläge ausprobieren.

Paul A Jungwirth
quelle
0

Wenn Sie nur wenige bekannte Variablen benötigen, die in makefile exportiert werden können, finden Sie hier ein Beispiel für meine Verwendung.

$ grep ID /etc/os-release 

ID=ubuntu
ID_LIKE=debian


$ cat Makefile

default: help rule/setup/lsb

source?=.

help:
        -${MAKE} --version | head -n1

rule/setup/%:
        echo ID=${@F}

rule/setup/lsb: /etc/os-release
        ${source} $< && export ID && ${MAKE} rule/setup/$${ID}


$ make

make --version | head -n1
GNU Make 3.81
. /etc/os-release && export ID && make rule/setup/${ID}
make[1]: Entering directory `/tmp'
echo ID=ubuntu
ID=ubuntu

- http://rzr.online.fr/q/gnumake

RzR
quelle
0

Je nach Ihrer Version von Make und umschließenden Hülle, können Sie eine schöne Lösung über implementieren eval, catund Verkettungs Anrufe mit &&:

ENVFILE=envfile

source-via-eval:
  @echo "FOO: $${FOO}"
  @echo "FOO=AMAZING!" > $(ENVFILE)
  @eval `cat $(ENVFILE)` && echo "FOO: $${FOO}"

Und ein kurzer Test:

> make source-via-eval
FOO:
FOO: AMAZING!
tankthinks
quelle
-1

Eine andere Möglichkeit wäre, ein sh-Skript zu erstellen, beispielsweise run.shdie erforderlichen Skripte zu erstellen und make innerhalb des Skripts aufzurufen.

#!/bin/sh
source script1
source script2 and so on

make 
Veera
quelle
-1
target: output_source
    bash ShellScript_name.sh

Versuchen Sie dies, es wird funktionieren, das Skript befindet sich im aktuellen Verzeichnis.

Amit Pandya
quelle