【问题标题】:Create linux make/build file创建 linux make/build 文件
【发布时间】:2010-08-26 14:49:08
【问题描述】:

我正在将一个 C++ 项目从 Windows 移动到 Linux,我现在需要创建一个构建/制作文件。我以前从未创建过构建/制作文件。我还需要包含 Boost 库以使其更复杂。 它也必须是一个makefile,我需要学习如何创建makefile,所以CMake和SCON都出来了。由于使用了 Boost,IDE 也被淘汰了,我所有的 IDE(Eclipse、VS 等)都只在 Windows 上。我必须从头开始生成一个 makefile。

那么创建 Linux c++ make 文件的基础是什么,以及如何将 Boost 库合并到其中以使其正确链接?

到目前为止,我的 makefile 看起来像这样。我认为CFLAGSLDFLAGS 是编译器和优化选项,但并不完全确定。

CC = g++
CFLAGS = -wall -o3 - c
LDFLAGS = -03 -mfp-rounding-mode=n

我提供赏金是因为我仍然很迷茫。如果有人喜欢冒险,我需要在 linux 中编译以下内容

  • simple_ls.h
  • simple_ls.cpp
  • 2dquicksort.h
  • rawr.h
  • rawr.cpp
  • converter.cpp

simple_ls.h 中的头文件:

#include "boost/filesystem/operations.hpp"
#include "boost/filesystem/path.hpp"
#include "boost/lexical_cast.hpp"

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

2dquicksort.h 中的标头:

#include <stdio.h>
#include <ctype.h>
#include <iostream>

rawr.h 中的头文件:

#include <iostream> // not required by most systems
#include <fstream>
#include <iomanip>
#include <cstdlib> // or (stdlib.h) for exit()

#include <cmath> 
#include <vector>
#include <limits>
#include <string>

一个可接受的答案是逐步解释 makefile 如何工作以及如何在没有 IDE 的情况下在 Linux 中使用 Boost 构建它们。

【问题讨论】:

  • 最好使用 CMake 或 SCons,而不是手动创建 makefile。好多了不要误会我的意思,自己创建它们是一种很好的做法,但对于大型项目来说根本无法管理。
  • 一个地方你说 IDE 已经过时了,最后提到的是你想要一个我不明白的 IDE 解决方案。
  • 您明确表示“我所有的 IDE(Eclipse、VS 等)都只在 Windows 上”。您可能想要修改它,Eclipse CDT 可用于多种平台,包括 linux。另请注意,conio.h 不是标准标头,它在 linux 中不可用。
  • @David,我设法删除了 conio.h 标头,感谢您的提醒
  • 请问为什么不能选择像 cmake 这样的现代工具?如果您一开始就使用 cmake 之类的东西,那么整个情况都不会出现问题,因为您可以为任何平台或 unix makefile 生成 IDE 项目文件。此外,使用 Boost 不会以任何方式、形状或形式阻止您使用 IDE。请原谅我,但这篇文章似乎是在提出问题之前决定问题答案的经典案例。

标签: c++ linux boost makefile


【解决方案1】:

什么是 Makefile(应用于Boost 项目)

Makefile 背后的根递归思想是:

要构建目标,我们需要先决条件(其他目标!)和说明来构建

先决条件

它们要么是文件、文件夹要么是虚假目标(通常在.PHONY)。测试文件/文件夹的存在和修改日期。

如果目标没有先决条件或比任何先决条件更旧,则需要重建目标。

说明

指令是 shell 命令,从一个选项卡开始。每个指令行是一个 shell 实例。当当前命令以反斜杠 \ 结尾时,shell 命令可以在下一行继续。

目标定义

目标是依赖项规则

依赖:

target : prerequisite1 prerequisite2 prerequisiteN

规则:

target : prerequisite1 prerequisite2 prerequisiteN
    instructions1
    @hidden_batch1 ; \
  hidden_batch2  

指令开始前有制表符。

调试

调试 Makefile 可能会让人头疼。 在您的 Makefile 中尝试以下操作以显示跟踪(warning 的文件和行位置):

