Cmake与make的示例代码有何区别?

131

我想知道是否有关于 MakefileCMakeLists.txt 的示例代码,两者都能实现同样的功能(唯一不同的是一个是用make编写,另一个是用cmake编写)。

我尝试搜索'cmake vs make',但我没有找到任何代码的对比。即使只是一个简单的情况,理解它们之间的区别也会很有帮助。


21
这是个好问题;当我开始学习cmake的时候,我也很想知道这个答案。但我怀疑你可能找不到,因为它们之间的能力映射不那么契合。如果你试图让cmakemake一样工作,你会把自己逼疯的,说真的。最好还是从头开始。在make中简单的事情在cmake中可能会很复杂,反之亦然。 - Ernest Friedman-Hill
1
@ErnestFriedman-Hill,你有关于这个的更多细节吗?所以makecmake是如此不同,以至于它们应该被视为互补而不是竞争工具? - Ehtesh Choudhury
3
@Shurane -- cmake本身并不构建任何内容;它创建Makefile(和其他类似的构建脚本),然后您运行这些脚本。因此,每当您编写cmake文件时,都必须考虑命令在生成时间还是在构建时间应用。一些操作,在构建时复制通配符集合的文件,例如,与您在Makefile中编写的“cp *.x $(OUTDIR)”相比较相当复杂。 对我来说,最让人困扰的部分可能是生成的Makefile在设计上完全不可移植且缺乏灵活性。(续) - Ernest Friedman-Hill
1
你甚至不能在同一台机器上移动源目录而不重新运行cmake以重建Makefile!因此,选择不是在cmake和make之间,而是在自己编写可移植的Makefile或者在每个构建机上使用cmake生成不可移植的Makefile之间做出(考虑到可以在Windows上使用Cygwin或mingw,我通常发现前者更容易)。 - Ernest Friedman-Hill
1
这是一个不错的问题,但是没有具体的答案。因为两个工具都试图解决不同的问题。CMake获取关于如何构建程序的信息,生成构建程序的Makefile文件。因此,CMake是一种具有抽象构建规则的语言,而GNU Make则是依赖关系解析器,它在有向无环图遍历时执行程序。 - Alexander Oh
3个回答

129
下面的Makefile从源代码中构建了一个名为prog的可执行文件,这些源文件是prog1.c,prog2.c,prog3.c和main.cprog链接到libmystatlib.alibmydynlib.so两个库,这两个库也是从源代码中构建的。此外,prog使用位于stuff/lib中的库libstuff.a及其头文件stuff/include。默认情况下,Makefile构建发布目标,但还提供调试目标:
#Makefile    
CC = gcc
CPP = g++
RANLIB = ar rcs
RELEASE = -c -O3 
DEBUG = -c -g -D_DEBUG
INCDIR = -I./stuff/include
LIBDIR = -L./stuff/lib -L.
LIBS = -lstuff -lmystatlib -lmydynlib
CFLAGS = $(RELEASE)

PROGOBJS = prog1.o prog2.o prog3.o

prog: main.o $(PROGOBJS) mystatlib mydynlib
    $(CC) main.o $(PROGOBJS) $(LIBDIR) $(LIBS) -o prog 
debug: CFLAGS=$(DEBUG)
debug: prog

mystatlib: mystatlib.o
    $(RANLIB) libmystatlib.a mystatlib.o
mydynlib: mydynlib.o
    $(CPP) -shared mydynlib.o -o libmydynlib.so

%.o: %.c
    $(CC) $(CFLAGS) $(INCDIR) $< -o $@ 
%.o: %.cpp
    $(CPP) $(CFLAGS) $(INCDIR) -fPIC  $< -o $@ 

这是一个 CMakeLists.txt 文件,几乎与 Makefile 相同,以下是一些注释以强调它们的相似之处:

#CMakeLists.txt     
cmake_minimum_required(VERSION 2.8)                    # stuff not directly
project(example)                                       # related to building

include_directories(${CMAKE_SOURCE_DIR}/stuff/include) # -I flags for compiler
link_directories(${CMAKE_SOURCE_DIR}/stuff/lib)        # -L flags for linker

set(PROGSRC prog1.c prog2.c prog3.c)                   # define variable 

add_executable(prog main.c ${PROGSRC})                 # define executable target prog, specify sources
target_link_libraries(prog mystatlib mydynlib stuff)   # -l flags for linking prog target

add_library(mystatlib STATIC mystatlib.c)              # define static library target mystatlib, specify sources

add_library(mydynlib SHARED mydynlib.cpp)              # define shared library target mydynlib, specify sources
#extra flags for linking mydynlib
set_target_properties(mydynlib PROPERTIES POSITION_INDEPENDENT_CODE TRUE) 
#alternatively:
#set_target_properties(mydynlib PROPERTIES COMPILE_FLAGS "-fPIC")

在这个简单的例子中,最重要的区别如下:
- CMake 可以识别应该使用哪些编译器来处理哪种类型的源代码。此外,针对每种类型的目标,它会调用正确的命令序列。因此,不需要明确指定像 $(CC) ...$(RANLIB) ... 这样的命令。 - 所有通常与包含头文件、库等相关的编译器/链接器标志都被取代为平台无关/构建系统无关的命令。 - 调试标志可以通过将变量 CMAKE_BUILD_TYPE 设置为 "Debug" 或在调用 CMake 时传递它来包含。例如:cmake -DCMAKE_BUILD_TYPE:STRING=Debug。 - CMake 还提供了平台无关的包含 "-fPIC" 标志(通过 POSITION_INDEPENDENT_CODE 属性)和许多其他标志。当然,更深奥的设置在 CMake 中可以像在 Makefile 中一样手动实现(使用 COMPILE_FLAGS 和类似的属性)。当第三方库(如 OpenGL)以可移植的方式被包含时,CMake 真正开始发挥作用。 - 如果你使用 Makefile,则构建过程只需一步,即在命令行中键入 make。对于 CMake,则需要两步:首先,你需要设置你的构建环境(在构建目录中键入 cmake <source_dir> 或运行某个 GUI 客户端)。这将根据你选择的构建系统(例如 Unix 上的 make 或 Windows 上的 VC++ 或 MinGW + Msys)创建一个 Makefile 或等效物。构建系统可以作为参数传递给 CMake;但是,CMake 根据系统配置做出合理的默认选择。其次,在所选的构建系统中执行实际的构建操作。
源代码和构建说明可在 https://github.com/rhoelzel/make_cmake 上找到。

