C语言 C中的多个源文件-makefile究竟是如何工作的?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4349553/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 07:17:30  来源:igfitidea点击:

Multiple source files in C- How exactly do makefiles work?

cfilemakefile

提问by Aeonstrife

I'm a relative beginner to C and I need to learn how makefiles work and I'm a bit confused on how the combination of C files work. Say we have a main.c, a foo.c, and a bar.c. How should the code be written so that main.c recognizes functions in the other files? Also, in foo.c and bar.c, is all of the code written in the main function there or do we need to write other functions for what we need them to do? I've read tutorials on how makefiles are written, and it makes sense for the most part, but I'm still a bit confused on the basic logistics of it.

我是 C 的相对初学者,我需要了解 makefile 的工作原理,而且我对 C 文件的组合如何工作感到有些困惑。假设我们有一个 main.c、一个 foo.c 和一个 bar.c。应该如何编写代码以便 main.c 识别其他文件中的函数?另外,在 foo.c 和 bar.c 中,是否所有的代码都写在 main 函数中,还是我们需要编写其他函数来完成我们需要它们做什么?我已经阅读了关于如何编写 makefile 的教程,这在很大程度上是有道理的,但我对它的基本逻辑仍然有些困惑。

回答by Gavin H

Generally what will happen is you will define your functions for the other files in a header file, which can then be included in main.c. For example, consider these snippets:

通常会发生的是,您将在头文件中为其他文件定义函数,然后可以将其包含在 main.c 中。例如,考虑这些片段:

main.c:

主文件:

#include "foo.h"

int main(int argc, char *argv[]) {
    do_foo();
    return 0;
}

foo.h:

foo.h:

void do_foo();

foo.c:

foo.c:

#include <stdio.h>
#include "foo.h"

void do_foo() {
    printf("foo was done\n");
}

What will happen is that main.c will be turned into an object file (main.o), and foo.c will be turned into an object file (foo.o). Then the linker will link these two files together and that is where the do_foo()function in main.c is 'associated' with the function in foo.o.

将会发生的事情是 main.c 将变成一个目标文件 (main.o),而 foo.c 将变成一个目标文件 (foo.o)。然后链接器将这两个文件链接在一起,这就是do_foo()main.c 中的函数与 foo.o 中的函数“关联”的地方。

Example GCC command: gcc -o myprogram main.c foo.c

示例 GCC 命令:gcc -o myprogram main.c foo.c

Example makefile

示例生成文件

myprogam: main.o foo.o
    gcc -o myprogram main.o foo.o

main.o: main.c foo.h
    gcc -c main.c

foo.o: foo.c foo.h
    gcc -c foo.c

回答by Kos

About C/C++ compilation

关于 C/C++ 编译

When you have a set of files, you usually do 2 things:

当你有一组文件时,你通常会做两件事:

  • compile each source file (.c) to an object file (.o)
  • link all object files into an executable.
  • 将每个源文件 (.c) 编译为目标文件 (.o)
  • 将所有目标文件链接到一个可执行文件中。

The source files are independent- you need header filesto be able to provide the "information" (declarations) about functions in a given module in order to let any other module use them. Header files are not compiled by themselves - they are #included as parts of source files.

源文件是独立的- 您需要头文件才能提供有关给定模块中函数的“信息”(声明),以便让任何其他模块使用它们。头文件不是自己编译的——它们是#include源文件的一部分。

Have a look below on how the commands for that look like and how they are handled by make.

看看下面的命令是什么样的,以及它们是如何被处理的make



About makefiles

关于生成文件