$(info Shell: $(SHELL))
$(warning CXX: $(CXX))

当您的 Makefile 包含大量嵌套的 if/else/endif 并且您不确定时,这很有帮助 现在的路径是什么。

Makefile 结构

理想的makefile结构是:

  1. 变量设置
  2. 目标/依赖声明

真正的目标指令处理开始于整个 Makefile 及其包含文件 已被理解(存储在make内部数据库中)。

示例

最后,将理论应用到这个使用 Boost 的特定示例并创建假源文件 来说明。

rawr.cpp

#include "rawr.h"

simple_ls.cpp

#include "rawr.h"

converter.cpp

#include <iostream>

#include "rawr.h"
#include "simple_ls.h"
#include "2dquicksort.h"

#include <boost/array.hpp>   // Boost! 

int main(int argc, char **argv)
{
    boost::array<int,4> a = { { 1, 2, 3, 4} };
    std::cout << a[1] << std::endl;
    return 0;
}

生成文件

如果您从 *stack***overflow** 复制 Makefile 源代码,请不要忘记用真正的制表符替换空格:

sed -i~ -e 's/^    /\t/' Makefile

Makefile 来源:

## Makefile for C++ project using Boost
#
# @author Cedric "levif" Le Dillau
#
# Some notes:
# - Using ':=' instead of '=' assign the value at Makefile parsing time,
#   others are evaluated at usage time. This discards
# - Use ':set list' in Vi/Vim to show tabs (Ctrl-v-i force tab insertion)
#

# List to '.PHONY' all fake targets, those that are neither files nor folders.
# "all" and "clean" are good candidates.
.PHONY: all, clean

# Define the final program name
PROGNAME := converter

# Pre-processor flags to be used for includes (-I) and defines (-D) 
CPPFLAGS := -DUSE_BOOST

# CFLAGS is used for C compilation options.
CFLAGS := -Wall -O0

# CXXFLAGS is used for C++ compilation options.
CXXFLAGS += -Wall -O0

# LDFLAGS is used for linker (-g enables debug symbols)
LDFLAGS  += -g

# Which Boost modules to use (all)
BOOST_MODULES = \
  date_time     \
  filesystem    \
  graph         \
  iostreams     \
  math_c99      \
  system        \
  serialization \
  regex

# Boost libraries' type (a suffix)
BOOST_MODULES_TYPE := -mt

# Define library names with their type
BOOST_MODULES_LIBS := $(addsuffix $(BOOT_MODULES_TYPE),$(BOOST_MODULES))

# Define the linker argument to use the Boost libraries.
BOOST_LDFLAGS := $(addprefix -lboost_,$(BOOST_MODULES_LIBS))

# Feed compiler/linker flags with Boost's
CPPFLAGS += $(BOOST_CPPFLAGS)
LDFLAGS += $(BOOST_LDFLAGS)

# List the project' sources to compile or let the Makefile recognize
# them for you using 'wildcard' function.
#
#SOURCES = simple_ls.cpp rawr.cpp converter.cpp
SOURCES = $(wildcard *.cpp)

# List the project' headers or let the Makefile recognize
# them for you using 'wildcard' function.
#
#HEADERS = simple_ls.h 2dquicksort.h rawr.h
HEADERS = $(wildcard %.h)

# Construct the list of object files based on source files using
# simple extension substitution.
OBJECTS = $(SOURCES:%.cpp=%.o)

#
# Now declare the dependencies rules and targets
#
# Starting with 'all' make it  becomes the default target when none 
# is specified on 'make' command line.
all : $(PROGNAME)

# Declare that the final program depends on all objects and the Makfile
$(PROGNAME) : $(OBJECTS) Makefile
    $(CXX) -o $@ $(LDFLAGS) $(OBJECTS)

# Now the choice of using implicit rules or not (my choice)...
#
# Choice 1: use implicit rules and then we only need to add some dependencies
#           to each object.
#
## Tells make that each object file depends on all headers and this Makefile.
#$(OBJECTS) : $(HEADERS) Makefile
#
# Choice 2: don't use implicit rules and specify our will
%.o: %.cpp $(HEADERS) Makefile
    $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $(OUTPUT_OPTION) $<

