Как создать скрипт в Makefile?

Есть ли лучший способ получить скрипт, который устанавливает env vars, из make-файла?

FLAG ?= 0
ifeq ($(FLAG),0)
export FLAG=1
/bin/myshell -c '<source scripts here> ; $(MAKE) $@'
else
...targets...
endif

person brooksbp    schedule 21.09.2011    source источник
comment
Похоже, что вы действительно хотите написать скрипт, который создает ваш скрипт для установки envvars, а затем запускает make, а не создает сам скрипт...   -  person Chris Dodd    schedule 22.09.2011
comment
Я видел отличный ответ здесь: unix.stackexchange.com/a/235254/18152 и здесь: blog.153.io/2016/04/18/ исходный-оболочка-скрипт-создания   -  person zx1986    schedule 24.12.2018


Ответы (14)


Чтобы ответить на заданный вопрос: вы не можете.

Основная проблема заключается в том, что дочерний процесс не может изменить родительскую среду. Оболочка обходит это, не разветвляя новый процесс при source, а просто запуская эти команды в текущем воплощении оболочки. Это прекрасно работает, но make не является /bin/sh (или любой другой оболочкой, для которой предназначен ваш скрипт) и не понимает этот язык (за исключением тех частей, которые у них есть общего).

Крис Додд и Фу Ба рассмотрели один возможный обходной путь, поэтому я предлагаю другой (при условии, что вы используете GNU make): постобработайте сценарий оболочки в текст, совместимый с make, и включите результат:

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

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

беспорядочные детали оставлены в качестве упражнения.

person dmckee --- ex-moderator kitten    schedule 22.09.2011
comment
Вы не можете создать сценарий для всего Makefile, но вы можете сделать это для одного рецепта. - person Yaroslav Nikitenko; 16.08.2018
comment
Да source compilationEnv.sh; $(CC) -c -o $< $^ или подобное будет работать как линия действия. - person dmckee --- ex-moderator kitten; 04.04.2019
comment
Вы можете использовать include .env (или любой файл ключ-значение), чтобы сделать переменные доступными для всего Makefile, а затем передать только необходимые в соответствующий рецепт. - person rypel; 24.08.2019

Оболочкой Makefile по умолчанию является /bin/sh, которая не реализует source.

Изменение оболочки на /bin/bash позволяет:

# Makefile

SHELL := /bin/bash

rule:
    source env.sh && YourCommand
person PureW    schedule 23.04.2017
comment
@VivekAditya Это, конечно, очень полезная вещь. Но обратите внимание, что OP просил установить переменную для использования make, а не только для использования в строках действий. Это то, как сделать последнее, но нет прямого способа сделать первое. - person dmckee --- ex-moderator kitten; 10.04.2019
comment
Для этого вы можете легко установить SHELL в свою собственную оболочку. Здесь показано gist.github.com/ingydotnet/99f7e259319f6b90e50a754f3053be7f - person ingydotnet; 24.09.2019

Если ваша цель состоит в том, чтобы просто установить переменные среды для Make, почему бы не сохранить синтаксис Makefile и не использовать команду include?

include other_makefile

Если вам нужно вызвать сценарий оболочки, зафиксируйте результат в команде shell:

JUST_DO_IT=$(shell source_script)

команда оболочки должна выполняться перед целями. Однако это не установит переменные среды.

Если вы хотите установить переменные среды в сборке, напишите отдельный сценарий оболочки, который получает ваши переменные среды и вызывает make. Затем в 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
person Foo Bah    schedule 21.09.2011
comment
У меня нет контроля над сценарием. Должен получить его. - person brooksbp; 22.09.2011
comment
Будет ли это захватывать переменные среды, если они не экспортируются? На первый взгляд кажется, что это вызывает новую оболочку, запускает команду, затем выходит и возвращается в старую среду..? - person brooksbp; 22.09.2011
comment
@brooksbp Команда оболочки не будет захватывать переменные среды. Но если вы напишите сценарий оболочки, который исходит из исходного, тогда он будет. Я объясню это дальше - person Foo Bah; 22.09.2011

Используя 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

не будет работать. Каждая строка запускается в своей собственной подоболочке.

