我将使用一个小例子作为参考。考虑一个具有以下内容的项目:
inner_definitions.o : inner_definitions.cpp inner_definitions.h
gcc $^ -o $@
inner_class_1.o : inner_class_1.cpp inner_class_1.h inner_definitions.h
gcc $^ -o $@
inner_class_2.o : inner_class_2.cpp inner_class_2.h inner_definitions.h
gcc $^ -o $@
outer_class.o : outer_class.cpp outer_class.h inner_class_1.h inner_class_2.h
gcc $^ -o $@
executable.o : executable.cpp executable.h outer_class.h
gcc $^ -o $@
executable : __?1__
__?2__
但是填充链接器依赖项的空白 __?1__
和链接器命令的 __?2__
并不容易。
在这个小例子中,人们可能会说很容易看出 __?1__ = inside_definitions.o inside_class_1.o inside_class_2.o outer_class.oexecutable.o
。然而,这显然不是一个可扩展的解决方案,因为它迫使每个开发人员了解他们正在使用的代码的所有依赖关系,以便他们可以手动找出依赖关系,而不是使用 make
实用程序。
另一种解决方案是为每个目标文件设置一个不同的变量,列出其所有下游依赖项:即 __?1__ =executable.o $(executable_dependencies)
。这不是一个理想的解决方案,因为它强制 makefile 以特定方式编译,因此变量仅在完全定义时才使用。此外,对于非常大的应用程序,这些变量可能会超过最大变量长度。
另一种解决方案是使用存档 .a 文件进行链接。在这种情况下,我们可以构造一个包含 inner_defintions.o
和 inner_class_1.o
的 inner_class_1.a
,因此它可以与任何需要 inner_class_1.o
的对象文件,而不强制开发人员重建依赖关系。这种方法看起来很有前途,但涉及到许多重复的文件。此外,gcc 链接器似乎无法处理嵌套的存档文件。
还有另一种方法吗?最好的方法是什么? gcc
链接器可以处理嵌套归档文件吗?
I'm going to use a small example for reference. Consider a project with:
inner_definitions.o : inner_definitions.cpp inner_definitions.h
gcc $^ -o $@
inner_class_1.o : inner_class_1.cpp inner_class_1.h inner_definitions.h
gcc $^ -o $@
inner_class_2.o : inner_class_2.cpp inner_class_2.h inner_definitions.h
gcc $^ -o $@
outer_class.o : outer_class.cpp outer_class.h inner_class_1.h inner_class_2.h
gcc $^ -o $@
executable.o : executable.cpp executable.h outer_class.h
gcc $^ -o $@
executable : __?1__
__?2__
But filling in the blanks __?1__
for the linker dependencies and __?2__
for the linker command isn't easy.
In this small example, one could argue that its easy to see that __?1__ = inner_definitions.o inner_class_1.o inner_class_2.o outer_class.o executable.o
. However, this is clearly not a scalable solution as it forces each developer to understand all the dependencies of the code they are working with so they can figure out the dependencies by hand rather than by using the make
utility.
Another solution would be to have a different variable for each object file that listed all its downstream dependencies: i.e __?1__ = executable.o $(executable_dependencies)
. This is not a desired solution because it forces the makefile to be compiled in the specific way so the variables are only used when they are fully defined. Also, for really large applications these variables might exceed the maximum variable length.
Yet another solution is to use archive .a files for linking. In this case, we could construct an inner_class_1.a
that included both inner_defintions.o
and inner_class_1.o
, so it could be linked with any object file that needed inner_class_1.o
without forcing the developer to reconstruct the dependencies. This approach seems promising, but involves having many duplicate files. Also, it doesn't appear that the gcc
linker can handle nested archive files.
Is there another approach? What is the best approach? Can the gcc
linker handle nested archive files?
发布评论
评论(2)
您尝试自动化的工作(选择正确的目标文件以满足所有引用)通常留给链接器,使用静态库(“.a”文件)对候选目标文件进行分组,就像您建议的那样。
您可能会忽略一个重要的细节:如果您向链接器传递一个存档,它只会链接该存档中实际需要的那些文件。因此,您可以以相当粗的粒度创建档案,而不必膨胀所有可执行文件——链接器将只选择它需要的内容——尽管如果您采取这种方法太过分,很容易导致不必要的缓慢构建。
GNU 链接器不会从嵌套库中提取对象。如果您想通过合并许多小库来创建一个大库,可以使用 ar 脚本。这将为您提供一个包含所有目标文件的单个存档,而无需任何嵌套库结构。
如果重复拥有包含相同目标代码的 .o 文件和 .a 文件让您感到困扰,精美的手册描述了一种“直接”更新档案的方法。
The job you're trying to automate (picking the right object files to satisfy all references) is usually left to the linker, using static libraries (".a" files) to group the candidate object files, just as you suggest.
An important detail you may be missing: If you pass the linker an archive, it will only link in those files from the archive that are actually needed. So you can create archives at a fairly coarse level of granularity without necessarily bloating all your executables -- the linker will pick just what it needs -- although can easily end up with needlessly slow builds if you take this approach too far.
The GNU linker will not pull objects out of nested libraries. If you want to make one big library by merging many small ones, you can do that with the "addlib" command in an ar script. That will give you a single archive containing all of the object files without any nested library structure.
If the duplication of having .o files and .a files containing the same object code lying around bothers you, the fine manual describes a way to have make update the archives "directly".
你的 makefile 必须有一个链接在一起的对象列表,如下所示:
必须有人写这个; gcc 不能为你做这件事,Make 也不能为你做这件事。并非项目中的每个开发人员都需要知道如何构建该列表,只有编写 makefile 的该部分的开发人员才需要知道。所有其他文件都将使用该 makefile,并且添加新依赖项(例如
inner_class_3
)的开发人员可以将其添加到列表中。如果您的 makefile 在火灾中丢失,并且唯一知道所有依赖项的开发人员被公共汽车击中,那么重建列表实际上并不难:当您尝试使
可执行
时,链接器抱怨foo::bar()
未定义,您 grep 并发现foo::bar()
是在inner_class_2.cpp
中定义的>,将inner_class_2.o
添加到列表中。重复直到链接器停止抱怨。PS一旦按顺序排列,您就可以大大简化 makefile 的其余部分:
编辑:
编辑:
好吧,这是先进技术的概述。
首先,考虑所有#included 头文件。最好让 Make 处理依赖项,而不是像上面的 makefile 那样手动放入它们。这是一个简单的任务:如果 X.cpp #includes Yh (直接或通过一些 #included 头文件链),那么 Xo 将依赖于 Yh 这已经被计算为 "高级自动依赖生成"。但是如果您遵循严格的命名约定,则可以更进一步:如果在 Xh 中声明但未定义的所有内容都在 X.cpp 中定义,那么通过遵循相同的 #include 语句树,我们应该能够构造一个列表所需的目标文件,这些文件将成为
可执行文件
的依赖项。这确实需要一次性吸收很多内容,所以我不会尝试通过一个示例来完成。我建议您查看该文档,看看它如何生成 Yh 依赖项,然后尝试将其应用到示例 makefile,然后考虑“进一步”应该做什么。
稍后您可以将其应用到测试工具,其中的目标文件为,例如
outer_class.o
、stub_inner_class_1.o
、stub_inner_class_2.o
和test_outer_class.o
。Your makefile must have a list of objects to link together, like so:
Someone must write this; gcc can't do it for you, Make can't do it for you. Not every developer on the project needs to know how to construct that list, only the one who writes that part of the makefile. All the others will use that makefile, and a developer who adds a new dependency (e.g.
inner_class_3
) can add it to the list.And if your makefile is lost in a fire and the only developer who knows all the dependencies is hit by a bus, it really isn't hard to reconstruct the list: when you try to make
executable
, the linker complains thatfoo::bar()
is undefined, you grep around and discover thatfoo::bar()
is defined ininner_class_2.cpp
, you addinner_class_2.o
to the list. Repeat until the linker stops complaining.P.S. Once that's in order, you can simplify the rest of the makefile quite a lot:
EDIT:
EDIT:
All right, here's an outline of the advanced technique.
First, consider all of the #included header files. It would be nice to have Make handle the dependencies instead of putting them in by hand, as in the makefile above. And this is a straightforward task: if X.cpp #includes Y.h (either directly or through some chain of #included header files), then X.o will depend on Y.h. This has already been worked out as "Advanced Auto-Dependency Generation". But if you follow a strict naming convention, you can take it a step further: if everything declared but not defines in X.h is defined in X.cpp, then by following the same tree of #include statements we should be able to construct a list of the needed object files, which will then be the dependencies of
executable
.This really is a lot to absorb at once, so I won't try to work through an example. I suggest you look over the document and see how it can generate the Y.h dependencies, then try applying it to the example makefile, then think about what the "step further" should do.
Later you can apply it to the test harness, where the object files are, e.g.,
outer_class.o
,stub_inner_class_1.o
,stub_inner_class_2.o
andtest_outer_class.o
.