计算机综合训练

前言:计算机综合训练是博主在2021.7.19至2021.7.25的大二升大三暑假的小学期课程。这篇博客记录了小学期里我学到的知识。

这门课有两个任务:①读remill源码 ②写个可以玩的数独软件。

cmd命令

  • 将某文件夹及其子文件夹内的所有文件的文件名输出到指定文件

    1
    dir /s/b > 所有文件.cpp

    输出示例:

    所有文件.txt

  • 将某文件夹及其子文件夹内的指定类型文件(如cpp文件)的文件名输出到指定文件

    1
    dir /s/b *.cpp > 所有文件.cpp

cmake语法

set

​ set有三个功能:设置普通变量、设置缓存入口、设置环境变量

​ 详细可查看官方文档:https://cmake.org/cmake/help/latest/command/set.html

设置普通变量

1
set(<variable> <value>... [PARENT_SCOPE])

<variable>在当前函数或目录范围内设置给定。

​ 如果PARENT_SCOPE给出该选项,则变量将设置在当前范围之上的范围内。每个新目录或函数都会创建一个新作用域。此命令会将变量的值设置到父目录或调用函数中(以适用于手头的情况为准)。变量值的先前状态在当前作用域中保持不变(例如,如果之前未定义,则仍为未定义;如果有值,则仍为该值)。

设置缓存入口

1
set(<variable> <value>... CACHE <type> <docstring> [FORCE])

​ 设置给定的缓存<variable>(缓存条目)。由于缓存条目旨在提供用户可设置的值,因此默认情况下不会覆盖现有的缓存条目。使用该FORCE选项覆盖现有条目。

<type>必须被指定为之一:

  • BOOL

    布尔ON/OFF值。 cmake-gui(1) 提供一个复选框。

  • FILEPATH

    磁盘上文件的路径。 cmake-gui(1) 提供一个文件对话框。

  • PATH

    磁盘上目录的路径。 cmake-gui(1) 提供一个文件对话框。

  • STRING

    一行文字。 cmake-gui(1) 提供文本字段或下拉选择,如果 STRINGS 缓存条目属性已设置。

  • INTERNAL

    一行文字。 cmake-gui(1)不显示内部条目。它们可用于跨运行持久存储变量。使用这种类型意味着FORCE.

​ 将<docstring>必须被指定为提供的选项,以快速摘要呈现一行文本cmake-gui(1) 用户。

​ 如果在调用之前缓存条目不存在或FORCE 给出了选项,则缓存条目将设置为给定值。

​ 缓存条目可能在调用之前存在,但如果它是在调用之前创建的,则没有设置类型 cmake(1)命令行由用户通过-D<var>=<value>选项而不指定类型。在这种情况下,该set命令将添加类型。此外,如果命令行中提供的<type>isPATHFILEPATH and<value>是相对路径,则该set命令会将路径视为相对于当前工作目录并将其转换为绝对路径。

设置环境变量

1
set(ENV{<variable>} [<value>])

​ 设置一个 Environment Variable 到给定的值。的后续调用$ENV{<variable>}将返回此新值。

​ 此命令仅影响当前 CMake 进程,而不影响调用 CMake 的进程,也不会影响整个系统环境,也不会影响后续构建或测试过程的环境。

​ 如果之后没有给出参数ENV{<variable>}或者 if<value>是空字符串,则此命令将清除环境变量的任何现有值。

​ 后面<value>的参数被忽略。如果发现额外的参数,则会发出警告。

用法举例

  • set(libs “${CMAKE_SOURCE_DIR}/src/main/jnilibs”)

​ 这个命令完成了这么一件事情:
定义了一个变量libs,并且变量的值为${CMAKE_SOURCE_DIR}/src/main/jnilibs,其中CMAKE_SOURCE_DIR 是一个cmake内置变量,指定了CMakeLists.txt所在的目录。详细介绍可参考:http://www.cnblogs.com/xianghang123/p/3556425.html。

  • set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/src/main/jnilibs/${ANDROID_ABI})
    这个命令用于给CMAKE_LIBRARY_OUTPUT_DIRECTORY宏赋值,该宏指定了cmake编译输出的东西应该放在什么地方。

    这个例子中的地方是${PROJECT_SOURCE_DIR}/src/main/jnilibs/${ANDROID_ABI}

    其中PROJECT_SOURCE_DIR 是cmake内置宏,指向构建工程的全路径。详细介绍可参考https://www.cnblogs.com/alphagl/p/6280061.html
    ————————————————
    参考链接:https://blog.csdn.net/guanguanboy/article/details/84838920

