📜  Linux make命令

📅  最后修改于: 2020-12-08 03:08:40             🧑  作者: Mango

Linux make命令

Linux make命令用于从源代码构建和维护程序和文件组。在Linux中,它是开发人员最常用的命令之一。它帮助开发人员从终端安装和编译许多实用程序。此外,它还处理大型项目的编译过程。这样可以节省编译时间。

make命令的主要目的是将一个大型程序分成几部分,并检查是否需要重新编译。同样,它发出必要的命令以重新编译它们。

在本节中,我们将使用C++程序,因为C++编程语言是一种面向对象的语言,但是您可以使用计算机上安装的任何语言。它不仅限于程序;我们也可以用它来描述其他任务。

make命令如何工作?

make命令将目标作为参数。这些参数在“ Makefile”中指定。生成文件包含目标以及与这些目标有关的关联操作。

当我们执行make命令时,它将搜索makefile并对其进行扫描以找到目标并访问其依赖项。如果未指定依赖关系,它将搜索并建立依赖关系。建立依赖关系后,它将构建主要目标。

例如,如果我们只想更改一个源文件,然后执行make命令;因此,这只会编译与该源文件连接的目标文件。这将在项目的最终编译中节省大量时间。

什么是Makefile?

make命令调用makefile的执行。这是一个特殊文件,其中包含我们为维护项目而创建的Shell命令。生成文件包含执行的目标和命令。不允许创建多个makefile。建议为其创建一个单独的目录。

它跟踪最近的文件,因此仅更新所需的那些文件。如果我们有一个包含许多源文件的大型程序,则必须重新编译所有从属文件。因此,这可能是一个非常耗时的过程。

生成文件具有标准列表。这些标准有助于系统了解我们要执行的命令。这些标准分为两部分,并用新行分隔。第一行是相关性行,随后的行被视为动作或命令。这些命令用新行中的选项卡分隔。

依赖关系指定每个文件与源文件的关系。目标是一个可执行文件,它是在执行make命令之后创建的。

选件

make命令提供了各种选项,以使其更加具体。一些重要的选项如下:

  • -b,-m:这些选项用于忽略make命令不同版本的兼容性。
  • -B,–always-make:这些选项用于无条件地制作所有目标。
  • -C dir,–directory = dir:这些选项用于在执行Makefile之前更改目录。
  • -d:用于print调试信息。
  • –debug [= FLAGS]:用于print调试信息以及正常处理。如果我们跳过该标志,那么它将显示与“ -d”选项相似的结果。
  • -e,-environment-overrides:用于将取自环境优先级的变量提供给makefile。
  • -f文件,–file =文件,–makefile = FILE:用于将文件用作makefile。
  • -i,-ignore-errors: “-i”选项用于忽略命令中的所有错误。
  • -I dir,–include-dir = dir:用于指定目录以搜索指定的makefile。如果我们指定许多“ -I”选项,它将按指定的顺序在许多目录中搜索。
  • -j [jobs],–jobs [= jobs]:用于指定要同时运行的作业数。如果我们提供许多“ -j”选项,则将考虑执行最后一个。如果我们不指定作业数量,则不会限制可以同时运行的作业。
  • -k,–keep-going:用于在出现错误后尽可能继续执行程序。
  • -l [load],–load-average [= load]:用于指定如果队列中有其他任务并且平均负载最小,则不应该启动任何新任务。
  • -n,–just-print,–dry-run,–recon:用于显示将要运行的命令。
  • -o file,-old-file = file,-assume-old = file:用于确保make不会重新制作该文件,即使该文件早于其依赖项也是如此。
  • -O [type],– output -sync [= type]:用于确认每个任务的输出是否放在一起,而不是其他任务的混合输出。使用'-j'选项对多作业处理很有用。
  • -p,– print -data-base:用于打印读取makefile后生成的数据库。与'-v'选项一起使用时,print版本信息也很有用。要print数据库而不尝试重新制作任何文件,请执行以下命令:
    使-p -f / dev / null。
  • -q,–question: “-q”选项用于问题模式。它不会运行任何命令或print任何内容。如果指定的目标已经同步,则只会返回退出状态为零。否则,它将显示非零的退出状态。
  • -r,–no-builtin-rules:用于消除对内置隐式规则的使用。
  • -R,–no-builtin-variables:如果我们不想定义任何内置变量,则很有用。
  • -s,-silent,-quiet:这些选项称为静默操作。它限制了在执行命令时print命令。
  • -S,–no-keep-going,–stop:用于取消“ -k,–keep-going”操作的效果。
  • -t,– touch 用于触摸文件而不是运行文件命令。
  • –trace:用于跟踪每个目标的配置。
  • -v,– version 用于printmake实用程序的安装版本。此外,它还显示了作者列表,版权以及有关make实用程序的一些注意事项。
  • -w,– print -directory:用于跟踪在其他处理之前和之后包含工作目录的打印消息。从递归的make命令的复杂结构中查找错误很有用。
  • –no-print-directory:用于关闭“ -w”选项。
  • -W文件,–what-if = file,–new-file = file,–assume-new = file:这些选项假装目标文件刚刚被修改。
  • –warn-undefined-variables:此选项用于警告引用了未定义的变量。

让我们了解一下make命令的一些示例。我们将了解makefile的基本用法,此外,我们将创建一些C++程序和一个makefile。我们将对它们执行一些操作以更好地理解make命令。

make命令的基本用法

让我们了解一下make命令的最基本用法,它可以帮助您了解它的工作原理。

创建目录“项目”并更改目录。考虑以下命令:

mkdir project
cd project

现在,为第一个程序创建一个具有以下内容的“ Makefile”:

