我们被要求使用Makefile把我们项目的所有东西整合在一起,但我们的教授从来没有教过我们怎么做。

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

就是这样。其他所有内容都包含在.cpp中。

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


当前回答

我建议(注意缩进是一个制表符):

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

or

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

后一个建议稍微好一点,因为它重用了GNU Make隐式规则。然而,为了工作,源文件必须与最终的可执行文件具有相同的名称(即:tool.c和tool)。

注意,没有必要声明源。中间目标文件使用隐式规则生成。因此,这个Makefile适用于C和c++(也适用于Fortran等等)。

还要注意,默认情况下,Makefile使用$(CC)作为链接器。$(CC)对于链接c++对象文件不起作用。我们修改LINK。只是因为这样。如果你想编译C代码,你不需要强制LINK。o值。

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

CFLAGS = -Wall
LDLIBS = -lm

另一方面:如果你必须使用外部库,我建议使用pkg-config来正确设置CFLAGS和LDLIBS:

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

细心的读者会注意到,如果更改了一个头文件,这个Makefile就不能正确地重新构建。添加这些行来解决问题:

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

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

当然,一个编写良好的Makefile也应该包括clean和distclean规则:

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

distclean: clean
    $(RM) tool

注意,$(RM)相当于RM -f,但最好不要直接调用RM。

all规则也很受欢迎。为了工作,这应该是你的文件的第一条规则:

all: tool

你也可以添加一个安装规则:

PREFIX = /usr/local
install:
    install -m 755 tool $(DESTDIR)$(PREFIX)/bin

DESTDIR默认为空。用户可以将其设置为在替代系统上安装程序(对于交叉编译过程是必需的)。为了在/usr中安装您的包,多个分发的包维护人员也可以更改PREFIX。

最后一句话:不要将源文件放在子目录中。如果您真的想这样做,请将这个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

其他回答

我用了弗雷德穆德的答案。我研究了一段时间,这似乎是一个开始的好方法。这个解决方案还有一个定义良好的添加编译器标志的方法。我又回答了一遍,因为我做了一些改变,让它在我的环境中工作,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++库中没有链接有关的错误。这个配置解决了这个问题。

我建议(注意缩进是一个制表符):

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

or

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

后一个建议稍微好一点,因为它重用了GNU Make隐式规则。然而,为了工作,源文件必须与最终的可执行文件具有相同的名称(即:tool.c和tool)。

注意,没有必要声明源。中间目标文件使用隐式规则生成。因此,这个Makefile适用于C和c++(也适用于Fortran等等)。

还要注意,默认情况下,Makefile使用$(CC)作为链接器。$(CC)对于链接c++对象文件不起作用。我们修改LINK。只是因为这样。如果你想编译C代码,你不需要强制LINK。o值。

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

CFLAGS = -Wall
LDLIBS = -lm

另一方面:如果你必须使用外部库,我建议使用pkg-config来正确设置CFLAGS和LDLIBS:

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

细心的读者会注意到,如果更改了一个头文件,这个Makefile就不能正确地重新构建。添加这些行来解决问题:

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

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

当然,一个编写良好的Makefile也应该包括clean和distclean规则:

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

distclean: clean
    $(RM) tool

注意,$(RM)相当于RM -f,但最好不要直接调用RM。

all规则也很受欢迎。为了工作,这应该是你的文件的第一条规则:

all: tool

你也可以添加一个安装规则:

PREFIX = /usr/local
install:
    install -m 755 tool $(DESTDIR)$(PREFIX)/bin

DESTDIR默认为空。用户可以将其设置为在替代系统上安装程序(对于交叉编译过程是必需的)。为了在/usr中安装您的包,多个分发的包维护人员也可以更改PREFIX。

最后一句话:不要将源文件放在子目录中。如果您真的想这样做,请将这个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

你有两个选择。

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

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

nmake a3a.exe

就是这样。如果你正在使用GNU Make,请使用" Make "或"gmake"或其他什么。

选项2:2行makefile。

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

我一直认为通过一个详细的示例更容易学习,因此下面是我对makefile的看法。对于每个section,你都有一行不缩进的行,它显示了section的名称和依赖项。依赖关系可以是其他节(将在当前节之前运行)或文件(如果更新将导致下次运行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

我之所以展示第一个示例,是因为它展示了makefiles的强大功能。如果需要编译另一个文件,只需添加另一个部分。下面是一个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。

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