我想知道是否有关于 Makefile
和 CMakeLists.txt
的示例代码,两者都能实现同样的功能(唯一不同的是一个是用make
编写,另一个是用cmake
编写)。
我尝试搜索'cmake vs make',但我没有找到任何代码的对比。即使只是一个简单的情况,理解它们之间的区别也会很有帮助。
prog
的可执行文件,这些源文件是prog1.c,prog2.c,prog3.c和main.c
。 prog
链接到libmystatlib.a
和libmydynlib.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")
$(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 根据系统配置做出合理的默认选择。其次,在所选的构建系统中执行实际的构建操作。CPPFLAGS
而不是INCDIR
,可以利用内置规则,显式调用编译器将是冗余的。同样地,对于ar的处理,内置规则也可以涵盖它。此外,为什么要明确设置CPP
和CC
?它们已经被make
设置为良好的值,它们是预定义变量。make
还能识别用于哪种源的编译器,内置规则很多。 - Christian Hujer:=
而不是 =
进行赋值。 - Christian Hujercmake
更适合与 automake
相比较,而不是 make
。 - ivan_pozdeevINCLUDES
而不是 INCDIR
。你不需要 %.o:%.c 规则。 - KRoy选择一些使用CMake作为构建系统的软件(有很多开源项目可以选择作为示例)。获取源代码并使用CMake进行配置。阅读生成的makefile并享受其中。
需要记住的一件事是这些工具不是一一对应的。最显而易见的区别是,CMake会扫描不同文件(例如C头文件和源文件)之间的依赖,而make则由makefile作者处理。
CMakeList.txt
文件的样例Makefile
输出,请查看cmake-backend源代码并生成一个这样的Makefile
。如果不是,那么在@Roberto的回复中添加,我正在尝试通过隐藏细节使其变得简单。
虽然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
定义的函数调用列表。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
提供了详细的recipe
,显示了如何进行构建。可以编写executable.mk
以将详细信息定义在一个文件中。通过这种方式,可以像我之前展示的那样减少makefile的大小。
CMake
和Make
中的内部变量CMake
中,我们可以设置编译器标志,例如:set(CMAKE_C_FLAGS "-Wall")
CMakeCache.txt
文件中的CMake
默认变量。以上CMake
代码等同于下面的Make
代码。CFLAGS = -Wall
CFLAGS
是Make
内部变量,同样地,CMAKE_C_FLAGS
是CMake
的内部变量。
cmake
中实现这一点。target_include_directories(testapp PRIVATE "myincludes")
list(APPEND testapp_LIBRARIES
mytest mylibrarypath
)
target_link_libraries(testapp ${testapp_LIBRARIES})
我们可以通过添加以下类似的行来添加头文件和库文件:
INCLUDES += -Imyincludes
LIBS += -Lmylibrarypath -lmytest
请注意,上面的这些行可以通过自动生成工具或pkg-config生成。(虽然Makefile不依赖于自动配置工具)
通常情况下,可以通过使用configure_file
函数生成一些类似于auto-config
工具的config.h
文件。也可以编写自定义函数来进行更多的操作。最后,我们可以像下面这样选择一个配置:
cmake --build . --config "Release"
使用 option
函数可以添加一些可配置的选项。
如果我们需要使用一些调试标志编译它,我们可以像下面这样调用 make
:
make CXXFLAGS=NDEBUG
我认为内部变量、Makefile-rules
和CMake-functions
是比较的好起点,祝你在更深入的挖掘中好运。
cmake
的时候,我也很想知道这个答案。但我怀疑你可能找不到,因为它们之间的能力映射不那么契合。如果你试图让cmake
像make
一样工作,你会把自己逼疯的,说真的。最好还是从头开始。在make
中简单的事情在cmake
中可能会很复杂,反之亦然。 - Ernest Friedman-Hillmake
和cmake
是如此不同,以至于它们应该被视为互补而不是竞争工具? - Ehtesh Choudhurycp *.x $(OUTDIR)
”相比较相当复杂。 对我来说,最让人困扰的部分可能是生成的Makefile在设计上完全不可移植且缺乏灵活性。(续) - Ernest Friedman-Hill