list

1
2
3
4
5
6
7
8
9
10
list(LENGTH <list><output variable>)
list(GET <list> <elementindex> [<element index> ...]<output variable>)
list(APPEND <list><element> [<element> ...])
list(FIND <list> <value><output variable>)
list(INSERT <list><element_index> <element> [<element> ...])
list(REMOVE_ITEM <list> <value>[<value> ...])
list(REMOVE_AT <list><index> [<index> ...])
list(REMOVE_DUPLICATES <list>)
list(REVERSE <list>)
list(SORT <list>)
  • LENGTH          返回list的长度

  • GET           返回list中index的element到value中

  • APPEND         添加新element到list中

  • FIND           返回list中element的index,没有找到返回-1

  • INSERT           将新element插入到list中index的位置

  • REMOVE_ITEM      从list中删除某个element

  • REMOVE_AT       从list中删除指定index的element

  • REMOVE_DUPLICATES 从list中删除重复的element

  • REVERSE          将list的内容反转

  • SORT           将list按字母顺序排序

————————————————

参考链接:https://blog.csdn.net/yjjy0921/article/details/50378567

find_package

1
2
3
4
find_package(<package> [version] [EXACT] [QUIET] [MODULE]
[REQUIRED] [[COMPONENTS] [components...]]
[OPTIONAL_COMPONENTS components...]
[NO_POLICY_SCOPE])

​ 从外部项目中查找和加载设置。 <package>_FOUND 将设置为指示是否找到了包。当找到包时,包特定的信息通过包本身记录的变量和导入目标提供。QUIET如果找不到包,该 选项将禁用消息。该 MODULE选项禁用下面记录的第二个签名。REQUIRED如果找不到包,该 选项将停止处理并显示错误消息。详见官方文档 https://cmake.org/cmake/help/v3.0/command/find_package.htm

​ 但这个搜索包的过程是如何实现的呢?官方文档没有说明。

​ 根据https://github.com/BrightXiaoHan/CMakeTutorial/tree/master/FindPackage 的指导,我梳理出下面的流程图。

find_package流程图.txt

通过Cmake内置模块引入依赖包

​ CMake官方为我们预定义了许多寻找依赖包的Module,他们存储在path_to_your_cmake/share/cmake-<version>/Modules目录下。每个以Find.cmake命名的文件都可以帮我们找到一个包。我们也可以在官方文档中查看到哪些库官方已经为我们定义好了,我们可以直接使用find_package函数进行引用。

​ 我们以curl库为例,假设我们项目需要引入这个库,从网站中请求网页到本地,我们看到官方已经定义好了FindCURL.cmake。所以我们在CMakeLists.txt中可以直接用find_pakcage进行引用。

1
2
3
4
5
6
7
8
find_package(CURL)
add_executable(curltest curltest.cc)
if(CURL_FOUND)
target_include_directories(clib PRIVATE ${CURL_INCLUDE_DIR})
target_link_libraries(curltest ${CURL_LIBRARY})
else(CURL_FOUND)
message(FATAL_ERROR ”CURL library not found”)
endif(CURL_FOUND)

​ 对于系统预定义的 Find<LibaryName>.cmake 模块,使用方法一般如上例所示。

​ 每一个模块都会定义以下几个变量

  • <LibaryName>_FOUND

  • <LibaryName>_INCLUDE_DIR or <LibaryName>_INCLUDES

  • <LibaryName>_LIBRARY or <LibaryName>_LIBRARIES

    ​ 你可以通过<LibaryName>_FOUND 来判断模块是否被找到,如果没有找到,按照工程的需要关闭 某些特性、给出提醒或者中止编译,上面的例子就是报出致命错误并终止构建。 如果<LibaryName>_FOUND 为真,则将<LibaryName>_INCLUDE_DIR 加入 INCLUDE_DIRECTORIES,

通过find_package引入非官方的库(该方式只对支持cmake编译安装的库有效)

