Как определить глобальные функции оболочки в Makefile?
Я хочу определить функцию оболочки
#!/bin/sh
test ()
{
do_some_complicated_tests $1 $2;
if something; then
build_thisway $1 $2;
else
build_otherway $1 $2;
fi
}
таким образом, что я могу использовать его в каждом правиле моего файла Makefile, например:
foo: bar
test foo baz
Чтобы быть ясным, я хочу, чтобы функция оболочки была частью Makefile. Каков самый элегантный способ сделать это? Бонусные баллы, если вы можете сделать это, не вызывая make рекурсивно.
Фон:
Моя фактическая проблема заключается в том, что make -n
производит очень длинный и нечитаемый вывод. Каждое правило использует почти ту же последовательность нечитаемых команд оболочки, и существует много правил. Вышеприведенное решение сделает вывод make -n
более полезным.
Ответы
Ответ 1
Это решение не полагается на внешний временный файл и не заставляет вас возиться с переменной SHELL
.
TESTTOOL=sh -c '\
do_some_complicated_tests $$1 $$2; \
if something; then
build_thisway $$1 $$2;
else
build_otherway $$1 $$2;
fi' TESTTOOL
ifneq (,$(findstring n,$(MAKEFLAGS)))
TESTTOOL=: TESTTOOL
endif
foo: bar
${TESTTOOL} foo baz
Блок ifneq…endif
проверяет флаг -n
в командной строке и устанавливает расширение TESTTOOL
на : TESTTOOL
, которое легко читать и безопасно выполнять.
Лучшее решение может заключаться в том, чтобы превратить функцию оболочки в настоящую программу, если это вариант для вас.
Ответ 2
Поскольку вопрос помечен gnu-make
, вы можете быть довольны решением Beta, но я считаю, что лучшим решением является 1) переименовать функцию, отличную от test
(избегайте имен типа ls
и cd
также); для обсуждения предположим, что вы переименовали его foo
, 2) просто напишите script с именем foo
и вызовите его из Makefile. Если вы действительно хотите определить функцию оболочки в Makefile, просто определите ее для каждого правила:
F = foo() { \
do_some_complicated_tests $$1 $$2; \
if something; then \
build_thisway $$1 $$2; \
else \
build_otherway $$1 $$2; \
fi \
}
all: bar
@$(F); foo baz bar
Не нужно, чтобы в каждой строке определения foo
были продолжены строки, а $
- все экранированные, чтобы они передавались в оболочку.
Ответ 3
Что-то подсказывает, что вам лучше фильтровать вывод make -n
, но то, что вы просите, возможно:
define test
@echo do some tests with $(1) and $(2); \
SOMETHING=$(1)_3 ; \
if [ $$SOMETHING == foo_3 ]; then \
echo build this way $(1) $(2); \
else \
echo build another way $(1) $(2) ; \
fi
endef
someTarget:
$(call test,foo,bar)
someOtherTarget:
$(call test,baz,quartz)
Ответ 4
Я не думаю, что это считается "элегантным", но, похоже, делает то, что вы хотите:
##
## --- Start of ugly hack
##
THIS_FILE := $(lastword $(MAKEFILE_LIST))
define shell-functions
: BEGIN
# Shell syntax here
f()
{
echo "Here you define your shell function. This is f(): [email protected]"
}
g()
{
echo "Another shell function. This is g(): [email protected]"
}
: END
endef
# Generate the file with function declarations for the shell
$(shell sed -n '/^: BEGIN/,/^: END/p' $(THIS_FILE) > .functions.sh)
# The -i is necessary to use --init-file
SHELL := /bin/bash --init-file .functions.sh -i
##
## -- End of ugly hack
##
all:
@f 1 2 3 4
@g a b c d
Запуск этого процесса:
$ make -f hack.mk
Here you define your shell function. This if f(): 1 2 3 4
Another shell function. This is g(): a b c d
Запуск с помощью -n
вызывает:
$ make -f hack.mk -n
f 1 2 3 4
g a b c d
Это зависит от того, что макросы, определенные между define
и endef
, вообще не интерпретируются make
до тех пор, пока они фактически не используются, поэтому вы можете напрямую использовать синтаксис оболочки, и вам не нужно заканчивать каждый линии с обратной косой чертой. Конечно, он будет бомбить, если вы вызовете макрос shell-functions
.
Ответ 5
Это действительно гетто, но что угодно. Я использую zsh, но я уверен, что существуют эквиваленты bash и sh.
Makefile:
export ZDOTDIR := ./
SHELL := /usr/bin/env zsh
default:
f
.zshenv, тот же каталог, что и Makefile:
f () { echo 'CHECK OUT THIS AWESOME FUNCTION!' }
Переменная ZDOTDIR делает zsh в текущем каталоге для dotfiles. Затем вы просто вставляете то, что хотите, в .zshenv
.
$ make
f
CHECK OUT THIS AWESOME FUNCTION!
Ответ 6
TL; ДР:
В вашем make файле:
SHELL=bash
BASH_FUNC_command%%=() { ... }
export BASH_FUNC_command%%
Длинный ответ
Я сделал что-то подобное с bash в неконкретизируемом контексте, который работает здесь.
Я объявил свои функции оболочки в bash, а затем экспортировал их с помощью:
export -f function-name
Затем они естественно доступны, если одна и та же оболочка вызывается как подпроцесс, в вашем случае, из make.
Пример:
$ # define the function
$ something() { echo do something here ; }
$ export -f something
$ # the Makefile
$ cat > Makefile <<END
SHELL=bash
all: ; something
END
$
Попробуйте
$ make
something
do something here
$
Как это выглядит в среде?
$ env | grep something
BASH_FUNC_something%%=() { echo do something here
Итак, для вас будет вопрос, как установить переменные среды из Makefile. Шаблон выглядит как BASH_FUNC_ имя-функции %% =() { function-body}
Непосредственно в make
Итак, как это работает для вас? Попробуйте этот makefile
SHELL=bash
define BASH_FUNC_something-else%%
() {
echo something else
}
endef
export BASH_FUNC_something-else%%
all: ; something-else
и попробуйте:
$ make
something-else
something else
Это немного уродливо в Makefile, но не представляет уродства для make -n