如何编写 *.h文件作为依赖项制定规则

发布于 2025-01-20 06:43:16 字数 2844 浏览 4 评论 0原文

我正在尝试了解如何处理制动规则中的标头文件依赖项。让我给你一个具体的例子。

我正在使用GNU Make构建名为MYAP的应用程序。它由各种*。h*。c文件组成。

目录inc/包含defs.hutil.h标头文件。

目录src/包含main.ccmd.cwin.c文件。

目录obj/包含所有生成的对象文件。

我有多个需要不同的构建选项的应用程序。因此,我不想依靠任何隐式规则,也想为所有对象文件等指定自己的规则。

我想指定以下规则:

对象文件取决于特定*。 >和*。c文件。如果其中任何一个都更改,则所有对象文件都必须重新生成。但是,即使*。h文件是先决条件列表的一部分,我也不想将它们传递给编译器。我只想编译*。c文件。

可执行myApp取决于特定的*。o文件。如果其中任何一个更改,则必须将可执行文件重新生成。

到目前为止,具有静态模式规则的以下makefile似乎可以正常工作:

myapp_inc := inc/defs.h inc/util.h
myapp_src := src/main.c src/cmd.c src/win.c
myapp_obj := $(patsubst src/%.c,obj/%.o,$(myapp_src))
myapp_bin := obj/myapp

.PHONY: all
all:

# Create obj/main.o obj/cmd.o and obj/win.o from various *.c files
# If any *.h files in $(myapp_inc) list change, all objects are regenerated.
# If any *.c files in $(myapp_src) list change, all objects are regenerated.
$(myapp_obj): obj/%.o: src/%.c $(myapp_inc) $(myapp_src)
    gcc -c -o $@ $<

# Create obj/myapp from various *.o files
# If any *.o files in $(myapp_obj) list change, executable is regenerated.
$(myapp_bin): $(myapp_obj)
    gcc -o $@ $^

all: $(myapp_bin)

