如何制作SIMPLE C ++ Makefile?

时间:2010-03-20 00:02:06

标签: c++ makefile

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

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

就是这样,其他一切都包含在.cpp

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

7 个答案:

答案 0 :(得分:492)

答案 1 :(得分:50)

我一直认为通过一个详细的例子更容易学习,所以这就是我对makefile的看法。对于每个部分,您有一行不缩进的行,它显示该部分的名称,后跟依赖项。依赖项可以是其他部分(将在当前部分之前运行)或文件(如果更新将导致下次运行make时再次运行当前部分)。

这是一个快速示例(请记住,我使用的是4个空格,我应该使用选项卡,Stack Overflow不会让我使用制表符):

a3driver: a3driver.o
    g++ -o a3driver a3driver.o

a3driver.o: a3driver.cpp
    g++ -c a3driver.cpp

当您键入make时,它将选择第一部分(a3driver)。 a3driver依赖于a3driver.o,所以它将转到该部分。 a3driver.o依赖于a3driver.cpp,所以它只会在a3driver.cpp自上次运行以来发生变化时运行。假设它已经(或从未运行过),它会将a3driver.cpp编译为.o文件,然后返回a3driver并编译最终的可执行文件。

由于只有一个文件,它甚至可以简化为:

a3driver: a3driver.cpp
    g++ -o a3driver a3driver.cpp

我展示第一个例子的原因是它显示了makefile的强大功能。如果需要编译另一个文件,可以添加另一个部分。这是一个带有secondFile.cpp的示例(它在一个名为secondFile.h的头文件中加载):

a3driver: a3driver.o secondFile.o
    g++ -o a3driver a3driver.o secondFile.o

a3driver.o: a3driver.cpp
    g++ -c a3driver.cpp

secondFile.o: secondFile.cpp secondFile.h
    g++ -c secondFile.cpp

这样,如果你在secondFile.cpp或secondFile.h中更改某些东西并重新编译,它只会重新编译secondFile.cpp(而不是a3driver.cpp)。或者,如果你在a3driver.cpp中更改某些内容,它将不会重新编译secondFile.cpp。

如果您对此有任何疑问,请与我们联系。

包括名为“all”的部分和名为“clean”的部分也是传统的。 “all”通常会构建所有可执行文件,而“clean”将删除像.o文件和可执行文件这样的“构建工件”:

all: a3driver ;

clean:
    # -f so this will succeed even if the files don't exist
    rm -f a3driver a3driver.o
编辑:我没注意到你在Windows上。我认为唯一的区别是将-o a3driver更改为-o a3driver.exe

答案 2 :(得分:32)

为什么每个人都喜欢列出源文件?一个简单的find命令可以很容易地解决这个问题。

这是一个简单的C ++ Makefile的例子。只需将其放在包含.C个文件的目录中,然后键入make ...

appname := myapp

CXX := clang++
CXXFLAGS := -std=c++11

srcfiles := $(shell find . -name "*.C")
objects  := $(patsubst %.C, %.o, $(srcfiles))

all: $(appname)

$(appname): $(objects)
    $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $(appname) $(objects) $(LDLIBS)

depend: .depend

.depend: $(srcfiles)
    rm -f ./.depend
    $(CXX) $(CXXFLAGS) -MM $^>>./.depend;

clean:
    rm -f $(objects)

dist-clean: clean
    rm -f *~ .depend

include .depend

答案 3 :(得分:12)

老问题,我知道,但对后代来说。你有两个选择。

选项1:最简单的makefile = NO MAKEFILE。

将“a3driver.cpp”重命名为“a3a.cpp”,然后在命令行上写:

nmake a3a.exe

就是这样。如果您正在使用gnu-make,请使用“make”或“gmake”或其他任何内容。

选项2:2行生成文件。

a3a.exe: a3driver.obj
        link /out:a3a.exe a3driver.obj

瞧。

答案 4 :(得分:6)

您的make文件将具有一个或两个依赖关系规则,具体取决于您是使用单个命令编译和链接,还是使用一个用于编译的命令和一个用于链接的命令。

依赖是一个规则树,如下所示:

main_target : source1 source2 etc
   command to build main_target from sources

