CLion开发环境配置完全解析(Qt开发?STM32?速通cmake?

9,859 阅读8分钟

下载与安装

关于CLion的安装我推荐安装最新的CLion 2022.3之后的版本,因为从这个版本后性能提升了很多,而且还增加了对cmake代码的调试功能,并且对wsl的支持得到进一步的增强,现在有了CLion在windows上使用Linux环境进行开发将是非常简单且舒适的过程。

建议先下载JetBrains家的toolbox然后再进行对应 CLion 的下载,这样也方便你管理与统一配置JB家的所有IDE。

官网链接如下:www.jetbrains.com/zh-cn/toolb…

下载完toolbox后,点开安装CLion 2022.3(或更新的版本) 即可。当然如果你不设置的话,下载完CLion后你会发现它默认安装在C盘,不要慌,toolbox也考虑到了这样的问题。

所以你只需要像下面这样操作:

  1. 图1
  2. 图2
  3. 图3

更改完这个路径后,它会自动的把数据移动到这个文件夹中,之后下载的所有IDE数据也都会在这个文件夹中了。

我们发现下面还有一个选项是代理,这对某些情况显然是很有用的,如果IDE中的某些东西的下载需要代理,那么可以在这里设置代理。

现在CLion已经下载好了,点开后可能有些人没法正常使用,毕竟大家都是白嫖党(),其实作为学生的我们也完全没有必要花这笔钱,我们去官网申请一个资格即可,之后所有的JB家的产品都可以免费使用一年了,每年需要重新申请一次,申请的网站如下:www.jetbrains.com/shop/eform/…

申请前请用邮箱注册一个jb的账号,注册链接如下:account.jetbrains.com/login

申请有多种方式,学校邮箱如果有的话就很简单了,但是很多大学生没有这个,那么大家可以使用官方文件的方式申请,这种方式稍微麻烦一点,需要去学信网弄一个验证,具体流程如下:

利用官方文件申请

上述邮箱同样也需要填入jb账号对应的邮箱,申请完后,一般7天内就会有邮箱通知你成功了,之后所有的jb家的产品都可以通过该账号免费使用。而且这个账号似乎可以供6/7个人同时使用(我不知道上限,但是我给了至少6/7个人)。

至此,CLion应该能够正常打开和使用了,但是打开的界面估计默认是中文,我建议去设置里面调成中文。

方式如下:1.点开设置 2.点开插件 3.点开已安装的插件 4.点击中文的那个插件禁用即可。

主题推荐

我推荐使用新UI + onedark的组合(我目前所使用的)。

效果如下:

主题1

如果你使用的是旧版本的UI,我之前经常使用的主题有以下三个:

  1. Atom Material Icons 效果大概如下,这个一个用于将文件夹颜色更明亮的插件,新UI暂时不可用fileicon
  2. material-theme-ui:插件主页 plugins.jetbrains.com/plugin/8006…
  3. one-dark-theme:插件主页 plugins.jetbrains.com/plugin/1193…

编辑器与clang-format设置

鼠标滚轮改变字体大小

Editor -> General -> Change font size with Ctrl+Mouse Wheel 打上勾就行。

mouse control

clang-format的使用

关于clang-format是什么,我截一段chatgpt的回答。

clang-format简介

CLion是自带clang-format的,你只需要开启即可,他会自动扫描项目根目录下的clang-format文件进行相应的格式化,开启后你每次创建一个新项目他也会自动生成一个clang-format文件到项目根目录,这个文件配置是根据CLion默认的格式化的格式来的,如果想要更改格式化风格,只需要更改clang-formt配置文件即可。

一般来说,我们只需要配置基本风格就行,一个.clang-format文件大概长这样:

.clang-format

一般来说没有什么特殊需求,那么就只需要填写 BasedOnStyle 即可,是Google风格还是LLVM风格还是Microsoft风格,都取决于上述的前三行代码,后面的都可以不用写。我这个配置文件是想要使用Google的格式化风格,但是Googel风格默认的代码边距太短了,所以进行了一系列的调整。

下面是CLion如何开启clang-format,开启后CLion对代码的格式化将会以你项目根目录的clang-format文件为主。

clang-format1

我的 .clang-format 配置

BasedOnStyle: Google
Language: Cpp
AccessModifierOffset: -3
AlignAfterOpenBracket: Align
AlignConsecutiveMacros: AcrossComments
AlignConsecutiveAssignments: AcrossComments
AlignConsecutiveDeclarations: AcrossComments
AlignEscapedNewlines: Left
AlignOperands: true
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: Always
AllowShortCaseLabelsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: AllIfsAndElse
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
  AfterCaseLabel: false
  AfterClass: true
  AfterControlStatement: Always
  AfterEnum: false
  AfterFunction: true
  AfterNamespace: false
  AfterObjCDeclaration: false
  AfterStruct: true
  AfterUnion: true
  AfterExternBlock: true
  BeforeCatch: true
  BeforeElse: true

BreakBeforeBinaryOperators: None
BreakBeforeBraces: Custom
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 80
CommentPragmas: "^ NOLINT:"
CompactNamespaces: false
ConstructorInitializerIndentWidth: 2
ContinuationIndentWidth: 2
Cpp11BracedListStyle: true
DeriveLineEnding: true
DerivePointerAlignment: true
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
  - foreach
  - Q_FOREACH
  - BOOST_FOREACH
IncludeBlocks: Regroup
IncludeCategories:
  - Regex: '^<ext/.*.h>'
    Priority: 2
    SortPriority: 0
  - Regex: '^<.*.h>'
    Priority: 1
    SortPriority: 0
  - Regex: "^<.*"
    Priority: 2
    SortPriority: 0
  - Regex: ".*"
    Priority: 3
    SortPriority: 0
IncludeIsMainRegex: "([-_](test|unittest))?$"
IncludeIsMainSourceRegex: ""
IndentCaseLabels: true
IndentGotoLabels: true
IndentPPDirectives: None
IndentWidth: 3
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ""
MacroBlockEnd: ""
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Never
ObjCBlockIndentWidth: 4
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Right
RawStringFormats:
  - Language: Cpp
    Delimiters:
      - cc
      - CC
      - cpp
      - Cpp
      - CPP
      - "c++"
      - "C++"
    CanonicalDelimiter: ""
    BasedOnStyle: google
  - Language: TextProto
    Delimiters:
      - pb
      - PB
      - proto
      - PROTO
    EnclosingFunctions:
      - EqualsProto
      - EquivToProto
      - PARSE_PARTIAL_TEXT_PROTO
      - PARSE_TEST_PROTO
      - PARSE_TEXT_PROTO
      - ParseTextOrDie
      - ParseTextProtoOrDie
    CanonicalDelimiter: ""
    BasedOnStyle: google
ReflowComments: true
SortIncludes: CaseInsensitive
SortUsingDeclarations: false
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 3
SpacesInAngles: false
SpacesInConditionalStatement: false
SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
SpaceBeforeSquareBrackets: false
Standard: Auto
StatementMacros:
  - Q_UNUSED
  - QT_REQUIRE_VERSION
TabWidth: 4

编译工具链设置

编译工具链的添加与解释

这个配置是进行C++开发的关键,因为这个编译工具链就意味着C++的编译环境。

按下图点开对应的信息,如果你任何编译工具链都没有添加,由于新版本的CLion它会自带一个mingw的编译套件,所以默认会有一个CLion自带的mingw编译工具链,如下图所示我的编译工具链稍微有点丰富,有 msvc、g++、clang++、mingw,作为一个刚刚入门学编程的新手,我建议编译工具链这一块暂时就没必要了解了,但在CLion中编译的具体配置流程我认为还是有必要讲清楚。bianyi

CLion中添加编译工具链非常简单,你本机把对应工具链的路径加入到了环境变量,那么在你点击 + 对应编译链类型后,会自动扫描到,如果实在没有扫描到,那么也可以自己填入对应的路径,整个编译链包括:

  1. cmake,用于跨平台以及简化底层编译脚本的工具。
  2. cmake生成更底层的编译命令(对应上述的Build Tool),比如gmake也就是解析.makefile文件进行命令执行,比如 ninja 解析 .ninja文件进行命令执行(编译速度比makefile更快,亲身体验)。
  3. C语言的编译器(clang/gcc/cl等等)。
  4. C++的编译器(clang++/g++/cl等等)。

如果是mingw,那么上述的一套都是包含的,只需要把 Toolset 这个选项选择为mingw对应的目录即可,选择好后,CLion会自动识别上述四件套的位置。

接下来简单介绍如何添加一些工具链:

  • 安装msvc编译工具链:直接到官网下载VS2022,然后安装对应C++环境,打开CLion后添加msvc环境时就会自动识别。官网:visualstudio.microsoft.com/zh-hans/vs/

其实对于我们C++程序员而言,最需要的就是一个Linux环境,因为很多底层的系统调用是不在C++标准之内的,C++想要做到跨平台很难,所以我们需要把开发环境切换到Linux系统,正好windows提供了Linux子系统,也就是wsl,完美的解决了这个问题,不要考虑日常的使用和开发环境我们到底选哪个了,我全都要!

而CLion对wsl的适配程度和正常的本机开发几乎没有任何区别,我们只需要现在Windows上安装wsl2后,CLion便可以自动识别你本机的wsl环境了,但是你有了wsl,并不意味着你有了对应的编译链,之前说了,编译链是包括四个东西的,你需要一一手动再wsl上先安装好,CLion会自动识别到的,如果识别不到,由于是通过 apt install 命令安装的,大家应该都清楚在具体那个目录下,实在不清楚可以使用 which 命令。

cmake配置项的添加与解释

解释完上述编译链后,我们发现CLion中有个很明显的 default 字眼,这个有什么用呢?

如果排在第一个的编译链,会被设置为默认编译链,如果想要其他的编译链为默认,点击上移即可。

至于具体的作用,别急,先等我讲完CLion中cmake配置项的处理。

clion1

如上图所示,第二个 CMake 选项就是我们现在要讲的东西,而这两个正好也是整个开发环境中最重要的东西,第一个编译工具链决定了CLion中已经识别了本机有哪些编译环境,而第二个 CMake 选项,则是用于配置 cmake 基于哪些配置项生成。

所以我们现在应该了解了CLion是如何去编译项目生成可执行文件的了。

  1. 通过cmake配置选项运行整个项目的CMakeList.txt
  2. 生成makefile或其他底层脚本后再通过对应的工具去执行这个脚本
  3. 运行编译好的程序

而我们现在讲的就是添加cmake配置选项,如果你手动敲cmake命令的话,那样对应的就是命令行参数了。

clion3

上述图片中已经解释了一些配置的作用。这些配置项一般是不常改动,使用默认值就行,比如 Build options 是执行最后的脚本所用的参数,默认为 -j 12,比如如果是makefile,那么就是 make -j12

下面是大家可能需要进行一些配置的选项:

  1. Build type:这是程序最终编译的类型,意味着编译器该以何种程度对源代码进行优化,比如Debug版本一般再gcc中对应o2的优化,release版本对应o3的优化,两者一般存在10倍左右的性能差距。
  2. Toolchain:这是前面所说的编译工具链,一般来说,想要切换编译器,你切换这个选项就行了,默认使用default工具链。
  3. Generator:这是前面所说的工具链中的较为底层的脚本的运行工具,可以是makefile或者ninja,不选的话也是默认工具链里的那个。
  4. CMake options:这个是cmake运行时可以加入的命令行参数,比如我们可以-D来定义对应的变量控制对应的cmake行为,甚至于前面的Build type我们完全可以不写(当然这是CLion,这个空必须得被填充),然后使用-DCMAKE_BUILD_TYPE=Release,这个变量可以决定最终cmake生成的执行脚本是按照release的标准去运行的,又比如-DBUILD_SHARED_LIBS=ON,那么最终是会生成动态库而不是静态库,我上图中的 -DENABLE_TEST=ON 是内部的cmake有定义一个变量默认为OFF值,如果为ON时会加入测试代码为子项目。

现在cmake在CLion中的配置项已经讲完了,简单实践一下来体验之前讲的CLion到整个运行的流程,我这里就直接配图了:

  1. 通过cmake配置选项运行整个项目的CMakeList.txt。 clion4
  2. 生成makefile或其他底层脚本后再通过对应的工具去执行这个脚本。 我们先看一眼上一步cmake生成的文件(放出了两个不同的配置项产生的脚本,第一个使用的Generator为ninja,第二个使用的为gmake): clion5 如果想要继续执行这个脚本,应该在CLion中执行对应的源代码,CLion会自动识别入口点函数,然后给出可执行的按钮,如图: clion6 点击执行后,不仅会直接对应的 makefile 或 build.ninja 还会顺便把这个程序运行到CLion内置的终端环境中。
  3. 运行编译好的程序:这一步已经在第二步一并执行了。

理解了这三步以及cmake的配置之后,我相信如果突然间CLion不出现执行程序的按钮,或者一个外部的项目我们无法跑起来,那么我们肯定是会有对应的排查思路了。比如没有执行程序的按钮,那可能对应的cmake配置项你还没设置好,如果外部项目跑不起来,你可以把那四个编译工具链中的某个换其他的环境试试?

cmake的使用与实战

经过上述文字和图片讲解,我们很自然的想到,整个CLion运行C++代码其实就是在运行cmake和makefile(或build.ninja),第二个过程我们参与不了,但是第一个cmake的编写过程我们却需要一直接触。

下面用CLion新建项目自动生成的cmake模板来简单对cmake语法热热身。

cmake_minimum_required(VERSION 3.22)
project(untitled)

set(CMAKE_CXX_STANDARD 17)

add_executable(untitled main.cpp)
  • cmake_minimum_required 命令:规定了编译本项目的cmake工具至少需要3.22版本。

  • project 命令:规定了本项目的项目名称,同时也根据这个传入的值生成了一堆变量,常用的如下:

    1. PROJECT_NAME :项目名称
    2. PROJECT_BINARY_DIR :项目的二进制文件目录,即编译后的可执行文件和库文件的输出目录
    3. PROJECT_SOURCE_DIR :项目的源文件目录,即包含CMakeLists.txt文件的目录

    举个简单例子说明上述变量的作用: 比如一个测试的子项目中的CMakeList.txt,可能需要写下面的语句(先不管file命令),由于是作为直接的子项目,那么里面肯定不会存在project语句,所以PROJECT_SOURCE_DIR变量表示的仍然是整个项目的根目录,直接通过 ${} 的形式来使用它即可,这样就不需要关心相对或绝对路径了。

    file(GLOB SONIC_TEST_FILES
         "${PROJECT_SOURCE_DIR}/tests/*.h"
         "${PROJECT_SOURCE_DIR}/tests/*.cpp"
    )
    
  • set 命令:设置对应变量为对应的值,该变量存在,则修改该变量的值,如果不存在则会创建并初始化为对应的值,这里对set的使用是设置了 CMAKE_CXX_STANDARD 变量为17,这个变量可以控制最终编译采用的C++版本,这里是使用C++17。

  • add_executable 命令:这是用于生成可执行程序的命令,第一个参数为该执行程序最终编译后生成的文件名,后面跟着的都是需要编译的源代码。

对于新手而言,其实不太需要自己手写cmake,因为CLion会在你新建源文件的时候把相应源文件添加到add_excutable命令的后面,但项目稍微大一点或者说引入了很多外部库,那么大概率会抛弃CLion的这种自动化了。

常用的cmake变量(入门)

下面只列出了部分变量的作用,更多的变量请查看文档:cmake.org/cmake/help/…

  • PROJECT_NAME :项目名称
  • PROJECT_BINARY_DIR :项目的二进制文件目录,即编译后的可执行文件和库文件的输出目录
  • PROJECT_SOURCE_DIR :项目的源文件目录,即包含CMakeLists.txt文件的目录
  • CMAKE_BINARY_DIR :当前CMake运行的二进制文件目录,通常和PROJECT_BINARY_DIR是同一个目录
  • CMAKE_SOURCE_DIR :当前CMake运行的源文件目录,通常和PROJECT_SOURCE_DIR是同一个目录
  • CMAKE_C_STANDARD :指定C语言的标准版本
  • CMAKE_CXX_STANDARD :指定C++语言的标准版本
  • CMAKE_CXX_FLAGS :指定编译C++代码时使用的编译选项
  • CMAKE_C_FLAGS :指定编译C代码时使用的编译选项
  • CMAKE_EXE_LINKER_FLAGS :指定链接可执行文件时使用的链接选项
  • CMAKE_SYSTEM_NAME :指定当前操作系统名称(如Windows、Linux等)
  • CMAKE_SYSTEM_PROCESSOR :指定当前处理器的类型(如x86、x86_64等)
  • CMAKE_CXX_COMPILER_ID :指定了当前使用的C++编译器,同理可得C的编译器对应的名字。

对这些变量做一个简单的实践:

  1. 通过message打印出PROJECT_BINARY_DIR、PROJECT_SOURCE_DIR、CMAKE_BINARY_DIR、CMAKE_SOURCE_DIR来加以验证。 目录结构:

    .
    ├── CMakeLists.txt
    ├── main.cpp
    └── sub
        └── CMakeLists.txt
    

    cmake:

    main:
    cmake_minimum_required(VERSION 3.14)
    project(main)
    
    add_subdirectory(sub)
    
    message(STATUS "main:${PROJECT_NAME}\n  pro-src:${PROJECT_SOURCE_DIR}\n pro-bin:${PROJECT_BINARY_DIR}\n cmake-src:${CMAKE_SOURCE_DIR}\n cmake-bin:${CMAKE_BINARY_DIR}")
    
    sub:
    project(sub)
    
    message(STATUS "sub:${PROJECT_NAME}\n  pro-src:${PROJECT_SOURCE_DIR}\n pro-bin:${PROJECT_BINARY_DIR}\n cmake-src:${CMAKE_SOURCE_DIR}\n cmake-bin:${CMAKE_BINARY_DIR}")
    

    打印信息如下:我们发现CMake对应的变量没有变化,而Prject有了变量,因为我们在sub也使用了project命令。 clion7

  2. 通过变量检测环境执行不同的cmake代码:

    #判断当前的操作系统
    if (CMAKE_SYSTEM_NAME MATCHES "Linux")
        target_link_libraries(my-logger PUBLIC fmt-header-only pthread)
        message(STATUS "Now is Linux")
    elseif (CMAKE_SYSTEM_NAME MATCHES "Windows")
        target_link_libraries(my-logger PUBLIC fmt-header-only ws2_32)
        message(STATUS "Now is windows")
    endif ()
    #判断当前使用的编译器
    if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
      # Do something for GCC
    elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
      # Do something for Intel C++
    elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Microsoft")
      # Do something for Microsoft Visual C++
    elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
      # Do something for Clang
    endif()
    
    #判断当前的系统架构
    if (CMAKE_SYSTEM_PROCESSOR MATCHES "i.86|x86|x86_64|AMD64")
      # Do something for x86 architecture
    elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm|aarch64)")
      # Do something for ARM architecture
    elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "^(mips|mipsel|mips64)")
      # Do something for MIPS architecture
    elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc64)")
      # Do something for PowerPC architecture
    endif()
    
  3. 通过调整链接时的flag防止动态链接,因为如果你是使用Windows平台下的编译工具链,CLion有些时候最终链接并不是采用静态链接,导致你最终生成的可执行程序没法直接执行,这个时候你就需要使用下面的命令来强制静态链接了:

    set(CMAKE_EXE_LINKER_FLAGS "-static")
    

