我想在shell中运行某些操作,具体取决于当前目录中的默认makefile是否包含某个目标。
#!/bin/sh
make -q some_target
if test $? -le 1 ; then
true # do something
else
false # do something else
fi
这是有效的,因为如果目标不存在,GNU make返回错误代码2,否则返回0或1。问题是这种方式没有记录。这是男人的一部分:
-q, --question
``Question mode''. Do not run any commands, or print anything;
just return an exit status that is zero if the specified targets
are already up to date, nonzero otherwise.
仅区分零/非零。这样做的正确方法是什么?
答案 0 :(得分:3)
您应该阅读the GNU make manual而不是手册页:手册页只是摘要而不是完整定义。手册说:
The exit status of make is always one of three values:
0 The exit status is zero if make is successful
2 The exit status is two if make encounters any errors. It will print messages
describing the particular errors.
1 The exit status is one if you use the ‘-q’ flag and make determines that
some target is not already up to date.
由于尝试创建一个不存在的目标是一个错误,因此在这种情况下,您将始终获得退出代码2。
答案 1 :(得分:3)
迟到的答案,但也许它会帮助将来遇到同样问题的人。
我使用下面的方法,它需要您修改Makefile(您需要添加单个%-rule-exists模式规则)。如果您不想这样做,可以直接从脚本运行make -n your-target-to-check &> /dev/null
。
我使用它来获得autobuild和autoupload等命令,这些命令包含在代码段中的上下文中。
%-rule-exists:
@$(MAKE) -n $* &> /dev/null
auto%: %-rule-exists
$(info Press CTRL-C to stop $*ing)
@while inotifywait -qq -r -e move -e create -e modify -e delete --exclude "\.#.*" src; do make $* ||: ; date ; done
示例输出:
$ make build-rule-exists; echo Exit code: $?
Exit code: 0
$ make nonsense-rule-exists; echo Exit code: $?
Exit code: 2
请注意,它实际上并不构建目标,以查明规则是否存在,由-n
make
标志提供。即使构建失败,我也需要它才能工作。
答案 2 :(得分:2)
另一种可能性是在数据库中grep,这是因为读取makefile:
some_target:
@ if $(MAKE) -C subdir -npq --no-print-directory .DEFAULT 2>/dev/null | grep -q "^$@:" ; \
then \
echo "do something" ; \
fi
其中
-n --just-print, --dry-run
Print the commands that would be executed, but do not execute them.
-p, --print-data-base
Print the data base (rules and variable values) that results from reading
the makefiles
-q, --question
Question mode. Do not run any commands, or print anything; just return and
exit status that is zero if the specified targets are already up to date,
nonzero otherwise.
答案 3 :(得分:0)
我需要一种可以用于多个目标/目标并且不触发Makefile进行重新制作的解决方案。
这是我最终使用的内容:
#!/usr/bin/env bash
set -euo pipefail
dir="$1"
targets="$2"
# If there is no Makefile then return.
[[ ! -f "${dir}/Makefile" ]] && exit 1
# First check if this build will remake itself.
# If it does then add Makefile as a target to stop it.
if eval ${MAKE} --dry-run "--directory=${dir}" Makefile &> /dev/null; then
db_target=Makefile
else
db_target=
fi
# Search the Make internal database for the targets.
eval ${MAKE} --dry-run --print-data-base "--directory=${dir}" "${db_target}" | \
awk -v targets="${targets}" '
BEGIN {
# Count the number of targets and put them in an array keyed by their own value.
numRemaining = split(targets, temp)
for (i in temp) remaining[temp[i]]
}
# If we found all the targets then consume all the input so the pipe does not fail.
numRemaining == 0 { next }
# Skip everything from the first line up to the start of the database comment inclusive.
NR == 1, /\# Make data base/ { next }
# Skip those that are not real targets.
/\# Not a target:/, /^[^#]/ { next }
{
# Check if this line starts with any of the targets.
for (target in remaining) {
if ($0 ~ "^"target":") {
delete remaining[target]
numRemaining--
next
}
}
}
END {
# If we get to the end then make sure that we found all the targets.
exit (numRemaining ? 1 : 0)
}'
警告是:
Makefile
(或根本不存在)。export MAKE
语句(如果愿意,可以将make
硬编码到脚本中)。eval
(真是!)。我在Make文件中使用它来聚合子项目上的目标,这些子项目可能不像这样:
top_targets := all setup build clean mostlyclean
sub_dirs := foo bar
.PHONY: $(top_targets)
$(top_targets): $(sub_dirs)
.PHONY: $(sub_dirs)
$(sub_dirs):
@if ./bin/make-target-exists.sh '$@' '$(MAKECMDGOALS)'; then $(MAKE) --directory='$@' $(MAKECMDGOALS); fi
答案 4 :(得分:0)
简单的解决方案:
output=$(make -n some_target 2>&1 | head -1)
if [[ "$output" != *"No rule to make target"* ]]; then
echo "target is present"
fi
这个想法是在不运行make的情况下检查其输出。不确定,但这是100%正确的方法。
可以包装在函数中:
function check_make_target {
output=$(make -n "$1" 2>&1 | head -1)
[[ "$output" != *"No rule to make target"* ]]
}
if check_make_target some_target; then
echo "target is present"
fi