Makefile is a set of targets and rules to build them. A targetis "something which can be built and results in a given file". (There exist also "phony" targets which don't result in a file and just are there to execute commands - a common one is called cleanto remove the compilation results).

Makefile 是一组构建它们的目标和规则。一个目标是“东西可以在一个给定的文件建立和结果”。(也存在“虚假”目标,它们不会生成文件,只是在那里执行命令 - 调用一个常见的目标clean来删除编译结果)。

Each target has 2 parts:

每个目标有 2 个部分:

  • list of dependencies, "sensitivity list"(other files and targets which are needed for this target) (after :, comma-separated),
  • list of shell commandswhich are executed to build this target (below the above, indented)
  • 依赖项列表“敏感性列表”(此目标所需的其他文件和目标)(后:,逗号分隔),
  • 为构建此目标而执行的 shell 命令列表(在上面的下方,缩进)

Consider this example:

考虑这个例子:

main: main.o module1.o module2.o
    g++ main.o module1.o module2.o -o main

This means that: "To build the file main, I need to first make sure that targets main.o, module1.oand module2.oare up to date; then I need to call the following command...".

这意味着:“要构建文件main,我需要首先确保目标main.omodule1.o并且module2.o是最新的;然后我需要调用以下命令......”。

This can be also rewritten as:

这也可以改写为:

main: main.o module1.o module2.o
    gcc $^ -o $@

The variables (everything starting with $is a variable) will be expanded to the dependencies list and the target name, as you expect.

如您所料,变量(所有$以变​​量开头的内容)将扩展为依赖项列表和目标名称。

You can define your own variables and expand them as follows:

您可以定义自己的变量并按如下方式展开它们:

OBJS = main.o module1.o module2.o

main: $(OBJS)
    # code goes here


You compile individual translation unitsas follows:

您可以按如下方式编译各个翻译单元

main.o: main.c
    gcc -c $< -o $@
    # note the -c option, which means: "compile, but don't link"
    # $< will expand to the first source file

You can add header dependencies to rebuild main.o when either main.c or any of its headers change:

当 main.c 或其任何头文件更改时,您可以添加头文件依赖项以重建 main.o:

main.o: main.c module1.h module2.h
    gcc -c $< -o $@

In order not to write the same command over and over again, you can define a general rule and just supply the dependencies (if you want):

为了不一遍又一遍地编写相同的命令,您可以定义一个通用规则并只提供依赖项(如果需要):

%.o: %.c
    gcc -c $< -o $@

main.o: main.c module1.h module2.h
module1.o: module1.c module1.h module2.h

There's also some magic to generate the dependencies automatically (see link). One of the downsides of using Make is that it doesn't do it by itself (as some building systems do - like SCons which I prefer for C/C++).

还有一些魔法可以自动生成依赖项(见链接)。使用 Make 的缺点之一是它不能自己完成(就像某些构建系统所做的那样 - 比如我更喜欢 C/C++ 的 SCons)。

回答by Clifford

In essence a makefile comprises of rules of the form:

本质上,makefile 包含以下形式的规则:

<this file> : <needs these files>
    <and is created by this command>

You normally have at least one high level target, if any of its dependencies do not exist, make looks for a rule that has that file as a target. It does this recursively until it has resolved all dependencies of the top-level target, before executing the top-level command (if there is one - both dependencies and command are optional fields in a rule)

您通常至少有一个高级目标,如果它的任何依赖项不存在,则 make 查找将该文件作为目标的规则。在执行顶级命令之前,它会递归执行此操作,直到解决了顶级目标的所有依赖项(如果存在 - 依赖项和命令都是规则中的可选字段)

A make file can have 'default rules' based on patterns, and there are built-in macros for various file matching scenarios as well as user define macros and inclusion of nested makefiles.

生成文件可以具有基于模式的“默认规则”,并且有用于各种文件匹配方案的内置宏以及用户定义的宏和嵌套生成文件的包含。

I have simplified the above rule form to the most usual case. In fact the command need not create the target at all, it is simply a command to be executed once all the files in the dependency are present. Moreover the target need not be a file either. Often the top level target is a 'dummy' target called "all" or similar.

我已将上述规则形式简化为最常见的情况。事实上,该命令根本不需要创建目标,它只是一个在依赖项中的所有文件都存在后执行的命令。此外,目标也不必是文件。通常,顶级目标是称为“全部”或类似的“虚拟”目标。

There are of course many subtleties and nuances to make, all detailed in the manual(GNU make specifically, there are other make utilities).

当然,还有许多微妙之处和细微差别需要制作,所有这些都在手册中有详细说明(特别是 GNU make,还有其他 make 实用程序)。

回答by nmichaels

Functions in foo.cthat need to be called from outside foo.cshould have prototypes in foo.h. The outside files that need to call those functions should then #include "foo.h". foo.cand bar.cshouldn't even have a main()function if they're part of the same program as main.c.

foo.c需要从外部调用的函数foo.c应该在foo.h. 然后,需要调用这些函数的外部文件应该#include "foo.h". foo.c并且bar.c甚至不应该有一个main()功能,如果他们在同一个程序的一部分main.c

Makefiles define targets. For simple programs, you can just have a single target that compiles the whole thing. More complex (read: bigger) programs can have intermediate targets (like foo.o) that will let makeavoid needless recompilation. The way makedetermines whether or not a given target needs to be recompiled is it looks at the modification times of all the prerequisites (the stuff after the colon) and if any of them come after the last changed time of the target file itself, it gets rebuilt.

Makefile 定义目标。对于简单的程序,您可以只拥有一个编译整个程序的目标。更复杂(阅读:更大)的程序可以有中间目标(如 foo.o),make可以避免不必要的重新编译。方式make确定给定的目标是否需要重新编译为它看起来在所有的先决条件(冒号后的内容)的修改时间,如果其中任何一个来最后一个更改的时间目标文件本身后,它会重建。

Here's a very simple example:

这是一个非常简单的例子:

main.c:

主文件:

#include "foo.h"

int main()
{
    fooprint(12);
    return 0;
}

foo.c:

foo.c:

#include "stdio.h"
#include "foo.h"

void fooprint(int val)
{
    printf("A value: %d\n", val);
}

foo.h:

foo.h:

void fooprint(int val);

Makefile:

生成文件:

main: main.c foo.o
    gcc -o main main.c foo.o

foo.o: foo.c
    gcc -c foo.c

Then you can run make mainand it will compile foo.cinto foo.othen compile main.c and link it with foo.o. If you modify main.c, it'll just recompile main.cand link it against the already built foo.o.

然后你可以运行make main,它会编译foo.cfoo.o然后编译 main.c 并将它与 foo.o 链接。如果您修改main.c,它只会重新编译main.c并将其链接到已经构建的foo.o.

回答by Nicholas Carey

Make has little to do with the structure of a C program. All make does is define a dependency tree and execute commands when it finds the dependencies are out of whack. My saying, in a makefile:

Make 与 C 程序的结构关系不大。make 所做的只是定义一个依赖树,并在发现依赖关系不正常时执行命令。我的说法,在makefile中:

foo.exe : foo.c bar.c baz.c

simply sez: foo.exe is dependent on foo.c, bar.c and baz.c. This, sotto vocce, gets expanded, using make's default rule set, to something like:

简单的 sez:foo.exe 依赖于 foo.c、bar.c 和 baz.c。这个,sotto vocce,使用 make 的默认规则集扩展为:

foo.exe : foo.obj bar.obj baz.obj

foo.obj : foo.c

bar.obj : bar.c

baz.obj : baz.c

Make simply walks the dependency tree starting at its root (in this case, foo.exe). If a target doesn't exist or if one of the objects upon which it depends is newer than the target, the associated commands are executed. to make the dependency correct.

Make 只是从其根(在本例中为 foo.exe)开始遍历依赖关系树。如果目标不存在或者它所依赖的对象之一比目标新,则执行关联的命令。使依赖正确。

See Managing Projects with Makefrom O'Reilly for more than you probably want to know.

请参阅O'Reilly 的Make 管理项目,了解您可能想知道的更多信息。

As far as the second part of your question goes, the answer is just two letters: K and R. Their The C Programming Languageis arguably one of the best computer programming books ever written.

就您问题的第二部分而言,答案只是两个字母:K 和 R。他们的The C Programming Language可以说是有史以来最好的计算机编程书籍之一。

alt text

替代文字

回答by vishesh

How Makefile works ?

Makefile 如何工作?

=> When the make command is executed on terminal, it looks for a file named makefile or Makefile in the current directory and constructs a dependency tree.

=> 在终端执行make命令时,它会在当前目录中查找名为makefile或Makefile的文件,并构建一个依赖树。

If you have several Makefiles, then you can execute specific with the command:

如果您有多个 Makefile,那么您可以使用以下命令执行特定的:

                           make -f MyMakefile

=> Based on make target specified in makefile, make checks if the dependency files of that target exist. And if they exist, whether they are newer than the target itself, by comparing file timestamps.

=> 基于 makefile 中指定的 make 目标,make 检查该目标的依赖文件是否存在。如果它们存在,通过比较文件时间戳,它们是否比目标本身更新。

Here our first and default target is “all” which looks for main.o and function.o file dependencies. Second and third target is main.o and function.o respectively which have dependencies of main.c and function.c respectively.

=> Before executing the commands of corresponding target, its dependencies must be met, when they are not met, the targets of those dependencies are executed before the given make target, to supply the missing dependencies.

=> 在执行相应目标的命令之前,必须满足其依赖项,当不满足时,这些依赖项的目标在给定的 make 目标之前执行,以提供缺失的依赖项。

=> When a target is a file-name, make compares the time-stamps of the target file and its dependency files. If the dependency file is newer than target file, target execute otherwise not execute.

=> 当目标是文件名时,make 会比较目标文件及其依赖文件的时间戳。如果依赖文件比目标文件新,则目标执行,否则不执行。

In our case, when first target “all” start executing it looks for main.o file dependency, if its not met. Then it goes to second target main.o which check for its dependency main.c and compare time-stamp with it. If target found main.c dependency is updated, then target execute else not. Same process is follow for next target function.o.

=> It thus winds up recursively checking all the way down the dependency tree, to the source code files. By this process, make saves time, by executing only commands that need to be executed, based on which of the source files (listed as dependencies) have been updated, and have a newer time-stamp than their target.

=> 因此,它会一直递归地检查依赖关系树,直到源代码文件。通过这个过程,make 节省了时间,只执行需要执行的命令,基于哪些源文件(列为依赖项)已更新,并且具有比目标更新的时间戳。

=> Now, when a target is not a file-name(which we called “special targets”), make obviously cannot compare time-stamps to check whether the target's dependencies are newer. Therefore, such a target is always executed.

=> 现在,当目标不是文件名(我们称之为“特殊目标”)时,make 显然无法通过比较时间戳来检查目标的依赖项是否更新。因此,总是执行这样的目标。

In our Makefile, special targets are “all” and “clean”. As we discussed target “all” earlier, but we not discuss target clean. Target clean removes the all object files created during compilation and binary executable files according to command.

For the execution of each target, make prints the actions while executing them. Note that each of the command are executed in a separate sub-shell environment because of secure execution so that they can not change current shell environment which may affect other target execution. For example, if one command contains cd newdir, the current directory will be changed only for that line command, for the next line command, the current directory will be unchanged.

对于每个目标的执行,make 会在执行时打印动作。请注意,由于安全执行,每个命令都在单独的子 shell 环境中执行,因此它们不能更改可能影响其他目标执行的当前 shell 环境。例如,如果一个命令包含 cd newdir,则仅针对该行命令更改当前目录,对于下一行命令,当前目录将保持不变。

Source : - http://www.firmcodes.com/linux/write-first-makefile-c-source-code-linux-tutorial/

来源:- http://www.firmcodes.com/linux/write-first-makefile-c-source-code-linux-tutorial/