.PHONY: clean
clean:
    rm -f obj/*

我不太了解如何正确编写规则以处理这种用例。上述静态模式规则是正确工作的唯一方法吗?

具体来说,我尝试了以下组合,如互联网上的各种简单示例中给出的那样,由于各种原因,它们都失败了。

此规则使$&lt;始终传递第一个先决条件的名称,该名称与多个*。c文件无效:

$(myapp_obj): $(myapp_src) $(myapp_inc)
    gcc -c -o $@ $<

$ make
gcc -c -o obj/main.o src/main.c
gcc -c -o obj/cmd.o src/main.c
gcc -c -o obj/win.o src/main.c
gcc -o obj/myapp obj/main.o obj/cmd.o obj/win.o
/bin/ld: obj/cmd.o: in function `main':
main.c:(.text+0x0): multiple definition of `main'; obj/main.o:main.c:(.text+0x0): first defined here
/bin/ld: obj/win.o: in function `main':
main.c:(.text+0x0): multiple definition of `main'; obj/main.o:main.c:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status
make: *** [Makefile:18: obj/myapp] Error 1

此规则原因$^始终传递所有先决条件的名称,这将失败:

$(myapp_obj): $(myapp_src) $(myapp_inc)
    gcc -c -o $@ $^

$ make
gcc -c -o obj/main.o src/main.c src/cmd.c src/win.c inc/defs.h inc/util.h
gcc: fatal error: cannot specify ‘-o’ with ‘-c’, ‘-S’ or ‘-E’ with multiple files
compilation terminated.
make: *** [Makefile:13: obj/main.o] Error 1

现在,我了解$&&&&&code>和$^变量之间的区别,但是很多文档是当处理多个*。c*。h文件作为先决条件时,尚不清楚如何使用它们。

建议的使用模式是什么?

为什么使用$&lt;*。c文件传递给食谱,而不是*。h文件?做一些内部过滤吗?这在任何地方都记录下来了吗?是否可以为自定义后缀修改此行为?

I'm trying to understand how to handle header file dependencies in Make rules. Let me give you a specific example.

I'm building application called myap using GNU Make. It consists of various *.h and *.c files.

Directory inc/ contains defs.h and util.h header files.

Directory src/ contains main.c, cmd.c and win.c files.

Directory obj/ contains all generated object files.

I have multiple applications that need different build options. So I don't want to rely on any implicit rules and would like to specify my own rules for all object files, etc.

I would like to specify the following rules:

Object files depend on specific *.h and *.c files. If any of them change, all object files must be regenerated. However, even though *.h files are part of the prerequisites list, I don't want to pass them to the compiler. I only want to compile *.c files.

Executable myapp depends on specific *.o files. If any of them change, executable file must be regenerated.

So far, the following Makefile with a static pattern rule seems to work correctly:

myapp_inc := inc/defs.h inc/util.h
myapp_src := src/main.c src/cmd.c src/win.c
myapp_obj := $(patsubst src/%.c,obj/%.o,$(myapp_src))
myapp_bin := obj/myapp

.PHONY: all
all:

# Create obj/main.o obj/cmd.o and obj/win.o from various *.c files
# If any *.h files in $(myapp_inc) list change, all objects are regenerated.
# If any *.c files in $(myapp_src) list change, all objects are regenerated.
$(myapp_obj): obj/%.o: src/%.c $(myapp_inc) $(myapp_src)
    gcc -c -o $@ 
lt;

# Create obj/myapp from various *.o files
# If any *.o files in $(myapp_obj) list change, executable is regenerated.
$(myapp_bin): $(myapp_obj)
    gcc -o $@ $^

all: $(myapp_bin)

.PHONY: clean
clean:
    rm -f obj/*

I don't quite understand how Make rules should be written correctly in order to handle such use case. Is the above static pattern rule, the only way that works correctly?

Specifically, I have tried the following combinations, as given in various simple examples on the Internet, and they all failed for various reasons.

This rule causes $< to always pass the name of the first prerequisite, which doesn't work with multiple *.c files:

$(myapp_obj): $(myapp_src) $(myapp_inc)
    gcc -c -o $@ 
lt;

$ make
gcc -c -o obj/main.o src/main.c
gcc -c -o obj/cmd.o src/main.c
gcc -c -o obj/win.o src/main.c
gcc -o obj/myapp obj/main.o obj/cmd.o obj/win.o
/bin/ld: obj/cmd.o: in function `main':
main.c:(.text+0x0): multiple definition of `main'; obj/main.o:main.c:(.text+0x0): first defined here
/bin/ld: obj/win.o: in function `main':
main.c:(.text+0x0): multiple definition of `main'; obj/main.o:main.c:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status
make: *** [Makefile:18: obj/myapp] Error 1

This rule causes $^ to always pass the names of all prerequisites, which fails:

$(myapp_obj): $(myapp_src) $(myapp_inc)
    gcc -c -o $@ $^

$ make
gcc -c -o obj/main.o src/main.c src/cmd.c src/win.c inc/defs.h inc/util.h
gcc: fatal error: cannot specify ‘-o’ with ‘-c’, ‘-S’ or ‘-E’ with multiple files
compilation terminated.
make: *** [Makefile:13: obj/main.o] Error 1

Now I understand the difference between $< and $^ variables, but a lot of documentation is not clear on how they should be used when dealing with a list of multiple *.c and *.h files as prerequisites.

What are the recommended usage pattern for this?

Why is it that when using $< only *.c files get passed to the recipe, but not *.h files? Is Make doing some internal filtering? Is this documented anywhere? Is it possible to modify this behavior for custom suffixes?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

海未深 2025-01-27 06:43:16

上述静态模式规则是使对象依赖于 *.h 和 *.c 文件,但在编译期间排除 *.h 文件的唯一方法吗?

我不明白试图避免隐含规则的目的。但无论如何,规则是隐式的还是显式的对于您编写的配方来说并不重要:无论哪种方式都设置相同的自动变量。 $< 自动变量始终是第一个先决条件,因此,如果您编写的规则使得第一个先决条件是适当的 .c 文件,那么您始终可以使用 $< 在你的配方中表示 .c 文件,而不是其他文件。以下所有内容都将起作用:

%.o : %.c $(headers)
        gcc -c -o $@ 
lt;
foo.o: foo.c $(headers)
        gcc -c -o $@ 
lt;
foo.o : %.o : %.c $(headers)
        gcc -c -o $@ 
lt;

%.o : %.c
        gcc -c -o $@ 
lt;
$(srcs) : $(headers)

以及其他。

这是否意味着所有先决条件都适用,但只有那些与模式匹配的先决条件才会传递给配方?

我真的不明白这个问题。变量的值和配方的扩展仅在 make 决定运行规则之后发生,并且并不真正相关(除了一些特殊的自动变量,如 $?)。一旦 make 确定目标已过时并且需要运行配方,它将分配适当的自动变量,扩展配方,然后将配方传递给要运行的 shell。

自动变量按照手册中的描述进行分配:$@ 是目标,$< 是第一个先决条件,$^ 是全部先决条件等。

预计到达时间

您还没有真正解释为什么不想使用静态模式规则。它们是一种完美且合理的做事方式。

如果您解释了您不喜欢静态模式规则的哪些方面,或者您希望采取不同的做法,那么我们可能会建议满足这些要求的替代方案。

具体来说,我尝试了以下组合,如互联网上各种简单示例所示,

$(myapp_obj): $(myapp_src) $(myapp_inc)

无论您在 Internet 上的何处发现此作为推荐示例,您都应该立即从任何书签中删除,因为该站点对 make 一无所知。

我们每周至少在 SO 上看到一次这种范例。我从来没有真正理解为什么人们认为它会起作用:我猜他们认为 make 比它实际上更“神奇”。考虑一下,上面的内容扩展到什么?假设 myapp_obj 包含 foo.o bar.o biz.omyapp_src 包含 foo.c bar.c biz.c 和 myapp_inc 包含 foo.h bar.h,然后 make 看到:

foo.o bar.o biz.o: foo.c bar.c biz.c foo.h bar.h

我想有些人认为 make 会凭直觉认为“.o”文件应该以某种方式与“.c”文件并将生成一堆规则来实现这一点。 make 不是这么做的。上面的行与编写此内容完全相同

foo.o: foo.c bar.c biz.c foo.h bar.h
bar.o: foo.c bar.c biz.c foo.h bar.h
biz.o: foo.c bar.c biz.c foo.h bar.h

也就是说,如果您有多个目标,则 make 会为每个目标创建一份规则副本,并具有相同的先决条件和配方。

这显然不是您想要的,这就是为什么尝试以这种方式做事的示例都无法正常工作。

为什么使用$<时会出现这样的情况?只有 *.c 文件传递​​到配方,而不是 *.h 文件? Make 是否进行了一些内部过滤?这有记录在任何地方吗?是否可以修改自定义后缀的此行为?

事实并非如此。正如我上面所描述的,$< 扩展为第一个先决条件。就这样。第一个先决条件是 .c 文件、.h 文件还是其他文件并不重要;无论它是什么,$< 将是该值。如果您将规则编写为:

foo.o : foo.c foo.h ; $(CC) -c -o $@ 
lt;

那么您的编译器将使用 foo.c 调用。如果您将规则编写为:

foo.o : foo.h foo.c ; $(CC) -c -o $@ 
lt;

那么您的编译器将使用 foo.h 调用。这里没有魔法。

Is the above static pattern rule, the only way to make objects depend on *.h and *.c files, but exclude *.h files during compilation?

I don't understand the goal of trying to avoid implicit rules. But in any event, it doesn't matter to the recipe you write whether the rule was implicit or explicit: the same automatic variables are set either way. The $< automatic variable is always the first prerequisite, so if you write your rules such that the first prerequisite is the appropriate .c file then you can always use $< in your recipe to mean the .c file and no other files. All the following will work:

%.o : %.c $(headers)
        gcc -c -o $@ 
lt;
foo.o: foo.c $(headers)
        gcc -c -o $@ 
lt;
foo.o : %.o : %.c $(headers)
        gcc -c -o $@ 
lt;

%.o : %.c
        gcc -c -o $@ 
lt;
$(srcs) : $(headers)

and others.

Does this mean that all of the prerequisites apply, but only those that match the pattern get passed to the recipe?

I don't understand the question, really. The value of variables and the expansion of the recipe happens only AFTER make has decided to run the rule and is not really related (except for some special automatic variables like $?). Once make has decided that the target is out of date and the recipe needs to be run, it will assign the appropriate automatic variables, expand the recipe, then pass the recipe to the shell to be run.

The automatic variables are assigned as described in the manual: $@ is the target, $< is the first prerequisite, $^ is all the prerequisites, etc.

ETA

You still haven't really explained why you don't want to use static pattern rules. They are a perfectly fine and reasonable way to do things.

If you explain what you don't like about static pattern rules, or what you wish you could do differently, then we can probably suggest alternatives that meet those requirements.

Specifically, I have tried the following combinations, as given in various simple examples on the Internet,

$(myapp_obj): $(myapp_src) $(myapp_inc)

Wherever you found this as a recommended example on the Internet, you should immediately delete from any bookmarks as that site doesn't know anything about make.

We see this paradigm at least once a week on SO. I've never really understand why people think it will work: I guess they think make is much more "magical" than it is. Consider, what does the above expand to? Suppose myapp_obj contained foo.o bar.o biz.o and myapp_src contained foo.c bar.c biz.c and myapp_inc contained foo.h bar.h, then make sees:

foo.o bar.o biz.o: foo.c bar.c biz.c foo.h bar.h

I suppose some people think make will intuit that the ".o" files should somehow match up with the ".c" files and will generate a bunch of rules that make that true. That's not what make does. The above line is exactly identical to writing this:

foo.o: foo.c bar.c biz.c foo.h bar.h
bar.o: foo.c bar.c biz.c foo.h bar.h
biz.o: foo.c bar.c biz.c foo.h bar.h

That is, if you have multiple targets make creates one copy of the rule for each target, with the same prerequisites and recipe.

This is obviously not what you want, and that's why none of the examples that try to do things this way can ever work properly.

Why is it that when using $< only *.c files get passed to the recipe, but not *.h files? Is Make doing some internal filtering? Is this documented anywhere? Is it possible to modify this behavior for custom suffixes?

None of that is the case. As I described above, the $< expands to the first prerequisite. That's all. It doesn't matter whether the first prerequisite is a .c file, a .h file, or some other file; whatever it is, $< will be that value. If you write your rule as:

foo.o : foo.c foo.h ; $(CC) -c -o $@ 
lt;

then your compiler will be invoked with foo.c. If you write your rule as:

foo.o : foo.h foo.c ; $(CC) -c -o $@ 
lt;

then your compiler will be invoked with foo.h. There's no magic here.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文