Чи є кращий спосіб отримати скрипт, який встановлює 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
спробуйте це буде працювати, скрипт знаходиться всередині поточного каталогу.