常用的cmake命令(入门)

下列只列出了部分命令,如果你以后有需要用到的其他命令,请前往官网进行查询:cmake.org/cmake/help/…

我个人较为常用的命令:

  1. project:用于定义项目名称、版本号和语言。

  2. add_executable:用于添加可执行文件。第一个参数很重要,被称为target,可以作为target_xxx命令的接收对象。

  3. add_library:用于添加库文件,可以创建静态库或动态库。第一个参数很重要,被称为target,可以作为target_xxx命令的接收对象。简单使用如下

    add_library(test_lib a.cc b.cc) #默认生成静态库
    add_library(test_lib SHARED a.cc b.cc) #默认生成静态库
    
  4. add_definitions:用于添加宏定义,注意该命令没有执行顺序的问题,只要改项目中用了该命令定义宏,那么所有的源代码都会被定义这个宏 add_definitions(-DFOO -DBAR ...)

  5. add_subdirectory:用于添加子项目目录,如果有该条语句,就先会跑去执行子项目的cmake代码,这样会导致一些需要执行后立马生效的语句作用不到,比如include_directories和link_directories如果执行在这条语句后面,则他们添加的目录在子项目中无法生效。有些命令如target_include_directories和target_link_directories是根据目标target是否被链接使用来生效的,所以这些命令的作用范围与执行顺序无关,且恰好同一个cmake项目中产生的库文件是可以直接通过名称链接的,无论链接对象是在子目录还是父目录

  6. target_link_libraries:用于将可执行文件或库文件链接到库文件或可执行文件。身为target_xxx的一员,很明显第二个参数也可以进行权限控制。

  7. include_directories:用于指定头文件搜索路径,优点是简单直接,缺点是无法进行权限控制,一旦被执行后,后续的所有代码都能搜索到对应的文件路径。

  8. target_include_directories:指定头文件搜索路径,并将搜索路径关联到一个target上,这里的target一般是指生成可执行程序命令里的target或者生成库文件的target,与上一个命令的不同点在于可以设置导出权限,比如现在我写了一个项目,这个项目引入了其他库,但是我不想让其他库的符号暴露出去(毕竟使用这个项目的人只关注这个项目的接口,不需要关注其他依赖的接口)可以通过PRIVATE将头文件搜索目录设置不导出的权限。

  9. link_directories:与前面的include_directories命令类似,添加的是库的搜索路径。

  10. target_link_directories:和前面的include版本一样的,只是改成了库路径。

  11. if\elseif\endif ,在编程语言立马已经用烂了,现在主要是了解 if(condition) 中的条件到底如何判断的,以及内部都支持哪些操作,比如大于等于啥的,这方面直接看官方文档吧,非常好懂:cmake.org/cmake/help/…

  12. aux_source_directory:这个指令简单实用,第一个参数传递一个文件目录,它会扫描这里面所有的源文件放到第二个参数定义的变量名中。注意第一个参数只能是文件夹。

    aux_source_directory(${PROJECT_SOURCE_DIR} SRC)
    
  13. file:可以说是上面那个命令的增强版本,但如果熟悉这个命令的朋友肯定很快站出来反对,因为这个命令实在是太强大了,你如果翻一翻这个官方文档就会发现它具备几乎文件系统的所有功能,什么读写文件啊,什么从网上下载文件,本地上传文件之类的它都有,计算文件的相对路径,路径转化等等。但我们平时用到的最多的命令还是用来获取文件到变量里。比如file(GLOB FILES "文件路径表示1" "文件路径表示2" ...) GLOB会产生一个由所有匹配globbing表达式的文件组成的列表,并将其保存到第二个参数定义的变量中。Globbing 表达式与正则表达式类似,但更简单,比如如果要实现前一个命令的功能可以这么写:

    file(GLOB SRC "${PROJECT_SOURCE_DIR}/*.cc")
    

    如果GLOB 换成GLOB_RECURSE ,那么上述命令将递归的搜寻其子目录的所有符合条件的文件,而不仅仅是一个层级。

  14. execute_process:用于执行外部的命令,如下的示例代码是执行git clone命令,执行命令的工作目录在 ${CMAKE_BINARY_DIR}/deps/

    execute_process(COMMAND git clone https://github.com/<username>/<repository>.git
                    WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/deps/<repository>)
    
  15. message:打印出信息用于debug。

  16. option:用于快速设置定义变量并赋值为对应的bool值,常被用于判断某些操作是否执行。

  17. find_package:用于查找外界的package,其实就是查找外界对应的 <package>Config.cmakeFind<package>.cmake 文件,这些文件里有外界包对应的变量信息以及库和头文件的各种路径信息。我们需要注意一些有关 find_package 命令查找 Config.cmake 路径的变量:

    • CMAKE_PREFIX_PATH 变量是一个路径列表,CMake 会在这些路径中搜索包的 Config.cmake 文件。
    • <Package>_DIR 变量是指向包的 Config.cmake 文件的路径。如果你手动设置了这个变量,那么 find_package 命令就可以找到包的信息。

    同时他的一些常用参数如下:

    • CONFIG :显式指定find_package去查找 <package>Config.cmake 文件,一般只要你在变量里面指定了 <package>Config.cmake的路径,那么该参数填不填都没差别。我建议最好还是带上该参数比较好。
    • REQUIRED :该参数表示如果没找到,那么直接产生cmake错误,退出cmake执行过程,如果没有REQUIRED,则即使没找到也不会终止编译。
    • PATHS :这个参数的效果和前面的变量类似,也是指定查找的路径。
    • COMPONENTS :用于指定查找的模块,模块分离在不同的文件中,需要使用哪个就指定哪个模块。典型的就是使用Qt时的cmake代码,比如 find_package(Qt5 COMPONENT Core Gui Widgets REQUIRED)
    • VERSION:可能有很多个不同版本的包,则需要通过该参数来指定,如:find_package(XXX VERSION 1.2.3)
  18. include:从文件或模块加载并运行 CMake 代码。我用这个命令实际上只是为了使用 FetchContent 这个module的功能,该功能是从cmake3.11开始支持的,使用该module前需要通过include命令加载该模块,命令如下:

    include(FetchContent)
    
  19. FetchContent:这是一个模块功能,它用来从代码仓库中拉取代码,例如我要把最近写的日志库引入到当前的项目中使用(注意这中间不会有任何代理,所以拉取GitHub的仓库可能失败):

    include(FetchContent)#引入功能模块
    
    FetchContent_Declare(
            my-logger  		 #项目名称
            GIT_REPOSITORY https://github.com/ACking-you/my-logger.git #仓库地址
            GIT_TAG        v1.6.2  #仓库的版本tag
            GIT_SHALLOW TRUE    #是否只拉取最新的记录
    )
    FetchContent_MakeAvailable(my-logger)
    
    add_excutable(main ${SRC})
    #链接到程序进行使用
    target_link_libraries(main my-logger)
    

    这样引入第三方库的好处显而易见,优点类似于包管理的效果了,但缺少了最关键的中心仓库来确保资源的有效和稳定。参考golang再做个proxy层级就好了。 同样可以拉取最新的googletest可以使用下列语句:

    FetchContent_Declare(
            googletest
            GIT_REPOSITORY https://github.com/google/googletest.git
            GIT_TAG        release-1.12.1
            GIT_SHALLOW TRUE
    )
    # For Windows: Prevent overriding the parent project's compiler/linker settings
    set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
    FetchContent_MakeAvailable(googletest)
    
    target_link_libraries(main gtest_main)
    
  20. function/endfunction :在cmake中用于定义函数,复用cmake代码的命令。第一个参数为函数的名称,后面为参数的名称,使用参数和使用变量时一样的,但是如果参数是列表类型,则在传入的时候就会被展开,然后与函数参数依次对应,多余的参数被 ARGN 参数吸收。

更多较为常用的命令:

  • add_custom_command:添加自定义规则命令,同样也是执行外界命令,但多了根据依赖和产物判断执行时机的作用。
  • install:添加install操作。
  • string:对string的所有操作,比如字符串替换啥的。
  • list:对list的所有操作,比如列表处理之类的。
  • foreach:cmake中的for循环。
  • ...

利用上述命令实现Qt开发中调用uic工具把 大量的 .ui 文件转化为 .cpp.h 文件,并实现当ui文件更新时或 .cpp/.h 文件不存在时才创建对应的 .cpp/.h 文件。

#函数功能实现
function(get_ui_source)
    foreach (item ${ARGN})
        set(UIC_EXE_PATH ${VCPKG_ROOT}/installed/x64-windows/tools/qt5/bin/uic.exe)
        get_filename_component(name ${item} NAME_WLE)
        string(PREPEND name "ui_")
        set(output_h ${PROJECT_SOURCE_DIR}/ui_gen/${name}.h)
        set(output_cpp ${PROJECT_SOURCE_DIR}/ui_gen/${name}.cpp)
        file(TIMESTAMP ${item} ui_time)
        #当.h文件已经存在时,仅当.ui文件被更新了才重新生成.h文件
        if (EXISTS ${output_h})
            file(TIMESTAMP ${output_h} h_time)
            if (ui_time GREATER h_time)
                execute_process(COMMAND ${UIC_EXE_PATH} ${item} -o ${output_h})
            endif ()
        else ()
            execute_process(COMMAND ${UIC_EXE_PATH} ${item} -o ${output_h})
        endif ()
        #当.cpp文件已经存在时,仅当.ui文件被更新了才重新生成.cpp文件
        if (EXISTS ${output_cpp})
            file(TIMESTAMP ${output_cpp} cpp_time)
            if (ui_time GREATER cpp_time)
                execute_process(COMMAND ${UIC_EXE_PATH} ${item} -o ${output_cpp})
            endif ()
        else ()
            execute_process(COMMAND ${UIC_EXE_PATH} ${item} -o ${output_cpp})
        endif ()
    endforeach ()
