Як отримати скрипт у файлі Makefile?


79

Чи є кращий спосіб отримати скрипт, який встановлює env vars, із файлу make?

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

2
Здається, те, що ви насправді хочете, - це написати сценарій, який створює скрипт для встановлення envvars, а потім запускає make, а не робить сам джерелом скрипт ...
Кріс Додд,

Я побачив чудову відповідь тут: unix.stackexchange.com/a/235254/18152 і тут: blog.153.io/2016/04/18/source-a-shell-script-in-make
zx1986

Відповіді:


47

Відповісти на запитання: не можна .

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

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

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

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

брудні деталі, залишені як вправа.


3
Ви не можете "створити" сценарій для цілого Makefile, але ви можете зробити це для одного рецепта.
Ярослав Нікітенко

Так source compilationEnv.sh; $(CC) -c -o $< $^чи подібне буде працювати як лінія дії.
dmckee --- екс-модератор кошеня

1
Ви можете використовувати include .env(або будь-який файл ключ-значення), щоб зробити змінні доступними для всього Makefile, а потім передати лише необхідні у відповідний рецепт.
Рипель

98

Шаблон за замовчуванням Makefile - це той, /bin/shякий не реалізований source.

Зміна оболонки на /bin/bashдозволяє:

# Makefile

SHELL := /bin/bash

rule:
    source env.sh && YourCommand

4
@VivekAditya Це, звичайно, надзвичайно корисна річ. Але зауважте, що OP просить встановити змінну для використання, makeа не лише для використання в рядках дій. Це як зробити останнє, але прямого способу зробити перше не існує.
dmckee --- екс-модератор кошеня

1
Для цього ви можете легко встановити SHELLвласну обгортку. Показано тут gist.github.com/ingydotnet/99f7e259319f6b90e50a754f3053be7f
ingydotnet

22

Якщо ваша мета - просто встановити змінні середовища для 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

4
Я не контролюю сценарій. Потрібно це отримати.
brooksbp

Чи захопить це змінні середовища, якщо вони не експортуються? З першого погляду здається, що він викликає нову оболонку, запускає команду, потім виходить і повертається до старого середовища ..?
brooksbp

@brooksbp Команда оболонки не захоплює змінні середовища. Але якщо ви напишете скрипт оболонки, який буде джерелом оригінального, тоді він буде. Поясню це далі
Foo Bah

17

За допомогою 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

не буде працювати. Кожен рядок запускається у своїй підоболонці.


1
Зверніть увагу, що збереження sourceта команди в одному рядку стають необхідними з GNU Make 4.1 (а можливо, також і 4.0). Використовуючи 3.81, я можу використовувати різні рядки. Для мобільності в цих версіях працює один рядок. Виклик зовнішнього сценарію стає більш читабельним, якщо рядок стає занадто довгим!
Ерік Платон,

Ти впевнений? Кілька рядків не працювали для мене в 3.81, як я вже згадував у відповіді. Make запускає кожен рядок у своїй власному підшелупці, тому кілька рядків не повинні працювати в будь-якій версії. Можливо, у вас є незвичайна ОС або побудова?
Самуель

1
кілька рядків працює до тих пір, поки ви закінчуєте кожен рядок
символом

@ user5359531 Так, це правда.
Самуель

10

Це працює для мене. Замініть env.shім'я файлу, який потрібно створити. Він працює, шукаючи файл у bash і виводячи модифіковане середовище після його форматування у файл, makeenvякий називається, а потім отримує файл make.

IGNORE := $(shell bash -c "source env.sh; env | sed 's/=/:=/' | sed 's/^/export /' > makeenv")                         
include makeenv   

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

Хороший варіант для встановлення правильних середовищ компілятора (тут 64-розрядна версія). Дякую.
revher

Чи є синтаксис для виконання цього: "make IGNORE"?
Джон

@ Джон, ні. Просто make.
gdw2

Що робити, якщо значення змінних у вашому env.shмістять спеціальні символи Make, такі як #або $. Коли ви це зробите, include makeenvці значення будуть заплутані.
donhector

6

Деякі конструкції однакові в 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} "Другий рядок"


Помилка, сценарій оболонки, як вище, не створює брудних деталей, як у прийнятій відповіді. У підсумку це схоже на файл kbuild .
бездумний шум

1
shellзмінні та makeзмінні не завжди прозоро взаємозамінні. Коли ви includeїх у своєму Makefile, вони повинні відповідати makeсинтаксису, який нав'язує певні символи, які слід уникати. Наприклад, значення змінної оболонки, як SECRET='12#34$56'у вашому .vars.sh, створить вам проблеми, коли ви будете використовувати його післяinclude vars.sh
donhector

1
Я думаю, що є два випадки використання; OP не має контролю над сценарієм. Отже, ваша думка дуже добра, оскільки ви, швидше за все, отримаєте дані, які потрібно врятувати. Це "% $ #". Якщо варіант використання полягає у спільному використанні змінних між інструментами, які використовують змінні середовища та роблять, тоді це рішення може працювати. Він має перевагу лише в одному джерелі інформації та відсутність зайвих процесів. Якщо у вас немає контролю над сценарієм оболонки, ця відповідь не така корисна. Також сумнівно, чому ви хочете "$% #" в назві змінної. Якщо ви обчислюєте елементи, вам потрібно дотримуватися частини "Редагувати:".
бездумний шум

3

Мені дуже подобається відповідь Фу Ба, де 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 не визначено .


Якщо у вас є старіша версія make, а .DEFAULT_GOAL не підтримується, перша гілка ifndef вийде з ладу з "make: *** Немає цілей. Зупинити.". Ви можете це виправити, додавши "все" до неявного правила, наприклад: "всі%:". Зауважте, що це спричинить попередження "*** змішані неявні та звичайні правила: застарілий синтаксис" із новими версіями make.
Самуель

1

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

Недоліком цього методу є те, що файл отримується для кожної цілі, що може бути небажаним.


1

Якщо ви хочете отримати змінні у середовищі , щоб вони передавалися дочірнім процесам, тоді ви можете використовувати bash set -aта set +a. Перший означає: "Коли я встановлюю змінну, встановлюю відповідну змінну середовища". Отже, це працює для мене:

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

Це буде проходити все в .env.testна cargo testяк змінні середовища.

Зверніть увагу, що це дозволить вам передавати середовище підкомандам, але не дозволить встановлювати змінні Makefile (які в будь-якому випадку різні). Якщо вам потрібно останнє, спробуйте одну з інших пропозицій тут.


0

Якщо вам потрібні лише кілька відомих змінних, експорт у 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


0

В залежності від версії Марки і огороджувальних оболонок, ви можете реалізувати хороше рішення з допомогою 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!


-1

Іншим можливим способом може бути створення сценарію sh, наприклад run.sh, отримання необхідних сценаріїв і виклик make у скрипті.

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

make 

-1
target: output_source
    bash ShellScript_name.sh

спробуйте це буде працювати, скрипт знаходиться всередині поточного каталогу.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.