​ 假设此时我们需要引入glog库来进行日志的记录,我们在Module目录下并没有找到 FindGlog.cmake。所以我们需要自行安装glog库,再进行引用。

​ 安装

1
2
3
4
5
6
7
8
9
10
# clone该项目
git clone https://github.com/google/glog.git
# 切换到需要的版本
cd glog
git checkout v0.40

# 根据官网的指南进行安装
cmake -H. -Bbuild -G "Unix Makefiles"
cmake --build build
cmake --build build --target install

​ 此时我们便可以通过与引入curl库一样的方式引入glog库了

1
2
3
4
5
6
7
8
find_package(glog)
add_executable(glogtest glogtest.cc)
if(GLOG_FOUND)
# 由于glog在连接时将头文件直接链接到了库里面,所以这里不用显示调用target_include_directories
target_link_libraries(glogtest glog::glog)
else(GLOG_FOUND)
message(FATAL_ERROR ”glog library not found”)
endif(GLOG_FOUND)

Module模式与Config模式

​ 通过上文我们了解了通过Cmake引入依赖库的基本用法。知其然也要知其所以然,find_package对我们来说是一个黑盒子,那么它是具体通过什么方式来查找到我们依赖的库文件的路径的呢。到这里我们就不得不聊到find_package的两种模式,一种是Module模式,也就是我们引入curl库的方式。另一种叫做Config模式,也就是引入glog库的模式。下面我们来详细介绍着两种方式的运行机制。

​ 在Module模式中,cmake需要找到一个叫做Find<LibraryName>.cmake的文件。这个文件负责找到库所在的路径,为我们的项目引入头文件路径和库文件路径。cmake搜索这个文件的路径有两个,一个是上文提到的cmake安装目录下的share/cmake-<version>/Modules目录,另一个使我们指定的CMAKE_MODULE_PATH的所在目录。

