CMake实战
CMake实战
1. Preface
通过CMake来实际构建一个项目,掌握cmake的使用流程;在之前的博客!《CMake学习记录》中,介绍了Cmake相关的基础知识;
2. 需要注意的问题
- 程序版本信息设置,包括编译时间设置
- 指定编译时的编译器
- 编译选项设置
3. 一个简单的Hello world项目
整个项目包含三个文件夹,一个main文件夹,一个para文件夹,一个calc文件夹;
目录结构如下所示:
3.1 根目录下:
CMakeLists.txt文件
# cmake最小版本3.1 cmake_minimum_required(VERSION 3.1) # 指定编译器的语句需要放在project之前 if(WIN32) # 需要在运行cmake命令是添加 -G "MinGW Makefiles"选项,则会将编译器默认切换为GUN g++, 否则不生效 set(CMAKE_C_COMPILER D:/software/mingw64/bin/gcc.exe) set(CMAKE_CXX_COMPILER D:/software/mingw64/bin/g++.exe) # cmake要求路径使用 / endif(WIN32) project(CalcDemon VERSION 1.2.4 LANGUAGES CXX C) # 给定程序的编译时间 string(TIMESTAMP COMPILE_TIME %Y%m%d-%H%M%S) configure_file(./CalcDemonConfig.h.in config.h) # 设置编译参数 if(WIN32) # add_complie_options 对所有编译器生效 / 作用域为全局,对子工程的编译也生效 # 根据实践,设置-Werror开启的时候,出现unused variable的时候会报错 # -Wextra开启的话,如果函数定义时是三个参数,实际函数内部只用到两个,编译器就会给出提示 add_compile_options(-Wall -std=c++11) # CMAKE_CXX_FLAGS 仅仅对C++编译器生效 #set(CMAKE_CXX_FLAGS -std=c++11 -Wall) endif(WIN32) # 设置编译类型 debug release set(CMAKE_BUILD_TYPE RelWithDebInfo) # 设置必须支持c++11 set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED True) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) # 构建子目录 add_subdirectory(./calc ./calc_build) # 构建子目录 add_subdirectory(./para ./para_build) # 构建子目录 构建子目录的时候,需要指定子目录 和构建的输出目录 # 注意:子目录构建的输出目录,是相对于当前cmakelists.txt文件的输出目录的 # 也即 ./mian构建的输出目录是相对于当前文件的目录 build/main_build add_subdirectory(./main ./main_build)
CalcDemonConfig.h.in文件:
// define the configured options and settings #define PROGRAM_NAME "@PROJECT_NAME@" #define PROGRAM_VER "@PROJECT_VERSION@" #define PROGRAM_VER_MAJOR "@PROJECT_VERSION_MAJOR@" #define PROGRAM_VER_MINOR "@PROJECT_VERSION_MINOR@" #define PROGRAM_VER_PATCH "@PROJECT_VERSION_PATCH@" // 编译时间 // 需要在cmakelists.txt文件中写入string(TIMESTAMP COMPILE_TIME %Y%m%d-%H%M%S) #define PROGRAM_COMPILE_TIMESTAMP "@COMPILE_TIME@"
3.2 calc目录
CMakeLists.txt文件,将calc目录下的源码编译为动态库:
# 定义makefile文件 # 导出为库 add_definitions(-DSIMPLE_EXPORT) # 定义宏 #add_definitions(-DSIMPLE_EXPORT=0) # 定义有值的宏 # file命令主要用于文件操作,包括读写,创建,匹配(GLOB) file(GLOB SRC_FILE_LST ./source/*.cpp) add_library(simpleCal SHARED ${SRC_FILE_LST}) target_include_directories(simpleCal PUBLIC ./include) message(STATUS "Generating lib simpleCal.")
simpleexportdef.h目录中定义导出符号:
#ifndef SIMPLE_EXPORT_DEF #define SIMPLE_EXPORT_DEF #if defined(WIN32) #ifdef SIMPLE_EXPORT #define SIMPLE_LIB_EXPORT __declspec(dllexport) #else #define SIMPLE_LIB_EXPORT __declspec(dllimport) #endif #else #define SIMPLE_LIB_EXPORT #endif #endif
add.h头文件定义add函数:
#ifndef ADD_H #define ADD_H #include "simpleexportdef.h" int SIMPLE_LIB_EXPORT addS(int x1, int x2); #endif
add.cpp文件实现add函数:
#include "add.h" int addS(int x1, int x2) { int sum = 0; sum = x1 + x2; return sum; }
div.h头文件定义div函数:
#ifndef DIV_H #define DIV_H #include "simpleexportdef.h" int SIMPLE_LIB_EXPORT divdS(int x1, int x2); #endif
div.cpp文件实现div函数
#include "div.h" int divdS(int x1, int x2) { int res = 0; if (x2 == 0) return res; res = x1 / x2; return res; }
mul.h头文件定义mul函数:
#ifndef MUL_H #define MUL_H #include "simpleexportdef.h" int SIMPLE_LIB_EXPORT mul(int x1, int x2); #endif
mul.cpp函数实现mul函数:
#include "mul.h" int mul(int x1, int x2) { int res = 0; res = x1 * x2; return res; }
subtract.h头文件定义subtract函数
#ifndef SUBTRACT_H #define SUBTRACT_H #include "simpleexportdef.h" int SIMPLE_LIB_EXPORT substract(int x1, int x2); #endif
subtract.h文件实现subtract函数
#include "substract.h" int substract(int x1, int x2) { int res = 0; res = x1 - x2; return res; }
3.3 para目录
CMakeLists.txt文件,将para目录下的源码编译为动态库:
# 定义makefile文件 # 导出为库 add_definitions(-DEXPORTS_GETOPT) # 定义宏 #add_definitions(-DSIMPLE_EXPORT=0) # 定义有值的宏 # file命令主要用于文件操作,包括读写,创建,匹配(GLOB) file(GLOB SRC_FILE_LST ./source/*.cpp) add_library(getopt SHARED ${SRC_FILE_LST}) target_include_directories(getopt PUBLIC ./include) message(STATUS "Generating lib getopt.")
getopt.h头文件定义getopt函数:
# ifndef __GETOPT_H_ # define __GETOPT_H_ # ifdef _GETOPT_API # undef _GETOPT_API # endif //------------------------------------------------------------------------------ # if defined(EXPORTS_GETOPT) && defined(STATIC_GETOPT) # error "The preprocessor definitions of EXPORTS_GETOPT and STATIC_GETOPT \ can only be used individually" # elif defined(STATIC_GETOPT) # pragma message("Warning static builds of getopt violate the Lesser GNU \ Public License") # define _GETOPT_API # elif defined(EXPORTS_GETOPT) # pragma message("Exporting getopt library") # define _GETOPT_API __declspec(dllexport) # else # pragma message("Importing getopt library") # define _GETOPT_API __declspec(dllimport) # endif # include <tchar.h> // Standard GNU options # define null_argument 0 /*Argument Null*/ # define no_argument 0 /*Argument Switch Only*/ # define required_argument 1 /*Argument Required*/ # define optional_argument 2 /*Argument Optional*/ // Shorter Versions of options # define ARG_NULL 0 /*Argument Null*/ # define ARG_NONE 0 /*Argument Switch Only*/ # define ARG_REQ 1 /*Argument Required*/ # define ARG_OPT 2 /*Argument Optional*/ // Change behavior for C\C++ # ifdef __cplusplus # define _BEGIN_EXTERN_C extern "C" { # define _END_EXTERN_C } # define _GETOPT_THROW throw() # else # define _BEGIN_EXTERN_C # define _END_EXTERN_C # define _GETOPT_THROW # endif _BEGIN_EXTERN_C extern _GETOPT_API TCHAR *optarg; extern _GETOPT_API int optind; extern _GETOPT_API int opterr; extern _GETOPT_API int optopt; struct option { /* The predefined macro variable __STDC__ is defined for C++, and it has the in- teger value 0 when it is used in an #if statement, indicating that the C++ l- anguage is not a proper superset of C, and that the compiler does not confor- m to C. In C, __STDC__ has the integer value 1. */ # if defined (__STDC__) && __STDC__ const TCHAR* name; # else TCHAR* name; # endif int has_arg; int *flag; TCHAR val; }; extern _GETOPT_API int getopt( int argc, TCHAR *const *argv , const TCHAR *optstring ) _GETOPT_THROW; extern _GETOPT_API int getopt_long ( int ___argc, TCHAR *const *___argv , const TCHAR *__shortopts , const struct option *__longopts , int *__longind ) _GETOPT_THROW; extern _GETOPT_API int getopt_long_only ( int ___argc, TCHAR *const *___argv , const TCHAR *__shortopts , const struct option *__longopts , int *__longind ) _GETOPT_THROW; // harly.he add for reentrant 12.09/2013 extern _GETOPT_API void getopt_reset() _GETOPT_THROW; _END_EXTERN_C // Undefine so the macros are not included # undef _BEGIN_EXTERN_C # undef _END_EXTERN_C # undef _GETOPT_THROW # undef _GETOPT_API # endif // __GETOPT_H_
getopt.cpp文件实现getopt函数:
# ifndef _CRT_SECURE_NO_WARNINGS # define _CRT_SECURE_NO_WARNINGS # endif # include <stdlib.h> # include <stdio.h> # include <tchar.h> # include "getopt.h" # ifdef __cplusplus # define _GETOPT_THROW throw() # else # define _GETOPT_THROW # endif enum ENUM_ORDERING { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER }; struct _getopt_data { int optind; int opterr; int optopt; TCHAR* optarg; int __initialized; TCHAR* __nextchar; int __ordering; int __posixly_correct; int __first_nonopt; int __last_nonopt; }; static struct _getopt_data getopt_data = { 0, 0, 0, NULL, 0, NULL, 0, 0, 0, 0 }; TCHAR* optarg = NULL; int optind = 1; int opterr = 1; int optopt = _T( '?' ); static void exchange( TCHAR** argv, struct _getopt_data* d ) { int bottom = d->__first_nonopt; int middle = d->__last_nonopt; int top = d->optind; TCHAR* tem; while ( top > middle && middle > bottom ) { if ( top - middle > middle - bottom ) { int len = middle - bottom; register int i; for ( i = 0; i < len; i++ ) { tem = argv[bottom + i]; argv[bottom + i] = argv[top - ( middle - bottom ) + i]; argv[top - ( middle - bottom ) + i] = tem; } top -= len; } else { int len = top - middle; register int i; for ( i = 0; i < len; i++ ) { tem = argv[bottom + i]; argv[bottom + i] = argv[middle + i]; argv[middle + i] = tem; } bottom += len; } } d->__first_nonopt += ( d->optind - d->__last_nonopt ); d->__last_nonopt = d->optind; } static const TCHAR* _getopt_initialize( const TCHAR* optstring , struct _getopt_data* d , int posixly_correct ) { d->__first_nonopt = d->__last_nonopt = d->optind; d->__nextchar = NULL; d->__posixly_correct = posixly_correct | !!_tgetenv( _T( "POSIXLY_CORRECT" ) ); if ( optstring[0] == _T( '-' ) ) { d->__ordering = RETURN_IN_ORDER; ++optstring; } else if ( optstring[0] == _T( '+' ) ) { d->__ordering = REQUIRE_ORDER; ++optstring; } else if ( d->__posixly_correct ) { d->__ordering = REQUIRE_ORDER; } else { d->__ordering = PERMUTE; } return optstring; } int _getopt_internal_r( int argc , TCHAR *const * argv , const TCHAR* optstring , const struct option* longopts , int* longind , int long_only , struct _getopt_data* d , int posixly_correct ) { int print_errors = d->opterr; if ( argc < 1 ) { return -1; } d->optarg = NULL; if ( d->optind == 0 || !d->__initialized ) { if ( d->optind == 0 ) { d->optind = 1; } optstring = _getopt_initialize( optstring, d, posixly_correct ); d->__initialized = 1; } else if ( optstring[0] == _T( '-' ) || optstring[0] == _T( '+' ) ) { optstring++; } if ( optstring[0] == _T( ':' ) ) { print_errors = 0; } if ( d->__nextchar == NULL || *d->__nextchar == _T( '\0' ) ) { if ( d->__last_nonopt > d->optind ) { d->__last_nonopt = d->optind; } if ( d->__first_nonopt > d->optind ) { d->__first_nonopt = d->optind; } if ( d->__ordering == PERMUTE ) { if ( d->__first_nonopt != d->__last_nonopt && d->__last_nonopt != d->optind ) { exchange( ( TCHAR * * ) argv, d ); } else if ( d->__last_nonopt != d->optind ) { d->__first_nonopt = d->optind; } while ( d->optind < argc && ( argv[d->optind][0] != _T( '-' ) || argv[d->optind][1] == _T( '\0' ) ) ) { d->optind++; } d->__last_nonopt = d->optind; } if ( d->optind != argc && !_tcscmp( argv[d->optind], _T( "--" ) ) ) { d->optind++; if ( d->__first_nonopt != d->__last_nonopt && d->__last_nonopt != d->optind ) { exchange( ( TCHAR * * ) argv, d ); } else if ( d->__first_nonopt == d->__last_nonopt ) { d->__first_nonopt = d->optind; } d->__last_nonopt = argc; d->optind = argc; } if ( d->optind == argc ) { if ( d->__first_nonopt != d->__last_nonopt ) { d->optind = d->__first_nonopt; } return -1; } if ( ( argv[d->optind][0] != _T( '-' ) || argv[d->optind][1] == _T( '\0' ) ) ) { if ( d->__ordering == REQUIRE_ORDER ) { return -1; } d->optarg = argv[d->optind++]; return 1; } d->__nextchar = ( argv[d->optind] + 1 + ( longopts != NULL && argv[d->optind][1] == _T( '-' ) ) ); } if ( longopts != NULL && ( argv[d->optind][1] == _T( '-' ) || ( long_only && ( argv[d->optind][2] || !_tcschr( optstring, argv[d->optind][1] ) ) ) ) ) { TCHAR* nameend; const struct option* p; const struct option* pfound = NULL; int exact = 0; int ambig = 0; int indfound = -1; int option_index; for ( nameend = d->__nextchar; *nameend && *nameend != _T( '=' ); nameend++ ) ; for ( p = longopts, option_index = 0; p->name; p++, option_index++ ) { if ( !_tcsncmp( p->name, d->__nextchar, nameend - d->__nextchar ) ) { if ( ( unsigned int ) ( nameend - d->__nextchar ) == ( unsigned int ) _tcslen( p->name ) ) { pfound = p; indfound = option_index; exact = 1; break; } else if ( pfound == NULL ) { pfound = p; indfound = option_index; } else if ( long_only || pfound->has_arg != p->has_arg || pfound->flag != p->flag || pfound->val != p->val ) { ambig = 1; } } } if ( ambig && !exact ) { if ( print_errors ) { _ftprintf( stderr , _T( "%s: option '%s' is ambiguous\n" ) , argv[0] , argv[d->optind] ); } d->__nextchar += _tcslen( d->__nextchar ); d->optind++; d->optopt = 0; return _T( '?' ); } if ( pfound != NULL ) { option_index = indfound; d->optind++; if ( *nameend ) { if ( pfound->has_arg ) { d->optarg = nameend + 1; } else { if ( print_errors ) { if ( argv[d->optind - 1][1] == _T( '-' ) ) { _ftprintf( stderr , _T( "%s: option '--%s' doesn't allow " ) _T( "an argument\n" ) , argv[0] , pfound->name ); } else { _ftprintf( stderr , _T( "%s: option '%c%s' doesn't allow " ) _T( "an argument\n" ) , argv[0] , argv[d->optind - 1][0] , pfound->name ); } } d->__nextchar += _tcslen( d->__nextchar ); d->optopt = pfound->val; return _T( '?' ); } } else if ( pfound->has_arg == 1 ) { if ( d->optind < argc ) { d->optarg = argv[d->optind++]; } else { if ( print_errors ) { _ftprintf( stderr , _T( "%s: option '--%s' requires an " ) _T( "argument\n" ) , argv[0] , pfound->name ); } d->__nextchar += _tcslen( d->__nextchar ); d->optopt = pfound->val; return optstring[0] == _T( ':' ) ? _T( ':' ) : _T( '?' ); } } d->__nextchar += _tcslen( d->__nextchar ); if ( longind != NULL ) { *longind = option_index; } if ( pfound->flag ) { *( pfound->flag ) = pfound->val; return 0; } return pfound->val; } if ( !long_only || argv[d->optind][1] == _T( '-' ) || _tcschr( optstring , *d->__nextchar ) == NULL ) { if ( print_errors ) { if ( argv[d->optind][1] == _T( '-' ) ) { /* --option */ _ftprintf( stderr , _T( "%s: unrecognized option '--%s'\n" ) , argv[0] , d->__nextchar ); } else { /* +option or -option */ _ftprintf( stderr , _T( "%s: unrecognized option '%c%s'\n" ) , argv[0] , argv[d->optind][0] , d->__nextchar ); } } d->__nextchar = ( TCHAR * ) _T( "" ); d->optind++; d->optopt = 0; return _T( '?' ); } } { TCHAR c = *d->__nextchar++; TCHAR* temp = ( TCHAR* ) _tcschr( optstring, c ); if ( *d->__nextchar == _T( '\0' ) ) { ++d->optind; } if ( temp == NULL || c == _T( ':' ) || c == _T( ';' ) ) { if ( print_errors ) { _ftprintf( stderr , _T( "%s: invalid option -- '%c'\n" ) , argv[0] , c ); } d->optopt = c; return _T( '?' ); } if ( temp[0] == _T( 'W' ) && temp[1] == _T( ';' ) ) { TCHAR* nameend; const struct option* p; const struct option* pfound = NULL; int exact = 0; int ambig = 0; int indfound = 0; int option_index; if ( *d->__nextchar != _T( '\0' ) ) { d->optarg = d->__nextchar; d->optind++; } else if ( d->optind == argc ) { if ( print_errors ) { _ftprintf( stderr , _T( "%s: option requires an argument -- '%c'\n" ) , argv[0] , c ); } d->optopt = c; if ( optstring[0] == _T( ':' ) ) { c = _T( ':' ); } else { c = _T( '?' ); } return c; } else { d->optarg = argv[d->optind++]; } for ( d->__nextchar = nameend = d->optarg; *nameend && *nameend != _T( '=' ); nameend++ ) ; for ( p = longopts, option_index = 0; p->name; p++, option_index++ ) { if ( !_tcsncmp( p->name , d->__nextchar , nameend - d->__nextchar ) ) { if ( ( unsigned int ) ( nameend - d->__nextchar ) == _tcslen( p->name ) ) { pfound = p; indfound = option_index; exact = 1; break; } else if ( pfound == NULL ) { pfound = p; indfound = option_index; } else if ( long_only || pfound->has_arg != p->has_arg || pfound->flag != p->flag || pfound->val != p->val ) { ambig = 1; } } } if ( ambig && !exact ) { if ( print_errors ) { _ftprintf( stderr , _T( "%s: option '-W %s' is ambiguous\n" ) , argv[0] , d->optarg ); } d->__nextchar += _tcslen( d->__nextchar ); d->optind++; return _T( '?' ); } if ( pfound != NULL ) { option_index = indfound; if ( *nameend ) { if ( pfound->has_arg ) { d->optarg = nameend + 1; } else { if ( print_errors ) { _ftprintf( stderr , _T( "%s: option '-W %s' doesn't allow " ) _T( "an argument\n" ) , argv[0] , pfound->name ); } d->__nextchar += _tcslen( d->__nextchar ); return _T( '?' ); } } else if ( pfound->has_arg == 1 ) { if ( d->optind < argc ) { d->optarg = argv[d->optind++]; } else { if ( print_errors ) { _ftprintf( stderr , _T( "%s: option '-W %s' requires an " ) _T( "argument\n" ) , argv[0] , pfound->name ); } d->__nextchar += _tcslen( d->__nextchar ); return optstring[0] == _T(':') ? _T(':') : _T('?'); } } else { d->optarg = NULL; } d->__nextchar += _tcslen( d->__nextchar ); if ( longind != NULL ) { *longind = option_index; } if ( pfound->flag ) { *( pfound->flag ) = pfound->val; return 0; } return pfound->val; } d->__nextchar = NULL; return _T( 'W' ); } if ( temp[1] == _T( ':' ) ) { if ( temp[2] == _T( ':' ) ) { if ( *d->__nextchar != _T( '\0' ) ) { d->optarg = d->__nextchar; d->optind++; } else { d->optarg = NULL; } d->__nextchar = NULL; } else { if ( *d->__nextchar != _T( '\0' ) ) { d->optarg = d->__nextchar; d->optind++; } else if ( d->optind == argc ) { if ( print_errors ) { _ftprintf( stderr , _T( "%s: option requires an " ) _T( "argument -- '%c'\n" ) , argv[0] , c ); } d->optopt = c; if ( optstring[0] == _T( ':' ) ) { c = _T( ':' ); } else { c = _T( '?' ); } } else { d->optarg = argv[d->optind++]; } d->__nextchar = NULL; } } return c; } } int _getopt_internal( int argc , TCHAR *const * argv , const TCHAR* optstring , const struct option* longopts , int* longind , int long_only , int posixly_correct ) { int result; getopt_data.optind = optind; getopt_data.opterr = opterr; result = _getopt_internal_r( argc , argv , optstring , longopts , longind , long_only , &getopt_data , posixly_correct ); optind = getopt_data.optind; optarg = getopt_data.optarg; optopt = getopt_data.optopt; return result; } int getopt( int argc, TCHAR *const * argv, const TCHAR* optstring) _GETOPT_THROW { return _getopt_internal( argc , argv , optstring , ( const struct option * ) 0 , ( int * ) 0 , 0 , 0 ); } int getopt_long( int argc , TCHAR *const * argv , const TCHAR* options , const struct option* long_options , int* opt_index ) _GETOPT_THROW { return _getopt_internal( argc , argv , options , long_options , opt_index , 0 , 0 ); } int _getopt_long_r( int argc , TCHAR *const * argv , const TCHAR* options , const struct option* long_options , int* opt_index , struct _getopt_data* d ) { return _getopt_internal_r( argc , argv , options , long_options , opt_index , 0 , d , 0 ); } int getopt_long_only( int argc , TCHAR *const * argv , const TCHAR* options , const struct option* long_options , int* opt_index ) _GETOPT_THROW { return _getopt_internal( argc , argv , options , long_options , opt_index , 1 , 0 ); } int _getopt_long_only_r( int argc , TCHAR *const * argv , const TCHAR* options , const struct option* long_options , int* opt_index , struct _getopt_data* d ) { return _getopt_internal_r( argc , argv , options , long_options , opt_index , 1 , d , 0 ); } void getopt_reset() _GETOPT_THROW { optarg = NULL; optind = 1; opterr = 1; optopt = _T( '?' ); // getopt_data.optind = 0; getopt_data.opterr = 0; getopt_data.optopt = 0; getopt_data.optarg = NULL; getopt_data.__initialized = 0; getopt_data.__nextchar = NULL; getopt_data.__ordering = 0; getopt_data.__posixly_correct = 0; getopt_data.__first_nonopt = 0; getopt_data.__last_nonopt = 0; }
4. 编译
在根目录下执行以下指令:
# 构建build目录 mkdir build # 开始构建 cmake -S . -B ./build -G "MinGW Makefiles" cd build # 编译 nmake
完成编译后的目录如下所示(关注build目录下生成内容):
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)