Проверьте, существует ли программа из Makefile
Как проверить, может ли программа быть вызвана из Makefile?
(То есть программа должна существовать в пути или иначе быть вызываемой.)
Он может использоваться для проверки того, какой компилятор установлен, например.
например. что-то вроде этот вопрос, но без предположения, что базовая оболочка совместима с POSIX.
Ответы
Ответ 1
Иногда вам нужен Makefile, чтобы иметь возможность работать в другой целевой ОС, и вы хотите, чтобы сборка не удалась раньше, если требуемый исполняемый файл отсутствует в PATH
а не работал в течение, возможно, долгого времени, прежде чем произойдет сбой.
Отличное решение, предоставляемое engineerchuan, требует создания цели. Однако, если у вас есть много исполняемых файлов для тестирования, а ваш Makefile имеет много независимых целей, каждая из которых требует тестов, тогда каждая цель требует цели тестирования в качестве зависимости. Это требует много дополнительной печати и времени обработки, когда вы делаете более одной цели за раз.
Решение, предоставляемое 0xf, может тестировать исполняемый файл без создания цели. Это экономит много времени при наборе текста и выполнении, когда есть несколько целей, которые можно построить как по отдельности, так и вместе.
Мое усовершенствование последнего решения заключается в использовании which
исполняемый файл (where
в Windows), а не полагаться на там быть --version
вариант в каждом исполняемом, непосредственно в GNU Make ifeq
директиву, вместо того, чтобы определить новую переменную, и использовать функцию GNU Make error
чтобы остановить сборку, если требуемый исполняемый файл не находится в ${PATH}
. Например, чтобы проверить исполняемый файл lzop
:
ifeq (, $(shell which lzop))
$(error "No lzop in $(PATH), consider doing apt-get install lzop")
endif
Если у вас есть несколько исполняемых файлов для проверки, вы можете использовать функцию foreach
с исполняемым файлом which
:
EXECUTABLES = ls dd dudu lxop
K := $(foreach exec,$(EXECUTABLES),\
$(if $(shell which $(exec)),some string,$(error "No $(exec) in PATH")))
Обратите внимание на использование оператора присваивания :=
который необходим для немедленной оценки выражения RHS. Если ваш Makefile изменяет PATH
, то вместо последней строки выше вам понадобится:
$(if $(shell PATH=$(PATH) which $(exec)),some string,$(error "No $(exec) in PATH")))
Это должно дать вам вывод, похожий на:
ads$ make
Makefile:5: *** "No dudu in PATH. Stop.
Ответ 2
Я смешал решения из @kenorb и @0xF и получил следующее:
DOT := $(shell command -v dot 2> /dev/null)
all:
ifndef DOT
$(error "dot is not available please install graphviz")
endif
dot -Tpdf -o pres.pdf pres.dot
Это прекрасно работает, потому что команда -v не печатает ничего, если исполняемый файл недоступен, поэтому переменная DOT никогда не определяется, и вы можете просто проверять ее, когда захотите. В этом примере я делаю ошибку, но вы можете сделать что-то более полезное, если хотите.
Если переменная доступна, "команда -v" выполняет недорогую операцию печати пути к команде, определяя переменную DOT.
Ответ 3
это то, что вы сделали?
check: PYTHON-exists
PYTHON-exists: ; @which python > /dev/null
mytarget: check
.PHONY: check PYTHON-exists
кредит моему коллеге.
Ответ 4
Используйте функцию shell
для вызова вашей программы таким образом, чтобы она печатала что-то на стандартный вывод. Например, перейдите --version
.
GNU Make игнорирует статус выхода команды, переданной в shell
. Чтобы избежать потенциального сообщения "команда не найдена", перенаправьте стандартную ошибку на /dev/null
.
Затем вы можете проверить результат с помощью ifdef
, ifndef
, $(if)
и т.д.
YOUR_PROGRAM_VERSION := $(shell your_program --version 2>/dev/null)
all:
ifdef YOUR_PROGRAM_VERSION
@echo "Found version $(YOUR_PROGRAM_VERSION)"
else
@echo Not found
endif
В качестве бонуса вывод (например, версия программы) может оказаться полезным в других частях вашего файла Makefile.
Ответ 5
Мое решение включает в себя небольшой помощник script 1 который помещает файл флага, если существуют все необходимые команды. Это связано с тем, что проверка необходимых команд выполняется только один раз, а не при каждом вызове make
.
check_cmds.sh
#!/bin/bash
NEEDED_COMMANDS="jlex byaccj ant javac"
for cmd in ${NEEDED_COMMANDS} ; do
if ! command -v ${cmd} &> /dev/null ; then
echo Please install ${cmd}!
exit 1
fi
done
touch .cmd_ok
Makefile
.cmd_ok:
./check_cmds.sh
build: .cmd_ok target1 target2
1 Подробнее о методе command -v
можно найти здесь.
Ответ 6
Убрал некоторые из существующих решений здесь...
REQUIRED_BINS := composer npm node php npm-shrinkwrap
$(foreach bin,$(REQUIRED_BINS),\
$(if $(shell command -v $(bin) 2> /dev/null),$(info Found `$(bin)`),$(error Please install `$(bin)`)))
$(info ...)
вы можете исключить, если вы хотите, чтобы это было тише.
Это будет быстро сбой. Не требуется цель.
Ответ 7
Для меня все приведенные выше ответы основаны на Linux и не работают с окнами. Я новичок, поэтому мой подход может быть не идеальным. Но полный пример, который работает для меня как для Linux, так и для Windows:
# detect what shell is used
ifeq ($(findstring cmd.exe,$(SHELL)),cmd.exe)
$(info "shell Windows cmd.exe")
DEVNUL := NUL
WHICH := where
else
$(info "shell Bash")
DEVNUL := /dev/null
WHICH := which
endif
# detect platform independently if gcc is installed
ifeq ($(shell ${WHICH} gcc 2>${DEVNUL}),)
$(error "gcc is not in your system PATH")
else
$(info "gcc found")
endif
Необязательно, когда мне нужно обнаружить больше инструментов, которые я могу использовать:
EXECUTABLES = ls dd
K := $(foreach myTestCommand,$(EXECUTABLES),\
$(if $(shell ${WHICH} $(myTestCommand) 2>${DEVNUL} ),\
$(myTestCommand) found,\
$(error "No $(myTestCommand) in PATH)))
$(info ${K})
Ответ 8
Вы можете использовать bash встроенные команды, такие как type foo
или command -v foo
, как показано ниже:
SHELL := /bin/bash
all: check
check:
@type foo
Где foo
- ваша программа/команда. Переадресация на > /dev/null
, если вы хотите, чтобы он молчал.
Ответ 9
Предположим, у вас есть разные цели и строители, каждый из которых требует другого набора инструментов. Установите список таких инструментов и рассмотрите их как цель, чтобы заставить проверить их доступность
Например:
make_tools := gcc md5sum gzip
$(make_tools):
@which [email protected] > /dev/null
file.txt.gz: file.txt gzip
gzip -c file.txt > file.txt.gz
Ответ 10
Решено путем компиляции специальной маленькой программы в другой целевой файл makefile, единственной целью которого является проверка того, что я искал для любого времени выполнения.
Затем я назвал эту программу еще одной целью makefile.
Это было что-то вроде этого, если я правильно помню:
real: checker real.c
cc -o real real.c `./checker`
checker: checker.c
cc -o checker checker.c
Ответ 11
Проверка решений для вывода STDERR
из --version
не работает для программ, которые печатают свою версию в STDOUT
вместо STDERR
. Вместо проверки их вывода в STDERR
или STDOUT
, проверьте код возврата программы. Если программа не существует, ее код выхода всегда будет отличным от нуля.
#!/usr/bin/make -f
# https://stackoverflow.com/questions/7123241/makefile-as-an-executable-script-with-shebang
ECHOCMD:=/bin/echo -e
SHELL := /bin/bash
RESULT := $(shell python --version >/dev/null 2>&1 || (echo "Your command failed with $$?"))
ifeq (,${RESULT})
EXISTS := true
else
EXISTS := false
endif
all:
echo EXISTS: ${EXISTS}
echo RESULT: ${RESULT}
Ответ 12
Я лично определяю цель require
, которая работает раньше всех остальных. Эта цель просто запускает команды версий всех требований по одному и печатает соответствующие сообщения об ошибках, если команда недействительна.
all: require validate test etc
require:
@echo "Checking the programs required for the build are installed..."
@shellcheck --version >/dev/null 2>&1 || (echo "ERROR: shellcheck is required."; exit 1)
@derplerp --version >/dev/null 2>&1 || (echo "ERROR: derplerp is required."; exit 1)
# And the rest of your makefile below.
Результат приведенного ниже сценария:
Checking the programs required for the build are installed...
ERROR: derplerp is required.
makefile:X: recipe for target 'prerequisites' failed
make: *** [prerequisites] Error 1