Makefile,“配置”文件和其他编译工具——它们如何工作?他们为什么要这样工作?
我对 UNIX/Linux 世界还是个新手,尤其是相关工具,例如 GCC 编译器。也就是说,我对 makefile 和类似的东西还是很陌生(我在 Windows 上使用 MinGW),因为到目前为止,我的大部分开发都是使用 Visual Studio 和 Eclipse 等 IDE 进行的。
当我打开一个典型的项目文件夹时,我会看到如下文件:
configure
make
INSTALL
install-sh (or a variant)
这里有很多我不明白的事情,我的主要问题是:
这些之间有什么区别,为什么我们需要每一个? (在 IDE 世界中,您拥有的只是一个项目文件;仅此而已。所以我很困惑为什么我们这里不仅仅是一个 makefile。)
这些文件是如何生成的?对于小型项目,您可能可以手动编写它们,但对于像 GCC 这样的大型项目,甚至尝试都是荒谬的。我发现编辑这些文件很痛苦,并且我得出的结论是我不应该手动修改它们。但如果是这样,那么人们通常使用什么工具来添加和修改这些文件?
为什么他们不在 makefile 中使用通配符?为什么每个目标文件都有一行?这是因为限制,还是有优势?
使用每个文件调用编译器的 shell 脚本与执行相同操作的 makefile 有什么区别?在 Windows 中,我很想在文件夹内创建一个批处理文件,然后用它编译所有内容——不需要多个文件。有什么区别?为什么不只用一个
.sh
文件来编译所有内容?
额外问题:
- 是否有“标准”makefile 格式?我见过不同的
make
工具,它们不接受彼此的格式...我如何知道该使用什么? GCC 只是这里常用的工具,还是有一些每个人都应该遵循的标准?
随着我对此类项目结构的了解越来越多,我可能会有更多问题,但就目前而言,这些是我最大的问题。 :)
I'm still new to the UNIX/Linux world, and, in particular, to related tools, such as the GCC compiler. Namely, I'm still new to makefiles and things like that (I use MinGW on Windows), since so far, most of my development has been with IDEs like Visual Studio and Eclipse.
When I open a typical project folder, I see files like these:
configure
make
INSTALL
install-sh (or a variant)
There's quite a few things I don't understand here, and my main questions are these:
What's the difference between these, and why do we need each one? (In the IDE world, all you have is a project file; that's it. So I'm confused why we have more than just a makefile here.)
How are these files generated? For small projects, you could probably write them by hand, but for big projects like GCC, it would be ridiculous to even try. I've found editing these files to be a pain, and I've come to the conclusion that I'm not supposed to be modifying them by hand. But if so, then what tools do people typically use for adding and modifying these files?
Why don't they use wildcards inside a makefile? Why is there a single line for every single object file? Is this because of a restriction, or is there an advantage to it?
What's the difference between having a shell script that calls the compiler with every file, and having a makefile that does the same thing? In Windows, I'd be very tempted to just create a batch file inside the folder, and compile everything with that -- no need for more than just one file. What's the difference? Why isn't there just a single
.sh
file, to compile everything?
Bonus question:
- Is there a "standard" makefile format? I've seen different
make
tools that don't accept each other's formats... how do I know what to use? Is GCC just the usual tool here, or is there some standard everyone should follow?
I'll probably have more questions as I learn more about the structure of projects like this, but for now, these are my biggest questions. :)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
在您的普通包中,这些文件都有各自的用途。这与“每个程序应该做一件事并做好那件事”的unix哲学是一致的。在大多数项目中,您会看到如下文件:
配置
configure.ac
Makefile
Makefile.in
Makefile.am
安装sh
安装
configure
(通常)是一个 shell 脚本,用于在构建任何内容之前检查系统是否具有所有必需的功能。Makefile.in
是Makefile
的模板。配置测试的结果被替换到Makefile.in
中以生成Makefile
。这是为了处理人们在模糊路径中拥有东西(编译器、头文件、库)、交叉编译(例如,在 x86 上为 ARM 构建)、可选库支持(某些程序具有可以打开或关闭的附加功能)、使用不同的选项进行编译,等等。编写一个万能的Makefile
实际上真的很难。正如您所注意到的,
configure
脚本本身是一团糟。它不适合凡人的眼睛看到,也不适合凡人的手编辑。它实际上是使用名为autoconf
的程序编译configure.ac
的结果。autoconf
是m4
宏处理器的宏包和包装器,它是当时处理此类事情的唯一好工具(autoconf
确实是相当古老的软件,但已经非常成熟了)。autoconf
使开发人员可以轻松编写测试来检查构建软件所需的标头、库或程序(并且这会因程序而异)。如果你再深入一点,你会发现
Makefile.in
也有点难看。这是因为编写好的Makefile
通常需要大量样板文件,这启发了另一个工具automake
。automake
将Makefile.am
(通常很短且是声明性的)编译为Makefile.in
(非常庞大),然后编译为 < code>Makefile byconfigure
(本质上)。install-sh
是与automake
一起分发但复制到其他包中的脚本。如果系统上的install
版本很糟糕(install
将文件复制到安装目录中),它会作为替代品存在。一些非常旧的系统的install 版本已损坏
,而automake
对于删除旧系统的警告非常保守)。其他一些履行类似角色的脚本有compile
、depcomp
和ylwrap
。INSTALL
只是一个描述如何安装软件包的文档。它通常是由automake
复制到包中的样板内容。我已经在上面回答了这个问题,但总结如下:
configure.ac ==[autoconf]==>配置
Makefile.am ==[automake]==> Makefile.in ==[配置]==>生成文件
负责的程序位于箭头内。要详细了解这一点,我推荐这个 autotools 教程。不要被页数吓倒,大部分都是逐个出现的图表。
有时会使用
Makefile
中的通配符。例如,GNU Make 支持$(wildcard)
函数,您可以在其中编写如下内容:SOURCES := $(通配符 src/*.c)
不使用像
$(wildcard)
这样的功能的主要原因是它们是扩展,并且automake
非常努力地生成Makefile
可与任何 POSIX 兼容make
配合使用。无论如何,项目成熟后,要编译的文件列表不会发生太大变化。显式列出文件的第二个原因是程序获得可选功能时。通配符不再适用,您必须列出编译附加功能的条件。
Makefile 跟踪文件之间的依赖关系,而 shell 脚本则无法跟踪文件之间的依赖关系(并非没有重要意义) 。无论如何,努力)。
如果您有如下
Makefile
规则:它告诉
make
如果foo.in
比更新 foo.out
,可以通过执行generate-foo foo.in
创建一个新的foo.out
。这可以节省大型项目中的大量冗余工作,在这些项目中,您可能只在重新编译之间更改一两个文件。你的奖金问题似乎有点不恰当。最常见的
make
可能是 GNU Make,尽管我猜测 BSD make 会紧随其后,其次是 Solaris、AIX 提供的各种专有make
版本等等。它们都接受
Makefile
中相同的基本结构(因为 POSIX 是这么说的),但可能具有特定于供应商的语法扩展。GCC 不是像
make
那样的构建工具。 GCC 是一个命令行编译器,类似于 Windows 上的 cl.exe。In your average package, those files each have an individual purpose. This is concordant with the unix philosophy of "each program should do one thing and do that thing well". In most projects, you'll see files like:
configure
configure.ac
Makefile
Makefile.in
Makefile.am
install-sh
INSTALL
configure
is (usually) a shell script that checks your system for all required features before building anything.Makefile.in
is a template for aMakefile
. The results of the configure tests are substituted intoMakefile.in
to generateMakefile
. This is to deal with people having things (compilers, headers, libraries) in obscure paths, cross-compiling (e.g., building for ARM on x86), optional library support (some programs have additional features that can be switched on or off), compiling with different options, and so on. Writing a one-size-fits-allMakefile
is actually really hard.As you've noticed, the
configure
script itself is a mess. It's not meant to be seen by mortal eyes, nor edited by mortal hands. It's actually the result of compilingconfigure.ac
, using a program calledautoconf
.autoconf
is a package of macros for and a wrapper around them4
macro processor, which was about the only good tool for this sort of thing at the time (autoconf
is really quite old software, but has aged remarkably well).autoconf
lets the developer easily write tests to check for headers, libraries or programs that are required to build the software (and this changes from program to program).If you dig a little deeper, you'll notice that
Makefile.in
also tends to be a little ugly. This is because writing goodMakefile
s is often a lot of boilerplate, and that's inspired another tool,automake
.automake
compilesMakefile.am
(which is often short and declarative) intoMakefile.in
(which is enormous), which is then compiled intoMakefile
byconfigure
(essentially).install-sh
is a script that is distributed withautomake
but copied into other packages. It exists as a replacement if the version ofinstall
on the system is crap (install
copies files into the installation directory. Some really old systems had broken versions ofinstall
, andautomake
is pretty conservative about dropping warnings for old systems). Some other scripts that fulfil similar roles arecompile
,depcomp
andylwrap
.INSTALL
is just a document that describes how to install the package. It's usually boilerplate content copied into the package byautomake
.I've answered this inline above, but here is the summary:
configure.ac ==[autoconf]=> configure
Makefile.am ==[automake]=> Makefile.in ==[configure]=> Makefile
Where the program responsible is inside the arrow. To understand this in detail, I recommend this autotools tutorial. Don't be put off by the page count, most of it is diagrams appearing piece by piece.
Wildcards in
Makefile
s are sometimes used. GNU Make, for example, supports a$(wildcard)
function, where you can write something like:SOURCES := $(wildcard src/*.c)
The main reason features like
$(wildcard)
aren't used is that they are extensions, andautomake
tries very hard to generateMakefile
s that will work with any POSIX-compliantmake
. After a project becomes mature, the list of files to compile doesn't change all that much, anyway.A second reason files are listed explicitly is when programs get optional features. Wildcards are no longer appropriate, and you instead have to list the conditions under which additional features are to be compiled in.
A
Makefile
tracks dependencies between files, where a shell script cannot (not without significant effort, anyway).If you have a
Makefile
rule like:It tells
make
that iffoo.in
is newer thanfoo.out
, a newfoo.out
can be created by executinggenerate-foo foo.in
. This saves lots of redundant work on big projects, where you might only change one or two files between recompilations.Your bonus question appears a little ill-posed. The most common
make
is probably GNU Make, although I'd guess that the BSD make would be a close second, followed by the various proprietarymake
versions supplied with Solaris, AIX and so on.These all accept the same basic structure in
Makefile
(because POSIX says so), but might have vendor-specific extensions to the syntax.GCC is not a build tool like
make
. GCC is a command-line compiler, akin tocl.exe
on windows.造成不同情况的原因有很多,但最终都归结为可移植性。在 Unix 战争期间,每个平台都是不同的,人们试图制作可以在尽可能多的平台上运行的软件。因此必须找到共同点。 sh 就是这样的通用性之一,因此编写配置脚本以使用最大程度可移植的 sh。最大的可移植性意味着使用最少的功能集。不同的 make 实现在它们支持的功能方面差异很大,因此编写 Makefile 时使用尽可能少的功能。如今情况稍有好转,但可移植性问题仍然是您所看到的许多事情的推动因素。
There are a lot of reasons for different things, but in the end it boils down to portability. During the unix wars, every platform was different, and people tried to make software that would work on as many as possible. So common denominators had to be found. sh was one such commonality, so configure scripts were written to use maximally portable sh. Maximal portability implies using a minimal set of features. Different implementations of make vary greatly in the features they support, so Makefiles were written that used as few features as possible. Things are slightly better today, but portability concerns still drive a lot of the things you are seeing.
configure
是一个设置构建环境并可能生成 makefile 的脚本。使用make
构建项目,使用make install
或install.sh
或类似工具安装编译后的文件。如果您想在本地测试某些内容,您可能希望将最后一部分排除在外,因此它是单独的。configure
is a script that sets up the build environment and possibly generates the makefile. Withmake
you build the project, withmake install
orinstall.sh
or similar, you install the compiled files. If you want to test something locally, you might want to leave the final part out and so it's seperate.