Чи є кращий спосіб отримати скрипт, який встановлює env vars, із файлу make?
FLAG ?= 0
ifeq ($(FLAG),0)
export FLAG=1
/bin/myshell -c '<source scripts here> ; $(MAKE) $@'
else
...targets...
endif
Чи є кращий спосіб отримати скрипт, який встановлює env vars, із файлу make?
FLAG ?= 0
ifeq ($(FLAG),0)
export FLAG=1
/bin/myshell -c '<source scripts here> ; $(MAKE) $@'
else
...targets...
endif
Відповіді:
Відповісти на запитання: не можна .
Основне питання полягає в тому, що дочірній процес не може змінити батьківське середовище. Оболонка обходить це, не розгалужуючи новий процес під час source
створення, а просто виконуючи ці команди в поточному втіленні оболонки. Це чудово працює, але make
це не так /bin/sh
(або для будь-якої оболонки, для якої призначений ваш сценарій), і не розуміє цієї мови (крім бітів, які вони мають спільні).
Кріс Додд та Фу Ба вирішили одне із можливих обхідних шляхів, тому я запропоную інший (за умови, що у вас запущено GNU make): обробити сценарій оболонки у сумісний текст і включити результат:
shell-variable-setter.make: shell-varaible-setter.sh
postprocess.py @^
# ...
else
include shell-variable-setter.make
endif
брудні деталі, залишені як вправа.
source compilationEnv.sh; $(CC) -c -o $< $^
чи подібне буде працювати як лінія дії.
include .env
(або будь-який файл ключ-значення), щоб зробити змінні доступними для всього Makefile, а потім передати лише необхідні у відповідний рецепт.
Шаблон за замовчуванням Makefile - це той, /bin/sh
який не реалізований source
.
Зміна оболонки на /bin/bash
дозволяє:
# Makefile
SHELL := /bin/bash
rule:
source env.sh && YourCommand
make
а не лише для використання в рядках дій. Це як зробити останнє, але прямого способу зробити перше не існує.
SHELL
власну обгортку. Показано тут gist.github.com/ingydotnet/99f7e259319f6b90e50a754f3053be7f
Якщо ваша мета - просто встановити змінні середовища для Make, чому б не зберегти його в синтаксисі Makefile і не використати include
команду?
include other_makefile
Якщо вам потрібно викликати скрипт оболонки, захопіть результат shell
командою:
JUST_DO_IT=$(shell source_script)
команда shell повинна виконуватися перед цілями. Однак це не встановить змінні середовища.
Якщо ви хочете встановити змінні середовища у збірці, напишіть окремий скрипт оболонки, який джерела ваших змінних середовища та викликів. Потім у файлі make попросіть цілі викликати новий сценарій оболонки.
Наприклад, якщо ваш оригінальний make-файл має цільову точку a
, ви хочете зробити щось подібне:
# 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
За допомогою GNU Make 3.81 Я можу отримати скрипт оболонки з make, використовуючи:
rule:
<tab>source source_script.sh && build_files.sh
build_files.sh "отримує" змінні середовища, експортовані source_script.sh.
Зверніть увагу, що використання:
rule:
<tab>source source_script.sh
<tab>build_files.sh
не буде працювати. Кожен рядок запускається у своїй підоболонці.
source
та команди в одному рядку стають необхідними з GNU Make 4.1 (а можливо, також і 4.0). Використовуючи 3.81, я можу використовувати різні рядки. Для мобільності в цих версіях працює один рядок. Виклик зовнішнього сценарію стає більш читабельним, якщо рядок стає занадто довгим!
Це працює для мене. Замініть env.sh
ім'я файлу, який потрібно створити. Він працює, шукаючи файл у bash і виводячи модифіковане середовище після його форматування у файл, makeenv
який називається, а потім отримує файл make.
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
містять спеціальні символи Make, такі як #
або $
. Коли ви це зробите, include makeenv
ці значення будуть заплутані.
Деякі конструкції однакові в shell
і в GNU Make
.
var=1234
text="Some text"
Ви можете змінити свій сценарій оболонки, щоб отримати вихідні дані. Усі вони повинні бути простими name=value
типами.
Тобто,
[script.sh]
. ./vars.sh
[Makefile]
include vars.sh
Тоді сценарій оболонки та Makefile можуть спільно використовувати одне й те саме «джерело» інформації. Я знайшов це запитання, тому що шукав маніфест загальноприйнятого синтаксису, який можна використовувати у скриптах Gnu Make та shell (мені все одно, яка оболонка).
Змінити: оболонки та дати зрозуміти $ {var} . Це означає, що ви можете об'єднати і т.д., var = "Один рядок" var = $ {var} "Другий рядок"
shell
змінні та make
змінні не завжди прозоро взаємозамінні. Коли ви include
їх у своєму Makefile
, вони повинні відповідати make
синтаксису, який нав'язує певні символи, які слід уникати. Наприклад, значення змінної оболонки, як SECRET='12#34$56'
у вашому .vars.sh
, створить вам проблеми, коли ви будете використовувати його післяinclude vars.sh
Мені дуже подобається відповідь Фу Ба, де make викликає сценарій, а сценарій дзвонить назад, щоб зробити. Щоб розширити цю відповідь, я зробив це:
# 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
Це має додаткову перевагу використання $ (MAKE) у випадку, якщо хтось використовує унікальну програму make, а також буде обробляти будь-яке правило, вказане в командному рядку, без необхідності дублювати ім'я кожного правила у випадку, коли SOME_DIR не визначено .
Моє рішення цього: (припускаючи, що у вас є, наприклад bash
, синтаксис для $@
відрізняється tcsh
)
Майте сценарій sourceThenExec.sh
як такий:
#!/bin/bash
source whatever.sh
$@
Потім у своєму файлі make вводьте перед цілями bash sourceThenExec.sh
, наприклад:
ExampleTarget:
bash sourceThenExec.sh gcc ExampleTarget.C
Звичайно, ви можете помістити щось на кшталт STE=bash sourceThenExec.sh
у верхній частині вашого make-файлу і скоротити це:
ExampleTarget:
$(STE) gcc ExampleTarget.C
Все це працює, оскільки sourceThenExec.sh
відкриває під оболонку, але тоді команди виконуються в тій самій під оболонці.
Недоліком цього методу є те, що файл отримується для кожної цілі, що може бути небажаним.
Якщо ви хочете отримати змінні у середовищі , щоб вони передавалися дочірнім процесам, тоді ви можете використовувати bash set -a
та set +a
. Перший означає: "Коли я встановлюю змінну, встановлюю відповідну змінну середовища". Отже, це працює для мене:
check:
bash -c "set -a && source .env.test && set +a && cargo test"
Це буде проходити все в .env.test
на cargo test
як змінні середовища.
Зверніть увагу, що це дозволить вам передавати середовище підкомандам, але не дозволить встановлювати змінні Makefile (які в будь-якому випадку різні). Якщо вам потрібно останнє, спробуйте одну з інших пропозицій тут.
Якщо вам потрібні лише кілька відомих змінних, експорт у makefile може бути опцією, ось приклад того, що я використовую.
$ 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
В залежності від версії Марки і огороджувальних оболонок, ви можете реалізувати хороше рішення з допомогою eval
, cat
і ланцюжок викликів з &&
:
ENVFILE=envfile
source-via-eval:
@echo "FOO: $${FOO}"
@echo "FOO=AMAZING!" > $(ENVFILE)
@eval `cat $(ENVFILE)` && echo "FOO: $${FOO}"
І швидкий тест:
> make source-via-eval
FOO:
FOO: AMAZING!
target: output_source
bash ShellScript_name.sh
спробуйте це буде працювати, скрипт знаходиться всередині поточного каталогу.