基本示例
如果项目是使用传统的Makefile构建的,并且您希望使用CMake调用这些Makefile,您可以使用CMake的add_custom_target
和add_custom_command
命令来实现。
首先,创建一个CMakeLists.txt文件。然后,在其中使用add_custom_target
和add_custom_command
来调用您的原始Makefile。
现在用一共示例来说明,假如有以下的项目结构
project/ ├── build/ ├── CMakeLists.txt ├── Makefile └── src/ ├── Makefile └── main.cc
这是一个简单的项目,其中src
目录包含一个main.cc
文件。接下来,我们需要编写两个Makefile。
- 在项目的根目录下创建一个Makefile:
在项目的根目录下的Makefile中,您可以使用$(MAKE)
命令来调用src
目录下的Makefile。:
# project/Makefile .PHONY: all all: call_src_makefile @echo %date% %time% - Running target 'all' call_src_makefile: $(MAKE) -C src clean: $(MAKE) -C src clean
- 这个Makefile现在定义了两个目标:
all
和clean
。all
目标依赖于build_src_makefile
目标,当您在顶层目录中运行make
或make all
时,它会调用src
目录下的Makefile来构建项目。同样,clean
目标会调用src
目录下的Makefile中的clean
目标来清理构建产物。 - 在
src
目录下创建另一个Makefile:
# project/src/Makefile CC = g++ CFLAGS = -Wall -std=c++11 TARGET = main SOURCES = main.cc all: $(TARGET) $(TARGET): $(SOURCES) $(CC) $(CFLAGS) $(SOURCES) -o $(TARGET) clean: rm -f $(TARGET)
- 此Makefile负责编译
main.cc
并生成一个名为main
的可执行文件。 - 创建一个简单的
main.cc
文件:
// project/src/main.cc #include <iostream> int main() { std::cout << "Hello, world!" << std::endl; return 0; }
- 确保项目根目录下的CMakeLists.txt文件包含以下内容:
cmake_minimum_required(VERSION 3.10) project(MyProject) add_custom_target(build_top_level_makefile COMMAND make -C ${CMAKE_CURRENT_SOURCE_DIR} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) add_custom_target(build_src_makefile COMMAND make -C ${CMAKE_CURRENT_SOURCE_DIR}/src WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src)
- 切换到
build
目录,并运行cmake
:
cd build cmake ..
- 这将在
build
目录下生成CMake的Makefile。 - 在
build
目录下运行make
:
make build_top_level_makefile
- 或者直接运行:
make build_src_makefile
- 这将使用
src
目录下的Makefile构建项目。 - 运行生成的可执行文件:
../src/main
说明:如何传递给具体的makefile文件
make -f 命令
make
是一个用于自动构建可执行程序和库的工具,它使用名为 Makefile
的文件,其中包含了一些规则和依赖来决定如何构建目标。
-f
是 make
的一个命令行选项,它允许你指定一个不同于默认的 Makefile
文件。默认情况下,make
命令会在当前目录下寻找一个名为 Makefile
的文件。如果你想使用一个不同的文件,比如 Makefile1
,你可以使用 -f
选项指定它,如 make -f Makefile1
。
当你运行 make -f Makefile1
时,make
会读取 Makefile1
文件,并查找第一个目标(或者默认目标)。默认目标通常是文件的第一个目标,但也可以通过 .DEFAULT
特殊目标来显式设置。make
会尝试构建这个目标,并在需要时,根据 Makefile1
中定义的规则和依赖,构建其他目标。
如果你想构建 Makefile1
中定义的特定目标,你可以在 make -f Makefile1
命令后面添加目标名称,如 make -f Makefile1 targetname
。在这种情况下,make
会尝试只构建 targetname
,以及任何 targetname
依赖的目标。
总的来说,make -f
命令使你能够使用不同的 Makefile
文件,或者在同一个 Makefile
中构建不同的目标。
make include指令
在 Makefile
中,include
指令用于包含其他的 Makefile
文件。这个指令允许你将一个大的 Makefile
分割成多个小的,易于管理和重用的文件。
当 Makefile
文件中出现 include
指令时,make
将会读取并处理指定的文件,就像这些文件的内容直接插入到 include
指令的位置一样。这意味着被包含文件中的所有规则、目标、变量定义等都将被添加到主 Makefile
中。
被包含的 Makefile
文件中的规则和目标是独立的,它们不会因为被包含而改变。如果一个目标在多个被包含的 Makefile
中都有定义,或者在主 Makefile
和一个或多个被包含的 Makefile
中都有定义,那么 make
将会使用最后一个定义的规则。在处理 Makefile
时,make
会依次读取文件,所以最后读取的文件中的定义将覆盖之前的定义。
例如,如果你有以下的 Makefile
:
include Makefile1 include Makefile2
而 Makefile1
和 Makefile2
中都定义了一个名为 target
的目标,那么 make target
将会使用 Makefile2
中的定义,因为 Makefile2
是最后被读取的。
然而,通常来说,为了避免混乱和不确定性,最好在 Makefile
文件中避免目标和规则的名字重复。如果你需要在多个 Makefile
中使用相同的目标名,可能需要重新考虑你的构建系统设计。
传递
make -C ${CMAKE_CURRENT_SOURCE_DIR}
命令会在${CMAKE_CURRENT_SOURCE_DIR}
目录下执行make
,并寻找名为Makefile
的文件来执行。在一个目录下通常只应有一个Makefile
。
如果${CMAKE_CURRENT_SOURCE_DIR}
目录下有多个Makefile
,那么通常需要使用不同的名称来区分它们,例如Makefile1
和Makefile2
。在这种情况下,make -C ${CMAKE_CURRENT_SOURCE_DIR}
命令将不知道应该执行哪个Makefile
,除非你指定具体的文件名,例如make -f Makefile1 -C ${CMAKE_CURRENT_SOURCE_DIR}
。
因此,如果${CMAKE_CURRENT_SOURCE_DIR}
目录下有多个Makefile
,make -C ${CMAKE_CURRENT_SOURCE_DIR}
命令默认会找到并执行名为Makefile
的文件。如果有多个文件都叫Makefile
,那么这个命令可能会出错,或者只执行其中一个Makefile
,具体哪个可能取决于文件系统的行为。
Cmake传递lib给Makefile
将CMake引用的库连接到调用的Makefile中需要手动处理一些细节,但这是可行的。以下是一种可能的方法:
- 在您的CMakeLists.txt中,找到和引用的库相关的信息,包括库文件的路径和链接选项。例如,如果您使用
find_package
和target_link_libraries
来引用库,那么您需要找到这些库的路径和链接选项。 - 创建一个新的Makefile变量,用于传递库信息给被调用的Makefile。例如,您可以创建一个名为
LIBS
的变量,并将CMake找到的库信息赋值给它。 - 修改被调用的Makefile,使其能够接收并使用从CMake传递的库信息。在Makefile中,您需要使用
$(LIBS)
变量来添加库链接选项。
例如,您的CMakeLists.txt文件可能如下所示:
cmake_minimum_required(VERSION 3.10) project(MyProject) # Find your libraries using find_package or other methods find_package(YourLibrary REQUIRED) # ... add_custom_target(build_with_libs COMMAND make -C ${CMAKE_CURRENT_SOURCE_DIR} LIBS="${YourLibrary_LIBRARIES}" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
接下来,修改您的Makefile,以便它能够接收和使用LIBS
变量。例如:
# Your original Makefile content # Add the library information passed from CMake LDFLAGS += $(LIBS) # ...
这样,当您通过CMake调用Makefile时,CMake找到的库信息将被传递给Makefile,并用于链接过程。
Cmake传递参数给Makefile,比如make clean
我的目的是希望通过cmake生成的makefile直接传递参数给我的主makefile
修改CMakeLists.txt
以下是一个在CMakeLists.txt中添加自定义目标的示例:
cmake_minimum_required(VERSION 3.10) project(MyProject) find_library(MY_LIBRARY my_library) find_path(MY_INCLUDE_DIR my_header.h) function(add_custom_make_target TARGET_NAME EXTRA_ARGS) add_custom_target(${TARGET_NAME} COMMAND ${CMAKE_MAKE_PROGRAM} -C ${CMAKE_SOURCE_DIR} ${TARGET_NAME} ${EXTRA_ARGS} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) endfunction() add_custom_target(default_target ALL COMMAND MY_LIBRARY=${MY_LIBRARY} MY_INCLUDE_DIR=${MY_INCLUDE_DIR} ${CMAKE_MAKE_PROGRAM} -C ${CMAKE_SOURCE_DIR} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) #add_custom_make_target(clean) add_custom_make_target(clean_all "") add_custom_make_target(clean_global "") add_custom_make_target(generate "MY_LIBRARY=${MY_LIBRARY} MY_INCLUDE_DIR=${MY_INCLUDE_DIR}") add_custom_make_target(make_parallel "") add_custom_make_target(preprocess "") add_custom_make_target(check_dups "") add_custom_make_target(flat_src "") add_custom_make_target(mak "")
Windows下使用Cmake调用Makefile
在Windows上使用CMake调用Makefile是可行的,但需要考虑一些额外因素。首先,Windows上没有默认的Make工具,因此您需要安装GNU Make或其他Make实现。GNU Make通常可以通过安装MinGW或Cygwin获得。
然后,确保将Make工具添加到系统的PATH环境变量中,以便CMake可以找到并调用它。当您在Windows上使用CMake时,生成的构建系统可能是Visual Studio解决方案或NMake文件。因此,调用Makefile时,您需要确保使用了正确的构建系统。
例如,假设您已经安装了GNU Make并将其添加到了系统PATH中。在CMakeLists.txt中,您可以创建一个自定义目标,以便在Windows上调用Makefile:
cmake_minimum_required(VERSION 3.10) project(MyProject) # ... if(WIN32) add_custom_target(build_with_makefile COMMAND make -C ${CMAKE_CURRENT_SOURCE_DIR} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) endif()
在Windows上使用CMake生成构建文件时,您可能需要指定一个生成器。例如,您可以使用命令行选项-G
指定生成器:
cmake -G "MinGW Makefiles" ..
这将生成适用于MinGW Make的Makefile。然后,您可以使用cmake --build . --target build_with_makefile
命令来调用CMakeLists.txt中定义的自定义目标,进而调用您的原始Makefile。
注意,Visual Studio解决方案使用MSBuild作为其构建系统,而不是Make。当您在Windows上使用CMake生成Visual Studio解决方案时,CMake会生成一个.sln文件和多个.vcxproj文件,这些文件与MSBuild一起工作。在这种情况下,直接在CMakeLists.txt中调用原始的Makefile是不合适的。
不过,如果您仍然需要在Visual Studio解决方案中调用Makefile,可以创建一个自定义的构建事件。您可以在Visual Studio项目属性中设置这个事件,以在构建过程的某个特定阶段调用Makefile。具体操作如下:
- 在Visual Studio中打开您的项目。
- 右键点击项目名称,选择“属性”。
- 在“配置属性”下展开“生成事件”。
- 在“预生成事件命令行”、“预链接事件命令行”或“后生成事件命令行”中输入执行Makefile所需的命令。例如,您可以输入
make -C "$(ProjectDir)"
,其中$(ProjectDir)
是Visual Studio的一个宏,代表项目目录的路径。 - 点击“应用”,然后点击“确定”。
现在,在Visual Studio构建解决方案时,将在指定的构建阶段执行Makefile。需要注意的是,这种方法与CMake在很大程度上是分离的,因此您可能需要手动管理库依赖关系和其他构建设置。