简体   繁体   English

如何将 $@ 更新为 Makefile 变量相关目标规则?

[英]How to update $@ into a Makefile variable dependent target rule?

I have a Makefile defining a variable dependent target like so:我有一个 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

With this Makefile, when I run make debug I have the following output:使用这个 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

My problem is into the two first rows of this output:我的问题是这个输出的前两行:

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

How can I make the $@ automatic variable to update when $(OBJDIR) is changed ?如何在 $(OBJDIR) 更改时使$@自动变量更新?

When make parses a Makefile it will expand all the variables upon building a dependency tree for targets that are not implicit targets.make解析 Makefile 时,它​​将在为非隐式目标的目标构建依赖树时扩展所有变量。 So when you call make debug it tries to build myprog , which in turn tries to build obj/main.o , not obj/debug/main.o , since the $(OBJFILES) has already been expanded when the rule for myprog was parsed.因此,当您调用make debug它会尝试构建myprog ,而后者又会尝试构建obj/main.o ,而不是obj/debug/main.o ,因为在解析myprog的规则时$(OBJFILES)已经扩展. That's why $@ expands to obj/main.o even though the variable is correct.这就是为什么$@扩展为obj/main.o即使变量是正确的。 See for yourself:你自己看:

$ 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/
...

It is possible to make it work though.不过有可能让它工作。 First of all, you would need to use second expansion to resolve dependencies at execution time.首先,您需要在执行时使用第二个扩展来解决依赖关系。 Second, you would need to make your $(NAME) target an implicit rule somehow to let make reevaluate the expression.其次,您需要以某种方式使您的$(NAME)目标成为隐式规则, make重新评估表达式。 Last, since your object location will differ (include debug or not), it will also propagate to the source location, which should generally be the same.最后,由于您的对象位置会有所不同(包括debug与否),它也会传播到通常应该相同的源位置。 I was able to make it work like this:我能够让它像这样工作:

$ 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

Output:输出:

$ 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

Personally I would also consider building final binaries with different name or location, otherwise you will never be sure what has been built.就个人而言,我也会考虑构建具有不同名称或位置的最终二进制文件,否则您将永远无法确定已构建的内容。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM