如何检查makefile中目标的存在性

如何检查makefile中目标的存在性,makefile,gnu-make,Makefile,Gnu Make,我希望根据当前目录中的默认makefile是否包含特定目标,在shell中运行特定操作 #!/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

我希望根据当前目录中的默认makefile是否包含特定目标,在shell中运行特定操作

#!/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.
仅区分零/非零。正确的方法是什么?

你应该阅读而不是手册页:手册页只是一个摘要,不是一个完整的定义。手册上说:

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的退出代码。

稍后回答,但它可能会帮助将来面临相同问题的人

我使用下面的方法,这确实需要您修改Makefile(您需要添加一个%-规则exists模式规则)。如果不想这样做,可以直接从脚本运行
make-n-your target来检查&>/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

请注意,由于
make
-n
标志,它实际上并不构建目标来确定规则是否存在。即使构建失败,我也需要它工作。

另一种可能性是在读取makefile后产生的数据库中进行grep:

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.

我需要一个解决方案,该解决方案将适用于多个目标/目标,并且不会触发重新制作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)
    }'
警告是:

  • Make文件必须被称为
    Makefile
    (或者根本不存在)
  • MAKE文件中有一个
    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
简单解决方案:

output=$(make -n some_target 2>&1 | head -1)
if [[ "$output" != *"No rule to make target"* ]]; then
    echo "target is present"
fi
其思想是在不运行make的情况下检查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

因此,除了解析stderr输出?Correct,无法将这种错误与其他错误区分开来;make不会对可能遇到错误的所有可能方式使用不同的错误代码。为什么同时使用
-n
-q