# Simple clean-up target
# notes:
# - the '@' before 'echo' informs make to hide command invocation.
# - the '-' before 'rm' command to informs make to ignore errors.
clean :
    @echo "Clean."
    -rm -f *.o $(PROGNAME)

文件列表

2dquicksort.h
converter.cpp
Makefile
rawr.cpp
rawr.h
simple_ls.cpp
simple_ls.h

编译

make clean all
Clean.
rm -f *.o converter
g++ -Wall -O0 -DUSE_BOOST  -c -o converter.o converter.cpp
g++ -Wall -O0 -DUSE_BOOST  -c -o rawr.o rawr.cpp
g++ -Wall -O0 -DUSE_BOOST  -c -o simple_ls.o simple_ls.cpp
g++ -o converter -g -lboost_date_time -lboost_filesystem -lboost_graph -lboost_iostreams -lboost_math_c99 -lboost_system -lboost_serialization -lboost_regex converter.o rawr.o simple_ls.o

结果

现在,几乎是最小的 Boost 程序的结果:

./converter
2

没有理由不使用它! Boost 确实是一个特色 C++ 工具箱 :)

【讨论】:

    【解决方案2】:

    一个非常简单的 GNU makefile 如下:

    CPPFLAGS += -Isome_include_path
    CXXFLAGS += -O3
    LDFLAGS  += -Lsome_link_path -lsome_lib -lboost_filesystem
    
    all: binary_name
    binary_name: foo.o bar.o john.o
    
    • CPPFLAGS 是应用于 C 预处理器的标志。诸如包含路径之类的内容。
    • CXXFLAGS 是应用于 C++ 编译器的标志。优化级别等。
    • LDFLAGS 是应用于链接器的标志。外部库(boost_filesystem)和其他库之类的东西。还有这些库的路径。
    • 约定说应该有一个make all 规则,这是默认的。在 make 中,第一条规则是默认的。
    • binary_name 是您的二进制文件的名称。
    • binary_name 依赖于 3 个文件:foo.obar.ojohn.o
    • 我们不包括 *.o 的规则,因为 gnu make 有一个隐含的规则。

    要使用 make,您将使用上面列出的内容创建一个名为 Makefile 的文件。要构建,您将在该目录中运行 make

    顺便说一句(正如其他人所提到的),我建议尽可能远离 make。那里有更好的系统。 make 的主要好处是它无处不在。但是,如果管理需要它,那么管理就需要它。

    (请注意,GNU Make += 表示法并不总是可移植到其他版本的 Make。但是,Linux 上的 make 是 GNU Make。)


    鉴于您的编辑,这里有一个示例,其中包含您记下的文件。需要注意的是,以 $(CXX) 开头的行应该以 TAB 字符开头!

    LDFLAGS  := -lboost_filesystem
    CXXFLAGS := -O3 -Wall
    CPPFLAGS :=
    
    all: program
    program: simple_ls.o converter.o rawr.o
        $(CXX) -o $< $^ $(LDFLAGS)
    
    simple_ls.o: simple_ls.cpp rawr.h simple_ls.h 2dquicksort.h
    converter.o: converter.cpp rawr.h simple_ls.h 2dquicksort.h
    rawr.o: rawr.cpp rawr.h simple_ls.h 2dquicksort.h
    

    【讨论】:

    • 这很好地解释了事物是什么,而不是如何实现它
    【解决方案3】:

    我不建议你自己写Makefiles。相反,请使用 CMakeSCons 之类的构建系统。

    值得注意的是,这些工具是跨平台的。因此,您可以在 Linux 和 Windows 上使用相同的构建系统。

    【讨论】:

      【解决方案4】:

      当然,您应该阅读 The Fine Manual(特别是 gcc 和 make)。但是,这里是 gcc 的基础知识:

      编译源文件:

      g++ -c file.cpp
      

      这将创建 file.o。然后你可能想要链接它们:

      g++ -o app_name file.o main.o other_file.o
      

      这将创建一个名为 app_name 的可执行文件。但是由于您使用的是 boost 并且可能到处都有头文件,因此您可能需要其他选项。编译时使用 -I 将目录添加到包含路径:

      g++ -I/usr/local/more_includes/ -c file.cpp
      

      您可能还需要链接到一些库。在链接期间:

      g++ -L/usr/local/more_libraries/ file.o main.o other_file.o -lsome_library
      

      现在进入makefile。 makefile 的基础是:

      target: dependencies
          command
      

      例如:

      my_app: file.o
          g++ -o my_app file.o
      
      file.o: file.cpp file.h
          g++ -o file.cpp
      
      clean:
          rm file.o my_app
      

      如果您键入“make”,则默认尝试创建第一个目标。 my_app 依赖于目标 file.o,因此它将检查自上次修改 my_app 以来 file.o 是否已被修改。如果是这样,它将重新链接。在检查 file.o 时,它注意到 file.o 依赖于 file.cpp 和 file.h。如果自上次创建 file.o 以来这些文件中的任何一个已被修改,它将重新编译该文件。

      目标也不一定必须是实际文件。最后一个叫做clean,它不依赖任何东西,只是删除file.o和my_app。如果您键入“make clean”,它将运行该命令。

      当然还有很多其他选择,但这应该可以帮助您入门。

      【讨论】:

      • 值得注意的是,“命令行”必须以分隔符开头,并且必须是制表符(所有版本的“make”?不确定)。导致很多头痛“Makefile:4:***缺少分隔符。停止。”是最常见的。如果您正在处理网页中的示例,则很容易省略。
      • 好电话!我什至没有想到这一点,因为我在编辑器中使用制表符进行缩进,这对我来说都是自动的。
      【解决方案5】:

      至少尝试通读您尝试使用的产品的官方文档:here。它确实解释了几乎所有的基础知识。

      特别是,阅读第 2 章和第 3 章,它们将使您获得 99% 的方法来有效地使用 gmake。另外,请仔细阅读Catalogue of Implicit Rules。这将告诉您大多数“特殊变量”是什么。

      我给你的一个提示是在你的项目目录中尝试gcc -M *.cpp。这将以 Makefile 格式输出每个 .cpp 文件的先决条件标头列表。事实上,对于一个初学者的 makefile,你可以这样做:

      gcc -M *.cpp > Makefile
      

      编辑这个文件,或多或少地把 sharth 的答案放在前面,你就有了一个可行的 Makefile。我可能会建议您删除 gcc -M 将添加到每个构建规则中的大量系统标头,但您实际上不必这样做。

      FWIW,如果您开始处理一个大型项目(多个源目录是一个很好的线索),那么是时候开发一个现代构建管理工具了(这里是 cmake 粉丝)。但对于小型项目,raw make 非常容易使用。

      【讨论】:

      • gcc -MM 将生成的构建规则中的标头列表限制为仅非系统标头。
      【解决方案6】:

      这不是您所要求的,但我强烈建议您使用名为 premake 的构建系统。 premake 和 scons 以及 cmake 之间的好处和区别在于它为您生成 makefile。这意味着您可以使用 premake 作为开始,然后查看它生成的文件以了解更多信息。

      除了这个 premake 之外,它还是一个易于学习和直观的工具,它还有一个额外的好处,那就是能够从相同的配置生成 Visual Studio 项目文件。

      如果您绝对必须拥有 makefile,我的建议是使用 premake,它将帮助您学习,并且最终可以灵活地替代直接编写自己的 makefile(这是一个主要的痛苦是 %*$)。

      【讨论】:

      • CMake 还会生成 Makefiles、VisualStudio/Eclipse/Xcode 项目文件。
      【解决方案7】:

      有一个很好的教程@ALP

      【讨论】:

        【解决方案8】:

        首先,我不是 Makefile 专家。我知道基础知识,但是这个答案中的 Makefile 几乎肯定会有改进的余地。有很多技巧和捷径可以让你更好地处理依赖关系和添加文件,而不是硬编码所有内容。但是,我认为这个示例足以满足您的目的,并且可能更具教育意义。

        其次,我想确保您了解构建项目的基本阶段。你可能已经知道这一点,但如果你不知道,下面的内容一开始会有点混乱。基本上有两个步骤:编译和链接。编译会将您的代码转换为目标代码 - 如果成功,它会将 .cpp 文件转换为 .o 文件。下一步是链接。这是它将所有目标代码粘贴在一起以创建可执行文件并链接从一个文件到另一个文件的函数调用的地方(因此,如果 file1.cpp 调用 file2.cpp 中定义的函数,则在此步骤file1.cpp 找出函数的实际位置)。还有更多内容,但这足以说明以下内容。

        此时您可以使用g++ 来编译和链接您的项目(您甚至可以在“一个”步骤中完成)。然而,这非常麻烦,特别是对于任何不平凡的项目。这也使得跟踪自上次编译以来发生更改的文件变得很困难。

        这就是 Makefiles 的用武之地。Makefile 是以下形式的规则列表:

        target: dependencies
            command
        

        (确保使用制表符而不是空格,如果使用空格,make 可能无法正常工作)。如果你运行命令:

        make some_target
        

        然后 make 将查找带有some_target 的规则。如果目标是一个文件,它将检查文件的“最后修改”时间戳,并检查您列出的所有依赖项的时间戳。如果任何依赖项具有稍后的时间戳,它将运行该命令。

        我将不得不对您的项目做出一些假设(即,哪些文件取决于哪些文件),因此您可能需要修改以下内容,但这里是您项目的基本 Makefile(请记住,选项卡不是空格。如果您复制并粘贴它,它将不起作用):

        CC = g++
        INCLUDE_DIRS = -I/path/to/boost
        
        all: binary_file
        
        clean:
            rm *.o
            rm binary_file
        
        binary_file: simple_ls.o rawr.o converter.o
            $(CC) -o binary_file simple_ls.o rawr.o converter.o
        
        rawr.o: rawr.h rawr.cpp 2dquicksort.h
            $(CC) -c rawr.cpp $(INCLUDE_DIRS)
        
        simple_ls.o: simple_ls.h simple_ls.cpp 2dquicksort.h
            $(CC) -c simple_ls.cpp $(INC*emphasized text*LUDE_DIRS)
        

        运行 makemake allmake binary_file 将在必要时编译所有文件,然后链接以创建名为 binary_file 的可执行文件。您可以进行一些改进,例如:

        %.o: %.cpp %.h 2dquicksort.h
            $(CC) -c $<
        

        它将找到所有的 .cpp 文件并将它们编译成 .o 文件。 .o 文件将依赖于同名的 .cpp 文件和 .h 文件(以及 2dquicksort.h)。

        【讨论】:

          【解决方案9】:

          使用@Job 的想法,我建议你把它留给一些IDE 去做。例如,在 Eclipse CDT 中构建一个项目并使用它自动生成的 make 文件。然后,您可以学习它,然后根据您的需要进行更改。

          【讨论】:

          • 那不行,因为我的 Windows 机器上只有 Eclipse
          【解决方案10】:

          我更喜欢标准的项目描述,例如this one。换句话说,我认为对 makefile 的关注太窄了。

          这个项目描述的优点是简单。它可以适应更复杂的项目,在all 目标中使用make -C subdir -f Makefile all 的子目录。 subdir 当然是您的子目录,其中包含应构建的代码。

          作者指出,创建和维护构建环境并不高效,因此您需要易于修改且易于理解的东西。我不一定会成为 makefile 专家。我认为这没有很多人想的那么有用。

          我非常喜欢 hiltmon 的想法,我将它变成了一个 shell 脚本。我可能会用 Python 编写完成的项目,并询问作者是否可以为 GitHub 做贡献。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-11-10
            • 1970-01-01
            • 2019-01-04
            • 1970-01-01
            • 2014-10-13
            • 2012-06-21
            相关资源
            最近更新 更多