c++ - 如何制作一个简单的 C++ Makefile

标签 c++ makefile

我们需要使用 Makefile 为我们的项目整合所有内容,但我们的教授从未向我们展示过如何做。

我只有一个文件,a3driver.cpp .驱动程序从一个位置导入一个类,"/user/cse232/Examples/example32.sequence.cpp" .

而已。其他所有内容都包含在 .cpp 中.

我将如何制作一个简单的 Makefile 来创建一个名为 a3a.exe 的可执行文件?

最佳答案

由于这是针对 Unix 的,可执行文件没有任何扩展名。

需要注意的一件事是 root-config是一个提供正确编译和链接标志的实用程序;以及用于针对 root 构建应用程序的正确库。这只是与本文档的原始受众相关的详细信息。

让我宝贝

否则你永远不会忘记你第一次被制造

make 的介绍性讨论,以及如何编写一个简单的 makefile

什么是制造?我为什么要关心?

该工具名为 Make是一个构建依赖管理器。也就是说,它负责了解需要以何种顺序执行哪些命令才能从源文件、目标文件、库、头文件等的集合中获取您的软件项目——其中一些可能已更改最近---并将它们转换为程序的正确最新版本。

实际上,您也可以将 Make 用于其他用途,但我不打算谈论它。

一个简单的 Makefile

假设您有一个包含以下内容的目录:tool tool.cc tool.o support.cc support.hh , 和 support.o取决于 root并且应该被编译成一个名为 tool 的程序,并假设您一直在攻击源文件(这意味着现有的 tool 现在已过时)并想要编译该程序。