person Samuel    schedule 31.12.2014
comment
Обратите внимание, что хранение source и команд в одной строке становится необходимым с GNU Make 4.1 (и, возможно, 4.0 тоже). Используя 3.81, я могу использовать разные строки. Для переносимости в этих версиях работает одна строка. Однако вызов внешнего скрипта более удобочитаем, если строка становится слишком длинной! - person Eric Platon; 12.02.2018
comment
Вы уверены? Несколько строк не работали у меня в 3.81, как я уже упоминал в ответе. Make запускает каждую строку в своей собственной подоболочке, поэтому несколько строк не должны работать ни в одной версии. Может у вас нестандартная ОС или сделать билд? - person Samuel; 13.02.2018
comment
несколько строк работают, если вы заканчиваете каждую строку символом `\` - person user5359531; 10.10.2018
comment
@ user5359531 Да, это правда. - person Samuel; 10.10.2018

Это работает для меня. Замените env.sh именем файла, который вы хотите использовать. Он работает, загружая файл в bash и выводя измененную среду после ее форматирования в файл с именем makeenv, который затем извлекается из makefile.

IGNORE := $(shell bash -c "source env.sh; env | sed 's/=/:=/' | sed 's/^/export /' > makeenv")                         
include makeenv   
person gdw2    schedule 10.05.2013
comment
Удобно для установки правильных сред компилятора (здесь 64-бит). Спасибо. - person revher; 23.12.2014
comment
Является ли синтаксис для выполнения этого: make IGNORE? - person John; 01.10.2019
comment
@Джон, нет. Только 1_. - person gdw2; 01.10.2019
comment
Что делать, если значения переменных в вашем env.sh содержат специальные символы Make, такие как # или $. Когда вы сделаете include makeenv, эти значения будут перепутаны. - person donhector; 07.02.2020

Некоторые конструкции одинаковы в shell и GNU Make.

var=1234
text="Some text"

Вы можете изменить свой сценарий оболочки, чтобы получить определения. Все они должны быть простыми name=value типами.

Ie,

[скрипт.ш]

. ./vars.sh

[Макетфайл]

include vars.sh

Тогда сценарий оболочки и Makefile могут использовать один и тот же «источник» информации. Я нашел этот вопрос, потому что искал манифест общего синтаксиса, который можно использовать в Gnu Make и сценариях оболочки (мне все равно, какая оболочка).

Изменить: Оболочки и понять ${var}. Это означает, что вы можете конкатенировать и т.д., var="Одна строка" var=${var} "Вторая строка"

person artless noise    schedule 05.12.2012
comment
Ошибаетесь, сценарий оболочки, подобный приведенному выше, не создает запутанных деталей, как в принятом ответе. В итоге он очень похож на файл kbuild. - person artless noise; 28.01.2015
comment
Переменные shell и переменные make не всегда прозрачно взаимозаменяемы. Когда вы include их в своем Makefile, они должны соответствовать синтаксису make, который требует экранирования определенных символов. Например, значение переменной оболочки, такое как SECRET='12#34$56' в вашем .vars.sh, вызовет у вас проблемы, когда вы будете использовать его после include vars.sh. - person donhector; 07.02.2020
comment
Я думаю, что есть два варианта использования; ОП не имеет контроля над сценарием. Таким образом, ваша точка зрения очень хороша, поскольку вы, скорее всего, получите данные, которые необходимо скрыть. Это %$#. Если вариант использования заключается в совместном использовании переменных между инструментами, которые используют переменные среды и make, то это решение может работать. Его преимущество состоит только в одном источнике информации и отсутствии дополнительных процессов. Если у вас нет контроля над сценарием оболочки, этот ответ не так полезен. Также сомнительно, почему вы хотите $% # в имени переменной. Если вы вычисляете элементы, вам нужно следовать части «Редактировать:». - person artless noise; 08.02.2020

Мне очень нравится ответ Фу Ба, где 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 не определен. .

person Samuel    schedule 10.03.2016
comment
Если у вас более старая версия make и .DEFAULT_GOAL не поддерживается, первая ветвь ifndef завершится с ошибкой make: *** Нет целей. Стоп.. Вы можете исправить это, добавив all к неявному правилу, например: all %:. Обратите внимание, что это вызовет *** смешанные неявные и обычные правила: предупреждение об устаревшем синтаксисе с более новыми версиями make. - person Samuel; 25.03.2016

Мое решение: (при условии, что у вас есть 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 открывает подоболочку, но затем команды выполняются в той же подоболочке.

Недостатком этого метода является то, что файл создается для каждой цели, что может быть нежелательно.

person Chris    schedule 13.02.2016

Если вы хотите поместить переменные в окружение, чтобы они передавались дочерним процессам, вы можете использовать bash set -a и set +a. Первое означает: «Когда я устанавливаю переменную, устанавливаю и соответствующую переменную среды». Так что это работает для меня:

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

Это передаст все в .env.test в cargo test как переменные среды.

Обратите внимание, что это позволит вам передать среду подкомандам, но не позволит вам установить переменные Makefile (которые в любом случае разные вещи). Если вам нужно последнее, вы должны попробовать одно из других предложений здесь.

person Paul A Jungwirth    schedule 01.03.2017

Если вам нужно только несколько известных переменных, экспорт в 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

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

person RzR    schedule 07.01.2016

В зависимости от вашей версии Make и прилагаемой оболочки вы можете реализовать хорошее решение с помощью 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!
person tankthinks    schedule 21.08.2018

Элегантное решение найдено здесь:

ifneq (,$(wildcard ./.env))
    include .env
    export
endif
person Sanpi    schedule 02.12.2020

Другой возможный способ — создать скрипт sh, например run.sh, найти необходимые скрипты и вызвать make внутри скрипта.

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

make 
person Veera    schedule 19.08.2016

target: output_source
    bash ShellScript_name.sh

попробуйте это, это сработает, скрипт находится внутри текущего каталога.

person amit pandya    schedule 01.09.2017