i007.cc

i007.cc

优先队列-降维打击

跨平台开发之CMake笔记

Git设置代理

一个实时的专用游戏服务器

因为近来需要把一些 Linux 项目转到 Windows 上来开发, 所以有了一些跨平台开发的笔记, 此篇讲CMake, 供以后查阅.

CMake介绍

你或许听过好几种 Make 工具,例如 GNU Make ,QT 的 qmake ,微软的 MS nmake,BSD Make(pmake),Makepp,等等。这些 Make 工具遵循着不同的规范和标准,所执行的 Makefile 格式也千差万别。这样就带来了一个严峻的问题:如果软件想跨平台,必须要保证能够在不同平台编译。而如果使用上面的 Make 工具,就得为每一种标准写一次 Makefile ,这将是一件让人抓狂的工作。

就是针对上面问题所设计的工具:它首先允许开发者编写一种平台无关的 CMakeList.txt 文件来定制整个编译流程,然后再根据目标用户的平台进一步生成所需的本地化 Makefile 和工程文件,如 Unix 的 Makefile 或 Windows 的 Visual Studio 工程。从而做到“Write once, run everywhere”。

显然,

. . .

CMake 是一个比上述几种 make 更高级的编译配置工具。一些使用 CMake 作为项目架构系统的知名开源项目有 VTK、ITK、KDE、OpenCV、OSG 等。

CMake一些有用的网站

将build和项目源文件分离的方法

假设项目A的根目录下有一个 CMakeLists, 在项目的根目录新建一个叫 build 的文件夹, 然后进入 build 文件夹内, 执行命令 cmake .. 即可.

CMakeLists实例讲解

比如有一个目录结构如下的项目 :

├─RealTimeServer
    ├─RealTimeServer
    │  ├─CMakeFiles.txt
    │  ├─a.h
    │  ├─a.cpp
    │  ├─b.h
    │  ├─b.cpp
    │  ├─TestFolder
    │  │  ├─a.h
    │  │  ├─a.cpp
    │  │  ├─b.h
    │  │  ├─b.cpp
    │  └─...
    ├─Tool

这是一个比较通用的CMakeLists.txt :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)

# support C++11
add_definitions(-std=c++11)



# 项目信息
set (PROJ_NAME RealTimeServer)
set (BIN_NAME rts)
project (${PROJ_NAME})


# 设置执行文件输出目录
# SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
# 设置库输出路径
# SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)


# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_SRCS 变量
# aux_source_directory(./${PROJ_NAME} DIR_SRCS)


# 查找当前目录以及子目录下的所有头文件
# 并将名称保存到 CURRENT_HEADERS 变量
# file(GLOB_RECURSE CURRENT_HEADERS  *.h *.hpp)


# 此命令可以用来收集源文件 CURRENT_HEADERS 作为变量保存收集的结果。 
# 后面为文件过滤器,其中 PROJ_NAME 为起始搜索的文件夹,即在 RealTimeServer 目录下,
# 开始收集,而且会遍历子目录
# file(
#     GLOB_RECURSE CURRENT_HEADERS 
#     LIST_DIRECTORIES false
#     "${PROJ_NAME}/*.h*"
# )

# 生成一个名为Include的VS筛选器
# source_group("Include" FILES ${CURRENT_HEADERS}) 


IF(WIN32) # Check if we are on Windows

    file(GLOB_RECURSE project_headers *.h)
    file(GLOB_RECURSE project_cpps *.c*)
    set(all_files ${project_headers} ${project_cpps})

    macro(create_filters source_files)
        if(MSVC)
            # 获取当前目录
            set(current_dir ${CMAKE_CURRENT_SOURCE_DIR})
            foreach(src_file ${${source_files}})
                # 求出相对路径
                string(REPLACE ${current_dir}/ "" rel_path_name ${src_file})
                # 删除相对路径中的文件名部分
                string(REGEX REPLACE "(.*)/.*" \\1 rel_path ${rel_path_name})
                # 比较是否是当前路径下的文件
                string(COMPARE EQUAL ${rel_path_name} ${rel_path} is_same_path)
                # 替换成Windows平台的路径分隔符
                string(REPLACE "/" "\\" rel_path ${rel_path})
                if(is_same_path)
                    set(rel_path "\\")
                endif(is_same_path)

                # CMake 命令
                source_group(${rel_path} FILES ${src_file})
            endforeach(src_file)
        endif(MSVC)
    endmacro(create_filters)

    create_filters(all_files)
    # add_executable(app ${all_files})

    # 指定生成目标
    add_executable(${PROJ_NAME} ${all_files})
    
  	if(MSVC) # Check if we are using the Visual Studio compiler
    		# set_target_properties(${PROJ_NAME} PROPERTIES LINK_FLAGS "/SUBSYSTEM:WINDOWS") # works for all build modes
    		set_target_properties(${PROJ_NAME} PROPERTIES LINK_FLAGS "/SUBSYSTEM:CONSOLE") # works for all build modes
    		target_link_libraries(${PROJ_NAME} wsock32 ws2_32)
        set_target_properties(${PROJ_NAME}
            PROPERTIES
            COMPILE_FLAGS /wd"4819"
        )
  	elseif(CMAKE_COMPILER_IS_GNUCXX)
	  	# SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mwindows") # Not tested
  	else()
	  	message(SEND_ERROR "You are using an unsupported Windows compiler! (Not MSVC or GCC)")
  	endif()

elseif(UNIX)

    file(GLOB_RECURSE all_files "*.*")

    include_directories(${PROJECT_SOURCE_DIR})
    # add_subdirectory(muduo/base)
    # add_subdirectory(muduo/net)

    # 指定生成目标
    add_executable(${BIN_NAME} ${all_files})
    # 添加链接库
    target_link_libraries(${BIN_NAME} pthread rt)

    # Inhibit all warning messages.
    if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
        # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long -pedantic")
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -w")
    endif()

  	# For gdb
    set(CMAKE_BUILD_TYPE "Debug")
    set(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")
    set(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")

else()
  	message(SEND_ERROR "You are on an unsupported platform! (Not Win32 or Unix)")
ENDIF()

对于像上面这样一个CMake的CMakeLists来说, 需要着重解释的有以下几点 :

  • add_definitions(-std=c++11) 这句是为了解决 linux 默认不支持 c++11 的问题
  • set_target_properties(${PROJ_NAME} PROPERTIES LINK_FLAGS "/SUBSYSTEM:WINDOWS") # works for all build modes 这句是为了解决WinMain的问题, 否则在vs平台会报main非法引用的问题. 这句会影响到vs的 “项目属性-链接器-系统-子系统”
  • target_link_libraries(${PROJ_NAME} wsock32 ws2_32) 这句是为了在vs下链接ws2_32库, windows需要链接这个库才能用socket. 这句会影响到vs的 “项目属性-链接器-输入-附加依赖项”
  • file(GLOB_RECURSE CURRENT_HEADERS *.h *.hpp) source_group("Include" FILES ${CURRENT_HEADERS}) 这句是为了解决在vs下不显示头文件的问题
  • set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -w")
    -w的意思是关闭编译时的警告,也就是编译后不显示任何warning,因为有时在编译之后编译器会显示一些例如数据转换之类的警告,这些警告是我们平时可以忽略的。
    -Wall选项意思是编译后显示所有警告。
    -W选项类似-Wall,会显示警告,但是只显示编译器认为会出现错误的警告。
    在编译一些项目的时候可以-W和-Wall选项一起使用。这里可以查看gcc的各种警告级别.

发表回复