【问题标题】:How to update $@ into a Makefile variable dependent target rule?如何将 $@ 更新为 Makefile 变量依赖目标规则?
【发布时间】:2021-02-07 16:23:05
【问题描述】:

我有一个 Makefile 定义一个变量依赖目标,如下所示:

SRCFILES=./src/main.c ./src/file.c
# Get the filenames of all the sources
SRCNAMES=$(notdir $(SRCFILES))

OBJDIR=./obj/
# Replace .c to .o in all source names
OBJNAMES=$(SRCNAMES:.c=.o)
# Add the obj/ repository as prefix
OBJFILES=$(addprefix $(OBJDIR), $(OBJNAMES))

NAME=myprog

all: $(NAME)

$(NAME): $(OBJFILES)
    gcc $(OBJFILES) -o $(NAME)

$(OBJDIR)%.o: ./src/%.c
    mkdir -p $(OBJDIR)
    gcc -c $< -o $@

# If the make debug rule is called, I want my objects to be build into ./obj/debug/
debug: OBJDIR = ./obj/debug/
debug: all

有了这个 Makefile,当我运行 make debug 时,我有以下输出:

❯ make debug
mkdir -p ./obj/debug/
gcc -c src/main.c -o obj/main.o
mkdir -p ./obj/debug/
gcc -c src/file.c -o obj/file.o
gcc ./obj/debug/main.o ./obj/debug/file.o -o myprog
gcc: error: ./obj/debug/main.o: No such file or directory
gcc: error: ./obj/debug/file.o: No such file or directory
gcc: fatal error: no input files
compilation terminated.
make: *** [Makefile:16: myprog] Error 1

我的问题出在这个输出的前两行:

mkdir -p ./obj/debug/  # <----------- Here we see that the $(OBJDIR) variable has been updated

gcc -c src/main.c -o obj/main.o # <-- However, the $@ variable which is generated from $(OBJDIR)%.o hasn't been updated accordingly

如何使$@ 自动变量在 $(OBJDIR) 更改时更新?

【问题讨论】:

  • 您的OBJFILES=$(addprefix $(OBJDIR), $(OBJNAMES)) 似乎是在您更新debug: OBJDIR = ./obj/debug/ 之前设置的。在填充 ...FILES 变量之前,您需要确保设置了要使用的目录。
  • @DavidC.Rankin 我不这么认为,OBJFILES 变量已正确更新,如第 5 行所示:gcc ./obj/debug/main.o ./obj/debug/file.o -o myprog
  • 是的,但./obj/debug/main.o ./obj/debug/file.o 不存在。它们被编译到上面的cc -c src/main.c -o obj/main.o
  • @DavidC.Rankin 是的,这是我的问题。 -o $@ 应该是./obj/debug/main.o,因为它来自$(OBJDIR)%.o,并且OBJDIR 在调用all 之前由debug 重新定义。我无法提前知道 Makefile 是否会使用 debug 规则调用。
  • 我过去处理这个问题的唯一方法是将变量传递给make,例如make with=debug 然后检查 ifeq ($(with), debug) 然后更新目录位置。否则,如果 make debug 应该自动为您处理 - 我必须返回文档并弄清楚。

标签: c++ c gcc makefile


【解决方案1】:

make 解析 Makefile 时,它​​将在为非隐式目标构建依赖关系树时扩展所有变量。因此,当您调用make debug 时,它会尝试构建myprog,而后者又会尝试构建obj/main.o,而不是obj/debug/main.o,因为在解析myprog 的规则时,$(OBJFILES) 已经被扩展。这就是为什么 $@ 扩展为 obj/main.o 即使变量是正确的。自己看:

$ make -dr debug
...
Considering target file 'debug'.
 File 'debug' does not exist.
 Looking for an implicit rule for 'debug'.
 No implicit rule found for 'debug'.
  Considering target file 'all'.
   File 'all' does not exist.
   Looking for an implicit rule for 'all'.
   No implicit rule found for 'all'.
    Considering target file 'myprog'.
     File 'myprog' does not exist.
      Considering target file 'obj/main.o'.
       File 'obj/main.o' does not exist.
       Looking for an implicit rule for 'obj/main.o'.
       Trying pattern rule with stem 'main'.
       Trying implicit prerequisite 'src/main.c'.
       Found an implicit rule for 'obj/main.o'.
        Considering target file 'src/main.c'.
         Looking for an implicit rule for 'src/main.c'.
         No implicit rule found for 'src/main.c'.
         Finished prerequisites of target file 'src/main.c'.
        No need to remake target 'src/main.c'.
       Finished prerequisites of target file 'obj/main.o'.
      Must remake target 'obj/main.o'.
mkdir -p ./obj/debug/
...

虽然可以让它工作。首先,您需要在执行时使用第二个扩展来解决依赖关系。其次,您需要以某种方式使您的 $(NAME) 目标成为隐式规则,以让 make 重新评估表达式。最后,由于您的对象位置会有所不同(是否包括debug),它也会传播到源位置,这通常应该是相同的。我能够让它像这样工作:

$ cat Makefile
SRCFILES=./src/main.c ./src/file.c
# Get the filenames of all the sources
SRCNAMES=$(notdir $(SRCFILES))

OBJDIR=./obj/
# Replace .c to .o in all source names
OBJNAMES=$(SRCNAMES:.c=.o)
# Add the obj/ repository as prefix
OBJFILES=$(addprefix $(OBJDIR), $(OBJNAMES))

NAME=myprog

.SECONDEXPANSION:

all: $(CURDIR)/$(NAME)

# Implicit rule due to %
%/$(NAME): $$(OBJFILES)
        gcc $(OBJFILES) -o $(NAME)

%.o: ./src/$$(notdir $$*).c
        mkdir -p $(OBJDIR)
        gcc -c $< -o $@

# If the make debug rule is called, I want my objects to be build into ./obj/debug/
debug: OBJDIR = ./obj/debug/
debug: all

输出:

$ make
mkdir -p ./obj/
gcc -c src/main.c -o obj/main.o
mkdir -p ./obj/
gcc -c src/file.c -o obj/file.o
gcc ./obj/main.o ./obj/file.o -o myprog
rm obj/main.o obj/file.o

$ rm -rf obj myprog
$ make debug
mkdir -p ./obj/debug/
gcc -c src/main.c -o obj/debug/main.o
mkdir -p ./obj/debug/
gcc -c src/file.c -o obj/debug/file.o
gcc ./obj/debug/main.o ./obj/debug/file.o -o myprog
rm obj/debug/main.o obj/debug/file.o

我个人也会考虑构建具有不同名称或位置的最终二进制文件,否则您将永远无法确定构建了什么。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-19
    • 2013-06-05
    • 1970-01-01
    相关资源
    最近更新 更多