endfunction()

get_ui_source(${UI_FILES}) #功能的使用

ideaVim的使用与设置

关于vim的基本操作,这里就不细讲,网络上大把大把的教程,我这里只讲在CLion中怎么使用vim,怎么配置vim让它在CLion中更好用。

在使用ideaVim前,我们需要先下载 IdeaVim 这个拓展,ideaVim的链接:plugins.jetbrains.com/plugin/164-…,下载好后,应该是默认启用了的。

一、配置ctrl+c和ctrl+v和ctrl+a防止这些你常用的快捷键被vim占用

clion8

clion8

二、配置.ideavimrc。

点击 Open ~/.ideavimrc 后,加入下面的配置(这是我个人使用的配置)

let mapleader = ","
let g:mapleader = ","
set timeoutlen=300


" general
imap jk <Esc>
imap kj <Esc>
vnoremap q <Esc>
nmap <C-o> :action Back<CR>
nmap <C-i> :action Forward<CR>
nnoremap <Leader>r :<C-u>action RenameElement<CR>

" Redo
nnoremap U <C-r>

" code editing
nnoremap == :<C-u>action ReformatCode<CR>
vnoremap == :<C-u>action ReformatCode<CR>
nnoremap cc :<C-u>action CommentByLineComment<CR>
vnoremap cc :<C-u>action CommentByLineComment<CR>

" Run and debug
nnoremap \r :action RunClass<cr>
nnoremap \i :<C-u>action OptimizeImports<CR>
nnoremap \R :action Run<cr>

nnoremap [[ :action MethodUp<cr>
nnoremap ]] :action MethodDown<cr>

加入后点击右上角的小图标进行刷新即可。

ideavim

上述配置文件实现了下面的功能:

  1. 按下 j+k 或者 k+j 将会退出插入模式。
  2. 在可视模式下按q退出。
  3. ctrl+o可以实现指针回退到上一次的位置,ctrl+i前进到上次的位置。可以类比为VS李的前进和后退按钮(一般查看定义查看的比较深,可以使用该命令回退或前进)。
  4. , + r 可以实现CLion中的重构变量名。
  5. U取消撤销(由于vim中按u是撤销,而取消撤销需要ctrl+r,这让人很不习惯)
  6. = + =实现代码格式化。
  7. c + c实现代码注释
  8. [ + [跳转到前一个函数位置
  9. ] + ]跳转到下一个函数位置
  10. \ + r 运行当前的可执行程序(如果当前代码段里面有运行按钮的话)
  11. \ + R 运行当前可执行程序,这个是直接相当于按了右上角的三角按钮运行
  12. \ + i 优化导入的包