我正在将一个C ++项目从Windows迁移到Linux,现在我需要创建一个build / make文件。我以前从未创建过build / make文件。我还需要包含Boost库以使其更复杂。 它也必须是一个makefile,我还需要学习如何创建makefile,所以CMake和SCON都出来了。由于使用了Boost,IDE也出局了,我的所有IDE(Eclipse,VS等)都只在Windows上运行。我必须从头开始生成一个makefile。
那么创建Linux c ++ make文件的基础是什么?如何将Boost库合并到其中以使其正确链接?
到目前为止,我的makefile看起来像这样。我认为CFLAGS
和LDFLAGS
是编译器和优化选项,但并不完全确定。
CC = g++
CFLAGS = -wall -o3 - c
LDFLAGS = -03 -mfp-rounding-mode=n
我提供赏金,因为我仍然非常迷失。如果有人喜欢冒险,我需要在linux中编译以下内容
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构建它们。
答案 0 :(得分:19)
一个非常简单的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.o
,bar.o
,john.o
。*.o
的规则,因为gnu make对这些规则有隐含的规则。要使用make,您将使用上面列出的内容创建名为Makefile
的文件。要构建,您可以在该目录中运行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
答案 1 :(得分:19)
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及其包含文件
已被理解(存储在make
内部数据库中)。
最后,使用Boost将理论应用于此特定示例并创建虚假源文件 说明。
#include "rawr.h"
#include "rawr.h"
#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 :(得分:8)
答案 3 :(得分:5)
当然你应该阅读精细手册(特别是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',它将运行命令。
当然还有很多其他选择,但这应该让你开始。
答案 4 :(得分:4)
至少尝试阅读您尝试使用的产品的官方文档:here。它确实解释了几乎所有的基础知识。
特别是,阅读第2章和第3章,这些将使您获得有效使用gmake所需的99%的路径。另外,请仔细阅读Catalogue of Implicit Rules。这将告诉你大多数“特殊变量”是什么。
我给你的一个提示就是在项目目录中试用gcc -M *.cpp
。这将以Makefile格式输出每个.cpp文件的先决条件标头列表。实际上,对于启动器makefile,您可以这样做:
gcc -M *.cpp > Makefile
编辑此文件,或多或少地预先添加sharth的答案,并且您有一个可行的Makefile。我可能会建议你删除gcc -M要添加到每个构建规则的大量系统头文件,但你真的不需要。
FWIW,如果你开始处理一个大型项目(多个源目录是一个很好的线索),是时候打破一个现代的构建管理工具(这里是cmake粉丝)。但对于小型项目,原始制作很容易使用。
答案 5 :(得分:2)
这并不是您要求的,但我强烈建议您使用名为premake的构建系统。 premake和scons以及cmake之间的好处和区别在于它为您生成makefile。这意味着您可以使用premake作为开始,然后查看它生成的文件以了解更多信息。
除了这个预制之外,它还是一个易于学习和直观的工具,它具有能够从相同配置生成可视工作室项目文件的额外好处。
我的建议是使用预制,如果你必须有makefile,它将帮助你学习,并最终是直接编写自己的makefile的灵活替代品(这是一个主要的痛苦是%* $)。
答案 6 :(得分:2)
有一个非常好的教程@ ALP。
答案 7 :(得分:2)
首先,我不是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)
运行make
或make all
或make binary_file
将导致所有文件在必要时进行编译,然后进行链接以创建名为binary_file
的可执行文件。您可以进行一些改进,例如:
%.o: %.cpp %.h 2dquicksort.h
$(CC) -c $<
将找到所有.cpp文件并将其编译为.o文件。 .o文件将依赖于.cpp文件和同名的.h文件(和2dquicksort.h)。
答案 8 :(得分:0)
使用@Job的想法,我建议你留下一些IDE来做。例如,在Eclipse CDT中构建项目并使用它自动生成的make文件。然后,您可以对其进行监控,并在时间进行更改以满足您的需求。
答案 9 :(得分:0)
我更喜欢标准的项目描述,例如this one。换句话说,我认为对makefile的关注范围太窄。
此项目描述的优点是简单。可以使用make -C subdir -f Makefile all
目标中的all
将其适应具有子目录的更复杂的项目。 subdir
当然是带有应构建代码的子目录。
作者指出,创建和维护构建环境没有生产力,因此您想要易于修改且易于理解的内容。我不一定会成为makefile专家。我认为这没有很多人想象的有用。
我非常喜欢hiltmon的想法,因此将其变成了shell脚本。我可以用Python编写完成的项目,然后问作者是否可以为GitHub做出贡献。