say_hello:
    echo "Hello World!"

在上面的文件中,say_hello是一个目标,其行为类似于任何编程语言中的函数,并且echo将被视为一项操作。必须记住,应该使用TAB来编写动作目标和操作共同为makefile创建规则。现在,执行make命令,如下所示:

考虑以下输出:

从上面的输出中,我们可以看到echo操作本身正在显示。如果我们不想在输出上显示echo命令,请执行以'@'符号开头的echo 。要抑制echo,请如下更新makefile的内容:

say_hello:
    @echo "Hello World!"

考虑以下输出:

目标可能是取决于操作的二进制文件。

让我们再添加一些目标,例如makefile中的generate和list。更新makefile,如下所示:

say_hello:
    @echo "Hello World!"
generate:
    @echo "Creating files"
    touch file-{1..5}.txt
list:
    @echo "Listing files"
    ls 

如果执行make命令,则它仅执行第一个目标,因为它是makefile的默认目标。考虑以下输出:

我们可以通过在makefile中包含以下内容来更改默认目标:

.DEFAULT_GOAL := generate

如下所示将其添加到文件的第一行:

上面的makefile将把'generate'作为默认目标。执行make命令,它将给出如下输出:

DEFAULT GOAL选项将仅执行一个目标以指定多个目标来使用所有选项。要指定多个目标,请如下更新makefile的第一行:

all: say_hello generate

它将执行指定的目标。考虑以下输出:

还有另一个选项可以让我们执行所有目标。如果我们要执行makefile的所有目标,请如下更新文件:

.PHONY: all say_hello generate list
say_hello:
    @echo "Hello World!"
generate:
    @echo "Creating files"
    touch file-{1..5}.txt
list:
    @echo "Listing files"
    ls 

上面的文件将执行所有指定的目标。执行make命令,考虑以下输出:

高级使用make命令

让我们创建一个包含文件main.cpp,function1.cpp,function2.cpp和一个依赖文件函数.h的C++项目。

文件的代码如下:

main.cpp:

#include
#include "functions.h"
int main()
{
    print_hello();
    std::cout<< std::endl;
    std::cout<< "The factorial of 5 is" << factorial(5) << std:: endl;
    return 0;
}

function1.cpp:

#include "functions.h"

int factorial(int n)
{
  if(n!=1)
    {
      return (n * factorial(n-1));
    }
      else return 1;
    }

function2.cpp:

#include 
#include "functions.h"
void print_hello()
{
  std::cout << "Hello World";
}

functions.h:

void print_hello();
int factorial (int n);

现在,通过执行以下命令来创建上述项目的可执行文件:

g++ main.cpp function1.cpp function2.cpp -o hello

上面的命令将创建文件main.cpp,function1.cpp和function2.cpp的可执行文件“ hello”。

考虑以下输出:

从上面的输出中,如果成功执行,将不会提供任何输出。

让我们通过使用makefile执行相同的任务。

创建一个文件作为Makefile并将以下代码放入其中。

all:
    g++ main.cpp function1.cpp function2.cpp -o hello

all关键字用于目标,在换行符中,使用与上述TAB相同的命令来指定操作。保存文件。考虑以下文件:

要进行操作,请执行以下命令:

make

上面的命令将创建指定文件的可执行文件“ hello”。考虑以下输出:

让我们向Makefile添加更多任务。添加任务“编译” ,如下所示:

all:

compile:
    g++ main.cpp function1.cpp function2.cpp -o hello

要执行任务编译,请执行以下命令:

make compile

上面的命令将执行编译任务。考虑以下输出:

让我们对makefile执行更多任务。

更新Makefile,如下所示:

all: hello
hello: main.o function1.o function2.o
    g++ main.o function1.o function2.o -o hello
main.o: main.cpp
    g++ -c main.cpp
function1.o: function1.cpp
    g++ -c function1.cpp
function2.o: function2.cpp
    g++ -c function2.cpp
clean:
    rm -rf *o hello

从上面的makefile文件中,我们创建了三个对象,分别是main.o,function1.o和function2.o。此外,我们分别提供了目标main.o,function1.o和function2.o的依赖关系,分别为main.cpp,function1.cpp和function2.cpp。所有目标将在其中执行指定的任务。我们还指定了一个清理目标来清理所有依赖项并删除可执行文件。

现在执行make all命令以执行我们的新makefile。

make all

考虑以下输出:

从上面的输出中,我们可以看到该命令首先分别执行了main.o,function1.o和function2.o。它将创建给定文件的可执行文件和对象文件。它不会执行清理目标,因为我们尚未在hello中指定它。考虑以下文件:

make命令具有直接的工作过程。它执行了all选项并打招呼。执行hello后,它将按指定顺序读取目标。它搜索每个目标及其依赖性,并按顺序执行它们。

要删除对象和可执行文件,请执行清理任务。要执行清理任务,请执行以下命令:

make clean

考虑以下输出:

上面的命令将删除所有对象和可执行文件。请参见下面的目录快照:

从上图可以看到我们已经清理了目录。

make命令中的变量

我们可以在makefile中定义变量。要定义变量,请使用'=“运算符。例如,如果要创建变量A并为其分配命令gcc,请将其分配为:

A=g++

在我们的makefile中按以下方式使用它:

hello: hello.cpp
    ${A} hello.cpp -o hello

它通过以下方式传递到终端:

g++ hello.cpp -o hello

我们可以使用$(A)代替$ {A},因为脚本对两者的处理相同。

Makefile中的注释

要将注释添加到makefile中,请使用“#”符号。例如,要在编译部分添加注释,请将其添加为“#这将编译程序” 。注释将被编译器忽略。