​ 如果Module模式搜索失败,没有找到对应的Find<LibraryName>.cmake文件,则转入Config模式进行搜索。它主要通过<LibraryName>Config.cmake or <lower-case-package-name>-config.cmake这两个文件来引入我们需要的库。以我们刚刚安装的glog库为例,在我们安装之后,它在/usr/local/lib/cmake/glog/目录下生成了glog-config.cmake文件,而/usr/local/lib/cmake/<LibraryName>/正是find_package函数的搜索路径之一。(find_package的搜索路径是一系列的集合,而且在linux,windows,mac上都会有所区别,需要的可以参考官方文档find_package

​ 由以上的例子可以看到,对于原生支持Cmake编译和安装的库通常会安装Config模式的配置文件到对应目录,这个配置文件直接配置了头文件库文件的路径以及各种cmake变量供find_package使用。而对于非由cmake编译的项目,我们通常会编写一个Find<LibraryName>.cmake,通过脚本来获取头文件、库文件等信息。通常,原生支持cmake的项目库安装时会拷贝一份XXXConfig.cmake到系统目录中,因此在没有显式指定搜索路径时也可以顺利找到。

————————————————

参考链接:https://github.com/BrightXiaoHan/CMakeTutorial/tree/master/FindPackage

add_executable & add_library

1
2
add_exectuable() 告诉工程生成一个可执行文件。
add_library() 则告诉生成一个库文件。

include_directories

1
include_directiories(/usr/include/curl)

​ 指定头文件目录在/usr/include/curl

1
target_link_libraries(myprogram path/curl.so)

​ 指定库文件目录在myprogram path/curl.so

include_directories与target_include_directories的区别

​ include_directories(x/y)影响目录范围。CMakeList中的所有目标,以及在其调用点之后添加的所有子目录中的目标,都会把路径x/y添加到它们的包含路径中。

​ target_include_directories(t x/y)有目标范围–它将x/y加入到目标t的包含路径中。

————————————————

参考链接:https://stackoverflow.com/questions/31969547/what-is-the-difference-between-include-directories-and-target-include-directories

几种include

1
2
3
4
# CMake的几种include
include_directories()
target_include_directories()
add_executable( xx.cpp xx.h)

​ 这三种方式都可以对当前项目添加引用路径。

include_directories()

​ include_directories()的影响范围最大,可以为CMakelists.txt后的所有项目添加头文件目录。

​ 一般写在最外层CMakelists.txt中影响全局。

target_include_directories()

​ target_include_directories()的影响范围可以自定义。如加关键子PRIVATE或这PUBLIC。

​ 一般引用库路径使用这个命令,作为外部依赖项引入进来,target是自己项目生成的lib。

1
2
project(myLib)
target_include_directories(myLib PRIVATE ${OpenCV_Include_dir}

​ 就表示将${OpenCV_Include_dir}头文件库路径只添加到了myLib项目

add_executable()

​ add_executable( )中添加的引用路径一般是当前目录下的源文件对应的头文件。是生成项目时引入的头文件。

​ 这种方式一般用于自己写的或某项目需要的头文件,这种方式需要加添加文件名字,而非头文件目录。

​ 如:

1
2
project(addFunc)
add_executable(addFunc addFunc.h addFunc.cpp)

————————————————
参考链接:https://blog.csdn.net/fb_help/article/details/81382746

target_compile_definitions

​ 为目标增加编译定义(可以理解为,这是个什么文件)。

1
2
3
4
target_compile_definitions(<target>
<INTERFACE|PUBLIC|PRIVATE> [items1...]
[<INTERFACE|PUBLIC|PRIVATE> [items2...] ...]
)

​ 例子:

1
2
3
target_compile_definitions(thirdparty_llvm INTERFACE
${LLVM_DEFINITIONS}
)

​ 说明thirdparty_llvm是LLVM文件,用LLVM编译。

foreach

1
2
3
4
5
6
7
foreach(LLVM_LIB IN LISTS LLVM_AVAILABLE_LIBS)
get_target_property(LLVM_LIB_TYPE ${LLVM_LIB} TYPE)
// lib类型是静态库,就把它加到LLVM_LIBRARIES里面
if(LLVM_LIB_TYPE STREQUAL "STATIC_LIBRARY")
list(APPEND LLVM_LIBRARIES "${LLVM_LIB}")
endif()
endforeach()

​ 以foreach()开头,以endforeach()结尾的循环。

math

1
math(EXPR REMILL_LLVM_VERSION_NUMBER "${LLVM_MAJOR_VERSION} * 100 + ${LLVM_MINOR_VERSION}")

​ 计算 ${LLVM_MAJOR_VERSION} * 100 + ${LLVM_MINOR_VERSION},并将结果返回到EXPR REMILL_LLVM_VERSION_NUMBER

CMAKE_HOST_SYSTEM_PROCESSOR

​ CMAKE正在运行的CPU的名字

CMAKE_ENABLE_EXPORTS

​ 指定可执行文件是否为可加载模块导出的option

message

​ message :为用户显示一条消息

1
2
message( [STATUS|WARNING|AUTHOR_WARNING|FATAL_ERROR|SEND_ERROR]
"message to display" ...)

​ 可以用下述可选的关键字指定消息的类型:

1
2
3
4
5
6
(无) = 重要消息;
STATUS = 非重要消息;
WARNING = CMake 警告, 会继续执行;
AUTHOR_WARNING = CMake 警告 (dev), 会继续执行;
SEND_ERROR = CMake 错误, 继续执行,但是会跳过生成的步骤;
FATAL_ERROR = CMake 错误, 终止所有处理过程;

编译选项

​ 在remill源码里看到了这一段,描述了VS编译器的编译选项。

1
2
3
4
5
6
7
8
9
target_compile_options(remill_settings INTERFACE
"$<$<CONFIG:Debug>:/MDd>$<$<CONFIG:Release>:/MD>"
/nologo /W3 /EHsc /wd4141 /wd4146 /wd4180 /wd4244
/wd4258 /wd4267 /wd4291 /wd4345 /wd4351 /wd4355 /wd4456
/wd4457 /wd4458 /wd4459 /wd4503 /wd4624 /wd4722 /wd4800
/wd4100 /wd4127 /wd4512 /wd4505 /wd4610 /wd4510 /wd4702
/wd4245 /wd4706 /wd4310 /wd4701 /wd4703 /wd4389 /wd4611
/wd4805 /wd4204 /wd4577 /wd4091 /wd4592 /wd4324
)

​ 那么这么多的编译选项,是什么意思呢?

​ /nologo表示编译时不在输出窗口显示这些设置

EH(异常处理模型)

1
/EH{s|a}[c][-]

此选项指定编译器使用的异常处理模型。

  • 使用 /EHs 指定同步异常处理模型(没有结构化异常处理异常的 C++ 异常处理)。如果使用 /EHs,不要依靠编译器捕捉异步异常。
  • 使用 /EHa 指定异步异常处理模型(带结构化异常处理异常的 C++ 异常处理)。

/EHc 选项要求指定 /EHs、/EHa 或 /GX。它通知编译器假定 extern C 函数从不引发异常。

上面的源码里有 /EHsc编译选项。不依靠编译器捕捉异步异常,且通知编译器假定 extern C 函数从不引发异常。而remill源码中也出现了很多 extern C,这是合理的。

————————————————

参考链接:

https://blog.csdn.net/zhou_ml/article/details/1470452

https://blog.csdn.net/dongfengkuayue/article/details/47044179

private、public、interface的区别

The PUBLIC, PRIVATE and INTERFACE keywords can be used to specify both the link dependencies and the link interface in one command. Libraries and targets following PUBLIC are linked to, and are made part of the link interface. Libraries and targets following PRIVATE are linked to, but are not made part of the link interface. Libraries following INTERFACE are appended to the link interface and are not used for linking <target>.

PUBLIC, PRIVATE和INTERFACE关键字可以用来在一条命令中指定链接依赖关系和链接界面。在PUBLIC后面的库和目标被链接到,并成为链接界面的一部分。在PRIVATE后面的库和目标被链接到,但不成为链接界面的一部分。在INTERFACE后面的库被附加到链接界面上,并且不用于链接

option

option是什么

​ option提供用户可选择的选项。

1
2
option(<option_variable> "help string describing option"
[initial value])

​ 用户可将option选择为 ON 或 OFF。如果未提供初始值,则使用 OFF。

​ remill源码举例:

1
2
option(REMILL_BARRIER_AS_NOP "Remove compiler barriers (inline assembly) in semantics" OFF)
option(REMILL_BUILD_SPARC32_RUNTIME "Build the Runtime for SPARC32. Turn this off if you have include errors with <bits/c++config.h>, or read the README for a fix" ON)

​ 每个option的描述性文字,大大方便了理解代码。

option的使用场景

CMake option使用场景 : 编译脚本传递参数 -> CMake脚本接收option -> 源代码宏。

  • 编译脚本传入参数
    传入一个cmake option TEST_DEBUG
1
2
3
4
#!/bin/sh

cmake -DTEST_DEBUG=ON .
cmake --build .

  • CMake脚本接收option
    cmake 脚本定义TEST_DEBUG 默认关闭OFF
1
2
3
4
5
6
7
project(test)

option(TEST_DEBUG "option for debug" OFF)
if (TEST_DEBUG)
add_definitions(-DTEST_DEBUG)
endif()
...

  • 源代码宏 test.c
1
2
3
4
5
#include "test.h"

#ifdef TEST_DEBUG
...
#endif

————————————————

参考链接(这个写的挺清晰的):https://www.jianshu.com/p/035bc18f8f62

extern C

extern “C”的主要作用就是为了能够正确实现C++代码调用其他C语言代码。加上extern “C”后,会指示编译器这部分代码按C语言(而不是C++)的方式进行编译。由于C++支持函数重载,因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中,而不仅仅是函数名;而C语言并不支持函数重载,因此编译C语言代码的函数时不会带上函数的参数类型,一般只包括函数名。

这个功能十分有用处,因为在C++出现以前,很多代码都是C语言写的,而且很底层的库也是C语言写的,**为了更好的支持原来的C代码和已经写好的C语言库,需要在C++中尽可能的支持C,而extern "C"就是其中的一个策略。**

​ 这个功能主要用在下面的情况:

  • C++代码调用C语言代码

  • 在C++的头文件中使用

  • 在多个人协同开发时,可能有的人比较擅长C语言,而有的人擅长C++,这样的情况下也会有用到

————————————————

参考链接:https://www.cnblogs.com/xiangtingshen/p/10980055.html

​ 宏(英语:Macro)是一种批量处理的称谓。

计算机科学里的宏是一种抽象(Abstraction),它根据一系列预定义的规则替换一定的文本模式。解释器编译器在遇到宏时会自动进行这一模式替换。对于编译语言,宏展开在编译时发生,进行宏展开的工具常被称为宏展开器。

​ 宏这一术语也常常被用于许多类似的环境中,它们是源自宏展开的概念,这包括键盘宏和宏语言。绝大多数情况下,“宏”这个词的使用暗示着将小命令或动作转化为一系列指令。

#define 这样的就是宏。

搭docker跑remill

​ 想做

各种文件类型

lib

​ LIB有两种,一种是静态库,比如C-Runtime库,这种LIB中有函数的实现代码,一般用在静态连编上,它是将LIB中的代码加入目标模块(EXE或者DLL)文件中,所以链接好了之后,LIB文件就没有用了。一种LIB是和DLL配合使用的,里面没有代码,代码在DLL中,这种LIB是用在静态调用DLL上的,所以起的作用也是链接作用,链接完成了,LIB也没用了。至于动态调用DLL的话,根本用不上LIB文件。 目标模块(EXE或者DLL)文件生成之后,就用不着LIB文件了。

dll

​ .dll,动态链接库。英文为DLL,是Dynamic Link Library的缩写。DLL是一个包含可由多个程序,同时使用的代码和数据的库。

优势是可以更为容易地将更新应用于各个模块,而不会影响该程序的其他部分。例如,您可能具有一个工资计算程序,而税率每年都会更改。当这些更改被隔离到 DLL 中以后,您无需重新生成或安装整个程序就可以应用更新。

环境变量与系统变量

​ 在装软件的时候我们往往要配置环境变量。这是为什么呢?

​ 环境变量是为了调用方便

​ 1、软件依赖项。

​ 比如java,很多软件会用到,如果不加入到环境变量,当需要调用java的时候就要指定java.exe的完整路径,如c:\program files\java\xxxxx\vvvv\java.exe,指定完整路径不要紧,问题是完整路径根本不知道,可能在D盘,也可能在E盘,不知道路径没法调用。加入到环境变量,其他软件直接call 一下 java.exe就行。管你在哪儿,os 帮你唤醒。

​ 2、方便。

​ 其实我感觉和上一条差不多,比如我们要打开注册表,运行,输入regedit就行,如果没有环境变量,就需要输入c:\windows\system32\regedit.exe。

​ 3、系统路径。

​ 比如有的软件想要到windows目录写入自己的文件,有些脚本语言是并没有类似的GetWindowsDirectory等函数获取windows目录,那可以直接使用类似%windir%的方式,直接切换到需要的系统路径。

作者:单纯的淫
链接:https://www.zhihu.com/question/381822915/answer/1099137710

​ 那么环境变量和系统变量是什么呢?

环境变量

​ 环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数,如:临时文件夹位置和系统文件夹位置等。

​ 环境变量是在操作系统中一个具有特定名字的对象,它包含了一个或者多个应用程序所将使用到的信息。例如WindowsDOS操作系统中的path环境变量,当要求系统运行一个程序而没有告诉它程序所在的完整路径时,系统除了在当前目录下面寻找此程序外,还应到path中指定的路径去找。用户通过设置环境变量,来更好的运行进程。

​ path 表示:指定可执行文件的搜索路径。

​ 实际上我们用的最多的也是这个变量,在我们安装各种软件,搭建各种开发环境时,一般也都是直接在这个path里面放入配置程序路径的。

​ 为了安全一般配置用户环境变量;为了省事一般配置系统环境变量。

系统变量

​ 系统变量是由操作系统定义的数据存储位置,无论谁登录该计算机,该位置都相同。Administrators 组(即管理员用户组)的用户可以添加新的变量或更改这些值。

————————————————

参考链接(这个写的挺清晰的):https://jingyan.baidu.com/article/a17d5285c9b0c48099c8f26a.html

一份笔记,没看,mark一下

https://sfumecjf.github.io/cmake-examples-Chinese/01-basic/1.7%20%20Including%20Third%20Party%20Library.html

打赏
  • 版权声明: 本博客所有文章除特别声明外,均采用 Apache License 2.0 许可协议。转载请注明出处!
  • © 2023 glisses
  • PV: UV:

请我喝杯咖啡吧~

支付宝
微信