source1 : dependents for source1
   command to build source1

在目标命令之后必须是一个空行,并且在命令之前必须是空白行。 makefile中的第一个目标是总体目标,仅当第一个目标依赖于它们时才构建其他目标。

所以你的makefile看起来像这样。

a3a.exe : a3driver.obj 
   link /out:a3a.exe a3driver.obj

a3driver.obj : a3driver.cpp
   cc a3driver.cpp

答案 5 :(得分:5)

我使用了Friedmud的回答。我对此进行了一段时间的研究,这似乎是一个很好的入门方式。此解决方案还有一个定义良好的添加编译器标志的方法。我再次回答,因为我做了更改,使其在我的环境,Ubuntu和g ++中工作。更多工作实例有时是最好的老师。

appname := myapp

CXX := g++
CXXFLAGS := -Wall -g

srcfiles := $(shell find . -maxdepth 1 -name "*.cpp")
objects  := $(patsubst %.cpp, %.o, $(srcfiles))

all: $(appname)

$(appname): $(objects)
    $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $(appname) $(objects) $(LDLIBS)

depend: .depend

.depend: $(srcfiles)
    rm -f ./.depend
    $(CXX) $(CXXFLAGS) -MM $^>>./.depend;

clean:
    rm -f $(objects)

dist-clean: clean
   rm -f *~ .depend

include .depend

makefile似乎非常复杂。我正在使用一个,但它产生了一个与不链接g ++库有关的错误。这种配置解决了这个问题。

答案 6 :(得分:5)

我建议:

tool: tool.o file1.o file2.o
        $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@

LINK.o = $(CXX) $(LDFLAGS) $(TARGET_ARCH)
tool: tool.o file1.o file2.o

后者的建议略胜一筹,因为它重用了GNU make隐式规则。但是,为了工作,源文件必须具有与最终可执行文件相同的名称(即:tool.ctool)。

注意,没有必要申报来源。使用隐式规则生成中间目标文件。因此,这Makefile适用于C和C ++(以及Fortran等...)。

另请注意,默认情况下,Makefile使用$(CC)作为链接器。 $(CC)无法链接C ++对象。我们仅因此而修改LINK.o。如果您想编译C代码,则不必强制LINK.o值。

当然,您还可以使用变量CFLAGS添加编译标记,并在LDLIBS中添加库。例如:

CFLAGS = -Wall
LDLIBS = -lm

一方面注意:如果您必须使用外部库,我建议use pkg-config正确设置CFLAGSLDLIBS

CFLAGS += $(shell pkg-config --cflags libssl)
LDLIBS += $(shell pkg-config --libs libssl)

细心的读者注意,如果更改了一个标头,则Makefile无法正确重建。添加以下行以解决问题:

override CPPFLAGS += -MMD
include $(wildcard *.d)

-MMD允许构建包含有关头依赖关系的Makefile片段的.d文件。第二行只是使用它们。

当然,编写良好的Makefile还应包含cleandistclean规则:

clean:
        $(RM) *.o *.d

distclean: clean
        $(RM) tool

请注意,$(RM)相当于rm -f,但最好不要直接致电rm

all规则也表示赞赏。为了工作,它应该是您文件的第一条规则:

all: tool

您还可以添加install规则:

PREFIX = /usr/local
install:
        install -m 755 tool $(DESTDIR)$(PREFIX)/bin
默认情况下,

DESTDIR为空。用户可以将其设置为将程序安装到备用系统(交叉编译过程必需)。用于多次分发的软件包维护者也可能会更改PREFIX,以便在/usr中安装您的软件包。

最后一句话,不要将源文件放在子目录中。如果您真的想这样做,请将此Makefile保留在根目录中,并使用完整路径来识别您的文件(即subdir/file.o)。

总而言之,您的完整Makefile应如下所示:

LINK.o = $(CXX) $(LDFLAGS) $(TARGET_ARCH)
PREFIX = /usr/local
override CPPFLAGS += -MMD
include $(wildcard *.d)

all: tool
tool: tool.o file1.o file2.o
clean:
        $(RM) *.o *.d
distclean: clean
        $(RM) tool
install:
        install -m 755 tool $(DESTDIR)$(PREFIX)/bin