要自己做这件事,你可以

  • 检查是否 support.ccsupport.hhsupport.o 新,如果是这样运行命令
    g++ -g -c -pthread -I/sw/include/root support.cc
    
  • 检查是否 support.hhtool.cctool.o 新,如果是这样运行命令
    g++ -g  -c -pthread -I/sw/include/root tool.cc
    
  • 检查是否 tool.otool 新,如果是这样运行命令
    g++ -g tool.o support.o -L/sw/lib/root -lCore -lCint -lRIO -lNet -lHist -lGraf -lGraf3d -lGpad -lTree -lRint \
    -lPostscript -lMatrix -lPhysics -lMathCore -lThread -lz -L/sw/lib -lfreetype -lz -Wl,-framework,CoreServices \
    -Wl,-framework,ApplicationServices -pthread -Wl,-rpath,/sw/lib/root -lm -ldl
    

  • 呼!多麻烦啊!有很多东西要记住,也有很多机会犯错误。 (顺便说一句——这里展示的命令行的细节取决于我们的软件环境。这些在我的电脑上工作。)

    当然,您可以每次都运行所有三个命令。这会起作用,但它不能很好地扩展到大量软件(例如 DOGS,它在我的 MacBook 上从头开始编译需要超过 15 分钟)。

    相反,您可以编写一个名为 makefile 的文件。像这样:
    tool: tool.o support.o
        g++ -g -o tool tool.o support.o -L/sw/lib/root -lCore -lCint -lRIO -lNet -lHist -lGraf -lGraf3d -lGpad -lTree -lRint \
            -lPostscript -lMatrix -lPhysics -lMathCore -lThread -lz -L/sw/lib -lfreetype -lz -Wl,-framework,CoreServices \
            -Wl,-framework,ApplicationServices -pthread -Wl,-rpath,/sw/lib/root -lm -ldl
    
    tool.o: tool.cc support.hh
        g++ -g  -c -pthread -I/sw/include/root tool.cc
    
    support.o: support.hh support.cc
        g++ -g -c -pthread -I/sw/include/root support.cc
    

    然后输入 make在命令行。这将自动执行上面显示的三个步骤。

    此处未缩进的行具有“目标:依赖项”的形式,并告诉 Make 如果任何依赖项比目标更新,则应运行关联的命令(缩进行)。也就是说,依赖行描述了需要重新构建以适应各种文件中的更改的逻辑。如 support.cc更改意味着 support.o必须重建,但 tool.o可以单独留下。当support.o变化 tool必须重建。

    与每个依赖行相关联的命令由一个选项卡(见下文)引出,应该修改目标(或至少触摸它以更新修改时间)。

    变量、内置规则和其他好东西

    在这一点上,我们的 makefile 只是记住需要做的工作,但我们仍然必须弄清楚并完整地键入每个需要的命令。不一定是这样:Make 是一种强大的语言,具有变量、文本操作函数和大量内置规则,可以使我们更容易地做到这一点。

    制作变量

    访问 make 变量的语法是 $(VAR) .

    分配给 Make 变量的语法是:VAR = A text value of some kind(或 VAR := A different text value but ignore this for the moment)。

    您可以在类似我们 makefile 的改进版本的规则中使用变量:
    CPPFLAGS=-g -pthread -I/sw/include/root
    LDFLAGS=-g
    LDLIBS=-L/sw/lib/root -lCore -lCint -lRIO -lNet -lHist -lGraf -lGraf3d -lGpad -lTree -lRint \
           -lPostscript -lMatrix -lPhysics -lMathCore -lThread -lz -L/sw/lib -lfreetype -lz \
           -Wl,-framework,CoreServices -Wl,-framework,ApplicationServices -pthread -Wl,-rpath,/sw/lib/root \
           -lm -ldl
    
    tool: tool.o support.o
        g++ $(LDFLAGS) -o tool tool.o support.o $(LDLIBS)
    
    tool.o: tool.cc support.hh
        g++ $(CPPFLAGS) -c tool.cc
    
    support.o: support.hh support.cc
        g++ $(CPPFLAGS) -c support.cc
    

    这更具可读性,但仍然需要大量输入

    制作函数

    GNU make 支持从文件系统或系统上的其他命令访问信息的各种函数。在这种情况下,我们对 $(shell ...) 感兴趣它扩展到参数的输出,和 $(subst opat,npat,text)它替换了 opat 的所有实例与 npat在文本中。

    利用这一点,我们可以:
    CPPFLAGS=-g $(shell root-config --cflags)
    LDFLAGS=-g $(shell root-config --ldflags)
    LDLIBS=$(shell root-config --libs)
    
    SRCS=tool.cc support.cc
    OBJS=$(subst .cc,.o,$(SRCS))
    
    tool: $(OBJS)
        g++ $(LDFLAGS) -o tool $(OBJS) $(LDLIBS)
    
    tool.o: tool.cc support.hh
        g++ $(CPPFLAGS) -c tool.cc
    
    support.o: support.hh support.cc
        g++ $(CPPFLAGS) -c support.cc
    

    这更容易打字,也更易读。

    请注意
  • 我们仍然明确说明每个目标文件和最终可执行文件的依赖关系
  • 我们必须明确输入两个源文件的编译规则

  • 隐式和模式规则

    我们通常希望所有 C++ 源文件都应该以相同的方式处理,Make 提供了三种方式来说明这一点:
  • 后缀规则(在 GNU make 中被认为已过时,但为了向后兼容而保留)
  • 隐含规则
  • 图案规则

  • 隐式规则是内置的,下面将讨论一些。模式规则以类似的形式指定
    %.o: %.c
        $(CC) $(CFLAGS) $(CPPFLAGS) -c $<
    

    这意味着目标文件是通过运行显示的命令从 C 源文件生成的,其中“自动”变量 $<扩展为第一个依赖项的名称。

    内置规则

    Make 有很多内置规则,这意味着实际上,一个项目通常可以由一个非常简单的 makefile 编译。

    C 源文件的 GNU make 内置规则是上面展示的规则。类似地,我们使用类似 $(CXX) -c $(CPPFLAGS) $(CFLAGS) 的规则从 C++ 源文件创建目标文件。 .

    使用 $(LD) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS) 链接单个目标文件,但这在我们的情况下不起作用,因为我们想要链接多个目标文件。

    内置规则使用的变量

    内置规则使用一组标准变量,允许您指定本地环境信息(例如在哪里可以找到 ROOT 包含文件),而无需重新编写所有规则。我们最有可能感兴趣的是:
  • CC -- C 编译器使用
  • CXX -- 要使用的 C++ 编译器
  • LD -- 要使用的链接器
  • CFLAGS -- C 源文件的编译标志
  • CXXFLAGS -- C++ 源文件的编译标志
  • CPPFLAGS -- C 预处理器的标志(通常包括在命令行上定义的文件路径和符号),由 C 和 C++ 使用
  • LDFLAGS -- 链接器标志
  • LDLIBS -- 要链接的库

  • 一个基本的 Makefile

    通过利用内置规则,我们可以将我们的 makefile 简化为:
    CC=gcc
    CXX=g++
    RM=rm -f
    CPPFLAGS=-g $(shell root-config --cflags)
    LDFLAGS=-g $(shell root-config --ldflags)
    LDLIBS=$(shell root-config --libs)
    
    SRCS=tool.cc support.cc
    OBJS=$(subst .cc,.o,$(SRCS))
    
    all: tool
    
    tool: $(OBJS)
        $(CXX) $(LDFLAGS) -o tool $(OBJS) $(LDLIBS)
    
    tool.o: tool.cc support.hh
    
    support.o: support.hh support.cc
    
    clean:
        $(RM) $(OBJS)
    
    distclean: clean
        $(RM) tool
    

    我们还添加了几个执行特殊操作(如清理源目录)的标准目标。

    请注意,当不带参数调用 make 时,它​​使用在文件中找到的第一个目标(在本例中为全部),但您也可以命名要获取的目标,这就是使 make clean 的原因。在这种情况下删除目标文件。

    我们仍然对所有依赖项进行了硬编码。

    一些神秘的改进
    CC=gcc
    CXX=g++
    RM=rm -f
    CPPFLAGS=-g $(shell root-config --cflags)
    LDFLAGS=-g $(shell root-config --ldflags)
    LDLIBS=$(shell root-config --libs)
    
    SRCS=tool.cc support.cc
    OBJS=$(subst .cc,.o,$(SRCS))
    
    all: tool
    
    tool: $(OBJS)
        $(CXX) $(LDFLAGS) -o tool $(OBJS) $(LDLIBS)
    
    depend: .depend
    
    .depend: $(SRCS)
        $(RM) ./.depend
        $(CXX) $(CPPFLAGS) -MM $^>>./.depend;
    
    clean:
        $(RM) $(OBJS)
    
    distclean: clean
        $(RM) *~ .depend
    
    include .depend
    

    请注意
  • 源文件不再有任何依赖行!?!
  • 有一些与 .depend 和 Depend 相关的奇怪魔法
  • 如果你这样做 make然后 ls -A你会看到一个名为 .depend 的文件其中包含看起来像 make 依赖行的内容

  • 其他阅读
  • GNU make manual
  • Recursive Make Considered Harmful关于编写不太理想的 makefile 的常用方法,以及如何避免它。

  • 知道错误和历史记录

    Make 的输入语言对空格敏感。特别是,依赖项后面的操作行必须以选项卡开头。但是一系列的空格看起来是一样的(确实有一些编辑器会默默地将制表符转换为空格,反之亦然),这会导致 Make 文件看起来正确但仍然无法工作。这在早期被确定为错误,但( the story goes )并未修复,因为已经有 10 个用户。

    (这是从我为物理研究生写的维基帖子中复制的。)

    关于c++ - 如何制作一个简单的 C++ Makefile,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2481269/

    相关文章:

    c++ - 迷失在指针的世界里

    c++ - 大型阵列的幂集

    c++ - c++中的时间计数器

    gfortran 编译器的 Vim 错误格式

    c - 海湾合作委员会错误 : cannot specify -c and -o with multiple files

    c++ - LCD 和 RTC_DS1307- 无法在液晶显示器上正确打印 1-9 位秒数

    c++ - 这种优化技术的名称是什么?

    c++ - 创建跨平台 C++ 库

    gcc - make -j distcc 是否可以扩展超过 5 倍?

    makefile 将特定变量作为先决条件