3
Makefile是否过于复杂?使用CPPFLAGS而不是INCDIR,可以利用内置规则,显式调用编译器将是冗余的。同样地,对于ar的处理,内置规则也可以涵盖它。此外,为什么要明确设置CPPCC?它们已经被make设置为良好的值,它们是预定义变量。make还能识别用于哪种源的编译器,内置规则很多。 - Christian Hujer
1
许多这些变量应该使用 := 而不是 = 进行赋值。 - Christian Hujer
1
从描述来看,cmake 更适合与 automake 相比较,而不是 make - ivan_pozdeev
提供的 Makefile 可以缩减至 3/4 行。你应该指定 INCLUDES 而不是 INCDIR。你不需要 %.o:%.c 规则。 - KRoy

7

选择一些使用CMake作为构建系统的软件(有很多开源项目可以选择作为示例)。获取源代码并使用CMake进行配置。阅读生成的makefile并享受其中。

需要记住的一件事是这些工具不是一一对应的。最显而易见的区别是,CMake会扫描不同文件(例如C头文件和源文件)之间的依赖,而make则由makefile作者处理。


4
如果这个问题是关于CMakeList.txt文件的样例Makefile输出,请查看cmake-backend源代码并生成一个这样的Makefile。如果不是,那么在@Roberto的回复中添加,我正在尝试通过隐藏细节使其变得简单。

CMake函数

虽然Make是灵活的规则和配方工具,但CMake是一层抽象,还添加了配置功能。

我的普通CMakeLists.txt将如下所示,

cmake_minimum_required(VERSION 2.8)
project(example)
file(GLOB testapp_SOURCES *.cc)
add_executable(testapp ${testapp_SOURCES})

请注意,CMake隐藏了构建的方法,我们只指定了输入和输出。
CMakeLists.txt包含由cmake定义的函数调用列表。

(CMake函数)与Make规则

Makefile中,使用规则和配方代替函数。除了函数类似的特性外,规则和配方还提供了链接功能。我的最简Makefile将如下所示:
-include "executable.mk"
TARGETS=testapp.bin
all:${TARGETS}

可执行文件executable.mk的内容如下所示:

SOURCES=$(wildcard *.cpp)
OBJECTS=$(SOURCES:.cpp=.o)
DEPS=$(SOURCES:.cpp=.d)

%.bin:$(OBJECTS)
    $(CC) $(CFLAGS) -o $@ $^ $(LFLAGS) $(LIBS)

.PHONY: all clean

clean:
    $(RM) $(OBJECTS) $(DEPS) $(TARGETS)

-include $(DEPS)

从头开始,我将使用如下的Makefile

all: testapp.bin

testapp.bin:sourcea.o sourcb.o
    $(CC) $(CFLAGS) -o $@ $^ $(LFLAGS) $(LIBS)

.PHONY: all clean

clean:
    $(RM) $(OBJECTS) testapp.bin

我从这里获取了这个代码片段并进行了修改。请注意,此文件中添加了一些隐式规则,可以在makefile文档中找到。一些隐式变量在这里也是相关的。
请注意,Makefile提供了详细的recipe,显示了如何进行构建。可以编写executable.mk以将详细信息定义在一个文件中。通过这种方式,可以像我之前展示的那样减少makefile的大小。

CMakeMake中的内部变量

现在进入稍微高级的内容,在CMake中,我们可以设置编译器标志,例如:
set(CMAKE_C_FLAGS "-Wall")

请查看CMakeCache.txt文件中的CMake默认变量。以上CMake代码等同于下面的Make代码。
CFLAGS = -Wall

请注意,CFLAGSMake内部变量,同样地,CMAKE_C_FLAGSCMake的内部变量。

在CMake中添加包含和库路径

我们可以使用函数在cmake中实现这一点。
target_include_directories(testapp PRIVATE "myincludes")
list(APPEND testapp_LIBRARIES
    mytest mylibrarypath
)
target_link_libraries(testapp ${testapp_LIBRARIES})

如何在Makefile中添加头文件和库文件路径

我们可以通过添加以下类似的行来添加头文件和库文件:

INCLUDES += -Imyincludes
LIBS += -Lmylibrarypath -lmytest

请注意,上面的这些行可以通过自动生成工具或pkg-config生成。(虽然Makefile不依赖于自动配置工具)

CMake配置/微调

通常情况下,可以通过使用configure_file函数生成一些类似于auto-config工具的config.h文件。也可以编写自定义函数来进行更多的操作。最后,我们可以像下面这样选择一个配置:

cmake --build . --config "Release"

使用 option 函数可以添加一些可配置的选项。

Makefile 配置/调整

如果我们需要使用一些调试标志编译它,我们可以像下面这样调用 make

make CXXFLAGS=NDEBUG

我认为内部变量、Makefile-rulesCMake-functions是比较的好起点,祝你在更深入的挖掘中好运。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接