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
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
Antworten:
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 make
nicht /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.
source compilationEnv.sh; $(CC) -c -o $< $^
oder ähnliches funktioniert als Aktionslinie.
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.
Die Standard-Shell für Makefile wird /bin/sh
nicht implementiert source
.
Das Ändern der Shell auf /bin/bash
macht es möglich:
# Makefile
SHELL := /bin/bash
rule:
source env.sh && YourCommand
make
und nicht nur für die Verwendung in Aktionszeilen festzulegen. So macht man Letzteres, aber es gibt keinen direkten Weg, Ersteres zu tun.
SHELL
auf Ihren eigenen Wrapper einstellen . Hier gezeigt gist.github.com/ingydotnet/99f7e259319f6b90e50a754f3053be7f
Wenn Sie lediglich Umgebungsvariablen für Make festlegen möchten, behalten Sie diese in der Makefile-Syntax bei und verwenden Sie den include
Befehl.
include other_makefile
Wenn Sie das Shell-Skript aufrufen müssen, erfassen Sie das Ergebnis in einem shell
Befehl:
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
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.
source
mit 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!
Das funktioniert bei mir. Ersetzen Sie env.sh
durch 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 makeenv
ausgegeben wird, die dann vom Makefile bezogen wird.
IGNORE := $(shell bash -c "source env.sh; env | sed 's/=/:=/' | sed 's/^/export /' > makeenv")
include makeenv
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
make
.
env.sh
speziellen Make - Zeichen enthalten, wie #
oder $
. Wenn Sie dies tun, werden include makeenv
diese Werte alle durcheinander gebracht.
Einige Konstrukte sind in shell
und 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"
shell
Variablen und make
Variablen sind nicht immer transparent austauschbar. Wenn Sie include
sie in Ihrer haben Makefile
, müssen sie der make
Syntax entsprechen, die vorschreibt, dass bestimmte Zeichen maskiert werden. Zum Beispiel ein Shell - Variablenwert wie SECRET='12#34$56'
in Ihrem .vars.sh
werden Sie Probleme verursachen , wenn sie unterwegs verwenden Sie es nachinclude vars.sh
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 .
Meine Lösung hierfür: (vorausgesetzt, Sie haben bash
, die Syntax für $@
ist tcsh
zum Beispiel anders )
Haben Sie ein Skript sourceThenExec.sh
als solches:
#!/bin/bash
source whatever.sh
$@
Stellen Sie Ihren Zielen in Ihrem Makefile bash sourceThenExec.sh
beispielsweise Folgendes vor:
ExampleTarget:
bash sourceThenExec.sh gcc ExampleTarget.C
Sie können natürlich so etwas wie STE=bash sourceThenExec.sh
oben in Ihr Makefile einfügen und dies kürzen:
ExampleTarget:
$(STE) gcc ExampleTarget.C
All dies funktioniert, weil sourceThenExec.sh
eine 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.
Wenn Sie die Variablen in die Umgebung übertragen möchten , damit sie an untergeordnete Prozesse übergeben werden, können Sie die Bashs set -a
und 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.test
auf cargo test
als 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.
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
Je nach Ihrer Version von Make und umschließenden Hülle, können Sie eine schöne Lösung über implementieren eval
, cat
und 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!
target: output_source
bash ShellScript_name.sh
Versuchen Sie dies, es wird funktionieren, das Skript befindet sich im aktuellen Verzeichnis.