cmake 使用

示例

CMakeLists.txt

1
2
3
$ mkdir cmake-project
$ cd cmake-project
$ touch CMakeLists.txt

Code

1
$ mkdir src && vim src/main.cpp

Config

1
2
3
4
5
6
7
cmake_minimum_required(VERSION 3.10)

# set the project name
project(Tutorial)

# add the executable
add_executable(Tutorial src/main.cpp)

Build

1
2
3
$ mkdir build && cd build
$ cmake ..
$ make

Run

1
2
$ ./Tutorial
Hello, CMake.

语法示例

1
2
3
cmake_minimum_required(VERSION 2.8)
project("pybindcpp")
add_executable(HELLO src/pybindcpp.cpp)

完整示例

1
2
3
4
cmake_minimum_required(VERSION 2.8)
project("pybindcpp")

add_executable(HELLO src/pybindcpp.cpp)

编译

1
2
3
$ mkdir build && cd build
$ cmake ..
$ make

macro

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
macro(AddLibrary MODULE)
set(options NONE)
set(oneValueArgs PREFIX DEP)
set(multiValueArgs SUBMODULES)
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
message(STATUS "AddLibrary MODULE=${MODULE} PREFIX=${ARG_PREFIX} DEP=${ARG_DEP} SUBMODULES=${ARG_SUBMODULES}")


if ("${ARG_PREFIX}" STREQUAL "")
message(FATAL_ERROR "PREFIX should not be empty")
endif ()
foreach (I IN LISTS ARG_SUBMODULES)
set(TGT ${MODULE}::${I})
add_library(${TGT} STATIC IMPORTED GLOBAL)
set_target_properties(${TGT} PROPERTIES
IMPORTED_LOCATION "${ARG_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}${I}${CMAKE_STATIC_LIBRARY_SUFFIX}"
INCLUDE_DIRECTORIES ${ARG_PREFIX}/include)
add_dependencies(${TGT} ${ARG_DEP})
endforeach ()
endmacro(AddLibrary)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
macro(AddLibraryV2 MODULE)
set(options NONE)
set(oneValueArgs PREFIX DEP)
set(multiValueArgs SUBMODULES)
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
message(STATUS "AddLibrary MODULE=${MODULE} PREFIX=${ARG_PREFIX} DEP=${ARG_DEP} SUBMODULES=${ARG_SUBMODULES}")


if ("${ARG_PREFIX}" STREQUAL "")
message(FATAL_ERROR "PREFIX should not be empty")
endif ()
foreach (I IN LISTS ARG_SUBMODULES)
find_path(TGT_LIB_${I} NAMES "${I}" "lib${I}" HINTS ${ARG_PREFIX} PATH_SUFFIXES lib lib64)
find_path(TGT_INCLUDE_${I} NAMES "${MODULE}" HINTS ${ARG_PREFIX} PATH_SUFFIXES include)
set(TGT ${MODULE}::${I})
message(STATUS "AddLibrary TARGET=${TGT} TARGET_LIB_DIR=${TGT_LIB_${I}} TARGET_INCLUDE_DIR=${TGT_INCLUDE_${I}}")
add_library(${TGT} STATIC IMPORTED GLOBAL)
set_target_properties(${TGT} PROPERTIES
IMPORTED_LOCATION "${TGT_LIB_${I}}/${CMAKE_STATIC_LIBRARY_PREFIX}${I}${CMAKE_STATIC_LIBRARY_SUFFIX}"
INCLUDE_DIRECTORIES ${TGT_INCLUDE_${I}})
add_dependencies(${TGT} ${ARG_DEP})
include_directories(${TGT_INCLUDE_${I}})
endforeach ()
endmacro(AddLibraryV2)

cmake 使用

简介

CMake是一个比make更高级的编译配置工具,它可以根据不同平台、不同的编译器,生成相应的Makefile或者vcproj项目。通过编写CMakeLists.txt,可以控制生成的Makefile,从而控制编译过程。CMake自动生成的Makefile不仅可以通过make命令构建项目生成目标文件,还支持安装(make install)、测试安装的程序是否能正确执行(make test,或者ctest)、生成当前平台的安装包(make package)、生成源码包(make package_source)、产生Dashboard显示数据并上传等高级功能,只要在CMakeLists.txt中简单配置,就可以完成很多复杂的功能,包括写测试用例。如果有嵌套目录,子目录下可以有自己的CMakeLists.txt。

步骤

  • 编写CMakeLists.txt
  • 执行命令cmake PATHccmake PATH 生成Makefile
  • 使用make命令编译,make -j4 指定编译并行度
  • 使用 make install 安装

编译

指定安装路径

参考 这里

1
2
3
4
5
6
# 3.15 +
$ cmake --install /path/to/build --prefix /path/to/install [--config <CONFIG>]

# < 3.15
$ cd build
$ cmake -DCMAKE_INSTALL_PREFIX=/path/to/install -P cmake_install.cmake

指定编译类型

1
2
3
4
5
# 1
$ cmake -DCMAKE_INSTALL_PREFIX:PATH=/path/to/install -DCMAKE_BUILD_TYPE=Release /path/to/src

# 2
$ cmake --config Release --build . --target install

语法

CMakeLists.txt 的语法比较简单,由命令、注释和空格组成。# 后面的是注释。

commond (args ...)

  • commond为命令名,大小写不敏感
  • args为参数
    • 如果包含空格,使用双引号

变量引用

变量引用用 ${VAR} 语法

命令

set

set命令将多变量放在一起。

1
set (Foo a b c)
  • commond(${Foo}) 等价于 command(a b c)
  • commond("${Foo}") 等价于 command("a b c")

cmake_minimum_required

指定运行此配置文件所需的CMake的最低版本。

cmake_minimum_required (VERSION 2.8)

project

指定项目信息。

1
project (Demo)

add_executable

指定生成目标。

1
add_executable(Demo demo.cpp)

aux_source_directory

查找指定目录下的所有源文件,将结果存进指定变量名。

1
aux_source_directory(<dir> <variable>)
1
2
3
4
cmake_minimum_required (VERSION 2.8)
project (Demo)
aux_source_directory (. DIR_SRCS)
add_executable(Demo ${DIR_SRCS})

add_subdirectory

指明项目包含一个子目录,这样该目录下的CMakeLists.txt文件和源文件也会被处理。

1
add_subdirectory(math)

指明可执行文件需要链接的库。

1
target_link_libraries(Demo MathFunctions)

add_library

将指定的源文件生成链接文件,然后添加到工程中去

1
2
aux_source_directory(. DIR_LIB_SRCS)
add_library(MathFunctions ${DIR_LIB_SRCS})

指定连接器查找库的文件夹。此命令的相对路径被解释为相对于当前源目录。

格式

1
link_directories(directory1 directory2 ...)

示例

1
link_directories(${PROJECT_BINARY_DIR}/third_party/googletest/)

include_directories

将给定的目录添加到编译器用来搜索头文件的目录中。相对路径被解释为相对于当前源目录。

格式

1
include_directories([AFTER|BEFORE] [SYSTEM] dir1 [dir2 ...])

示例

1
2
include_directories(${PROJECT_SOURCE_DIR}/third_party/glog/)
include_directories(${PROJECT_SOURCE_DIR}/third_party/hdfs/)

find_package_handle_standard_args

用于 find_package 的实现。

变量

Cache

1
2
3
4
5
6
7
# CACHE, 不覆盖已有值. 
## 用途
### 1. 在命令行指定变量, 且不被覆盖
set(MY_CACHE_VARIABLE "VALUE" CACHE STRING "Description")

# FORCE, 强制设置
set(MY_CACHE_VARIABLE "VALUE" CACHE STRING "" FORCE)

常用变量

  • PROJECT_BINARY_DIR
    • Full path to build directory for project.
  • PROJECT_SOURCE_DIR
    • Top level source directory for the current project.
  • CMAKE_CURRENT_SOURCE_DIR
    • This the full path to the source directory that is currently being processed by cmake.

Scope

1
2
3
add_subdirectory : 建立新的 scope
include : 不会建立新的 scope
function : 建立新的 scope

示例

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
121
122
123
124
125
126
127
128
129
bovenson@HP:~/Git/notes/C++/Code/CMake/test$ tree
.
├── CMakeLists.txt
└── t.cpp

0 directories, 2 files
bovenson@HP:~/Git/notes/C++/Code/CMake/test$ cat t.cpp
#include <iostream>

using namespace std;

int main() {
cout<<"Hello"<<endl;
return 0;
}
bovenson@HP:~/Git/notes/C++/Code/CMake/test$ cat CMakeLists.txt
project("test")
add_executable(Hello t.cpp)
bovenson@HP:~/Git/notes/C++/Code/CMake/test$ cmake .
-- The C compiler identification is GNU 5.4.0
-- The CXX compiler identification is GNU 5.4.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/bovenson/Git/notes/C++/Code/CMake/test
bovenson@HP:~/Git/notes/C++/Code/CMake/test$ tree
.
├── CMakeCache.txt
├── CMakeFiles
│ ├── 3.5.1
│ │ ├── CMakeCCompiler.cmake
│ │ ├── CMakeCXXCompiler.cmake
│ │ ├── CMakeDetermineCompilerABI_C.bin
│ │ ├── CMakeDetermineCompilerABI_CXX.bin
│ │ ├── CMakeSystem.cmake
│ │ ├── CompilerIdC
│ │ │ ├── a.out
│ │ │ └── CMakeCCompilerId.c
│ │ └── CompilerIdCXX
│ │ ├── a.out
│ │ └── CMakeCXXCompilerId.cpp
│ ├── cmake.check_cache
│ ├── CMakeDirectoryInformation.cmake
│ ├── CMakeOutput.log
│ ├── CMakeTmp
│ ├── feature_tests.bin
│ ├── feature_tests.c
│ ├── feature_tests.cxx
│ ├── Hello.dir
│ │ ├── build.make
│ │ ├── cmake_clean.cmake
│ │ ├── DependInfo.cmake
│ │ ├── depend.make
│ │ ├── flags.make
│ │ ├── link.txt
│ │ └── progress.make
│ ├── Makefile2
│ ├── Makefile.cmake
│ ├── progress.marks
│ └── TargetDirectories.txt
├── cmake_install.cmake
├── CMakeLists.txt
├── Makefile
└── t.cpp

6 directories, 31 files
bovenson@HP:~/Git/notes/C++/Code/CMake/test$ make
Scanning dependencies of target Hello
[ 50%] Building CXX object CMakeFiles/Hello.dir/t.cpp.o
[100%] Linking CXX executable Hello
[100%] Built target Hello
bovenson@HP:~/Git/notes/C++/Code/CMake/test$ tree
.
├── CMakeCache.txt
├── CMakeFiles
│ ├── 3.5.1
│ │ ├── CMakeCCompiler.cmake
│ │ ├── CMakeCXXCompiler.cmake
│ │ ├── CMakeDetermineCompilerABI_C.bin
│ │ ├── CMakeDetermineCompilerABI_CXX.bin
│ │ ├── CMakeSystem.cmake
│ │ ├── CompilerIdC
│ │ │ ├── a.out
│ │ │ └── CMakeCCompilerId.c
│ │ └── CompilerIdCXX
│ │ ├── a.out
│ │ └── CMakeCXXCompilerId.cpp
│ ├── cmake.check_cache
│ ├── CMakeDirectoryInformation.cmake
│ ├── CMakeOutput.log
│ ├── CMakeTmp
│ ├── feature_tests.bin
│ ├── feature_tests.c
│ ├── feature_tests.cxx
│ ├── Hello.dir
│ │ ├── build.make
│ │ ├── cmake_clean.cmake
│ │ ├── CXX.includecache
│ │ ├── DependInfo.cmake
│ │ ├── depend.internal
│ │ ├── depend.make
│ │ ├── flags.make
│ │ ├── link.txt
│ │ ├── progress.make
│ │ └── t.cpp.o
│ ├── Makefile2
│ ├── Makefile.cmake
│ ├── progress.marks
│ └── TargetDirectories.txt
├── cmake_install.cmake
├── CMakeLists.txt
├── Hello
├── Makefile
└── t.cpp

6 directories, 35 files
bovenson@HP:~/Git/notes/C++/Code/CMake/test$ ./Hello
Hello

参考

cmake 使用

Command Line

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
$ Generate a Project Buildsystem
# cmake [<options>] <path-to-source>
# cmake [<options>] <path-to-existing-build>
# cmake [<options>] -S <path-to-source> -B <path-to-build>

$ Build a Project
# cmake --build <dir> [<options>] [-- <build-tool-options>]

$ Install a Project
# cmake --install <dir> [<options>]

$ Open a Project
# cmake --open <dir>

$ Run a Script
# cmake [{-D <var>=<value>}...] -P <cmake-script-file>

$ Run a Command-Line Tool
# cmake -E <command> [<options>]

$ Run the Find-Package Tool
# cmake --find-package [<options>]

$ View Help
# cmake --help[-<topic>]

内置变量

1
2
3
4
PROJECT_SOURCE_DIR           项目目录
CMAKE_CURRENT_LIST_DIR 当前 cmake 文件所在目录
CMAKE_STATIC_LIBRARY_PREFIX 静态库前缀, 例如 lib
CMAKE_STATIC_LIBRARY_SUFFIX 静态库后缀, 例如 .a

CMakeLists

设置cmake最小版本

1
cmake_minimum_required(VERSION 2.8)

设置项目名称

1
project("...")

判断OS

1
2
3
4
5
6
7
if (APPLE)
# do something
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
elseif (UNIX)
# do something
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
endif()

生成共享库

1
add_library(name SHARED src)

使用静态库

1
2
3
SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a")    # 查找库文件后缀
SET(BUILD_SHARED_LIBS OFF) # 关闭使用共享库
SET(CMAKE_EXE_LINKER_FLAGS "-static") # 连接时使用静态库

生成可执行文件

1
add_executable(MAIN src/main.cpp)

包含cmake文件

1
include(path/to/cmake)

打印消息

1
MESSAGE("msg...")

指定compiler

1
2
set(CMAKE_C_COMPILER "gcc-5")
set(CMAKE_CXX_COMPILER "g++-5")

编译类型

1
set(CMAKE_BUILD_TYPE=Release)	# or Debug

指定FLAGS

1
2
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")

遍历

1
2
3
4
set(L 1 2 3)
foreach(ITEM IN LISTS L)
messag(STATUS "item: ${ITEM}")
endforeach()

循环

1
2
3
4
set (L A B C)
foreach (V IN LISTS L)
... ${V}
endforeach()

打印 Target 属性

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
FUNCTION(PrintTargetProperties _tgt)
IF (NOT CMAKE_PROPERTY_LIST)
EXECUTE_PROCESS(COMMAND cmake --help-property-list OUTPUT_VARIABLE CMAKE_PROPERTY_LIST)

# Convert command output into a CMake list
STRING(REGEX REPLACE ";" "\\\\;" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}")
STRING(REGEX REPLACE "\n" ";" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}")
LIST(REMOVE_DUPLICATES CMAKE_PROPERTY_LIST)
ENDIF ()

IF (NOT TARGET ${_tgt})
MESSAGE(STATUS "[TargetProperties] There is no target named '${_tgt}'")
RETURN()
ENDIF ()

FOREACH (property ${CMAKE_PROPERTY_LIST})
STRING(REPLACE "<CONFIG>" "${CMAKE_BUILD_TYPE}" property ${property})

# Fix https://stackoverflow.com/questions/32197663/how-can-i-remove-the-the-location-property-may-not-be-read-from-target-error-i
IF (property STREQUAL "LOCATION" OR property MATCHES "^LOCATION_" OR property MATCHES "_LOCATION$")
CONTINUE()
ENDIF ()

GET_PROPERTY(_was_set TARGET ${_tgt} PROPERTY ${property} SET)
IF (_was_set)
GET_TARGET_PROPERTY(value ${_tgt} ${property})
MESSAGE("[TargetProperties] ${_tgt} ${property} = ${value}")
ENDIF ()
ENDFOREACH ()
ENDFUNCTION(PrintTargetProperties)

使用

1
PrintTargetProperties(spdlog::spdlog)

添加定义(add_definitions)

添加字符串

1
2
3
4
5
6
7
8
9
execute_process(
COMMAND sh -c "git rev-parse --short HEAD"
OUTPUT_VARIABLE TAG_REVERSION
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if (TAG_REVERSION STREQUAL "")
set(TAG_REVERSION "UNKNOWN")
endif ()
add_definitions(-DTAG_REVERSION="${TAG_REVERSION}") # 添加字符串

添加定义

1
add_definitions(-DDEBUG_MODE) # 添加字符串

使用

1
2
3
4
5
#ifdef DEBUG_MODE
...
#else
...
#endif

添加文件

1
file(GLOB_RECURSE SRCS src/**.cpp)  # 递归添加 src 下所有的 cpp 文件

变量

判断变量是否定义

1
2
3
if (DEFINED VAR_NAME) # NOT DEFINED VAR_NAME
...
endif()

转换字符串为大写

1
string(TOUPPER ${ORIGIN_VAR} DEST_VAR)

判断变量为空

1
2
3
if (${V} STREQUAL "")
...
endif()

变量默认值

1
2
3
4
5
6
# option, 只对 BOOL 类型, 默认 OFF
option(BUILD_THIRD_PARTY "build third party library" ON)

# cache, STRING 类型等
set(BUILD_THIRD_PARTY ON CACHE BOOL "build third party library")
set(DEPS_DIR "/tmp/cpp-external-lib" CACHE STRING "library install prefix" )

编译和安装

1
2
3
4
5
6
7
8
9
10
# build director
$ mkdir build
$ cd build
# cmake configure
$ cmake ..
$ cmake -DCMAKE_INSTALL_PREFIX=$PWD ..
# build
$ cmake --build .
# install
$ cmake --install . --config Release # debug...

PkgConfig

{library}.pc.in

1
2
3
4
5
6
7
8
9
10
11
12
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=${prefix}
includedir=@PKG_CONFIG_INCLUDEDIR@
libdir=@PKG_CONFIG_LIBDIR@

Name: lib@PROJECT_NAME@
Description: cpp common
URL: https://github.com/sunzhenkai/cpp-common
Version: @CPP_COMMON_VERSION@
CFlags: -I${includedir}
Libs: -L${libdir}
Requires: @PKG_CONFIG_REQUIRES@

cmake config

1
2
3
4
5
6
7
8
9
10
11
12
13
14
SET(PKG_CONFIG ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.pc)

IF (IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}")
SET(PKG_CONFIG_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}")
ELSE ()
SET(PKG_CONFIG_INCLUDEDIR "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
ENDIF ()
IF (IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}")
SET(PKG_CONFIG_LIBDIR "${CMAKE_INSTALL_LIBDIR}")
ELSE ()
SET(PKG_CONFIG_LIBDIR "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}")
ENDIF ()
CONFIGURE_FILE("cmake/${PROJECT_NAME}.pc.in" "${PKG_CONFIG}" @ONLY)
INSTALL(FILES "${PKG_CONFIG}" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")

修改库搜索路径

1
2
export CMAKE_PREFIX_PATH="$CUSTOME_LIBRARY_PATH"
export CMAKE_LIBRARY_PATH="$CUSTOME_LD_LIBRARY_PATH"

查找&链接库

1
2
3
4
5
# 将库路径写入 CMAKE_PREFIX_PATH
set(CMAKE_PREFIX_PATH ${PATH_TO_LIB} ${CMAKE_PREFIX_PATH})
find_package(<library-name> REQUIRED)
# 使用
target_link_libraries(<library-name> <target-name>)

示例

1
2
find_package(Snappy REQUIRED)
target_link_libraries(brpc Snappy::snappy)

自定义 Find Cmake 文件

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
find_path(THRIFT_INCLUDE_DIR
NAMES
thrift/Thrift.h
HINTS
/usr/local
PATH_SUFFIXES
include
)

find_library(THRIFT_LIBRARIES
NAMES
thrift libthrift
HINTS
/usr/local
PATH_SUFFIXES
lib lib64
)

find_program(THRIFT_COMPILER
NAMES
thrift
HINTS
/usr/local
PATH_SUFFIXES
bin bin64
)

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(THRIFT DEFAULT_MSG THRIFT_LIBRARIES THRIFT_INCLUDE_DIR THRIFT_COMPILER)
# 设置变量为高级,在 GUI 模式下默认不展示
mark_as_advanced(THRIFT_LIBRARIES THRIFT_INCLUDE_DIR THRIFT_COMPILER)

引入库

引入第三方库

引入第三方库的几种方式。第一种,find_path 查找头文件,find_library 查找库文件,分别使用 include_directories(DEP_INCLUDE_DIR)、target_link_libraries(target library) 链接库,这种方式一般用于没有 Find*.cmake 的库。第二种,对于有 Find cmake 的库,可以使用 find_package(Library REQUIRED) 来 import 库,然后使用 target_link_libraries 来链接库。第三种,自定义 Find Cmake 文件,借助 find_package_handle_standard_args 实现。对于有 pkgconfig 的库来说,也可以用 pkg_check_modules 来导入,但是有个问题,pkgconfig 内可能有写死的 prefix,移动之后可能会出现找不到库的问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# snappy
find_package(Snappy REQUIRED)
target_link_libraries(brpc-static Snappy::snappy)

# thrift
find_path(THRIFT_INCLUDE_DIR NAMES thrift/Thrift.h PATH_SUFFIXES include)
find_library(thrift thrift REQUIRED CONFIG)
include_directories(${THRIFT_INCLUDE_DIR})
target_link_libraries(brpc-static thrift)

# pkg_check_modules
include(FindPkgConfig)
pkg_check_modules(Curl libcurl REQUIRED)
# Curl_INCLUDE_DIR、Curl_LIBRARIES、Curl_FOUND 会被设置

示例

1
2
3
include(FindPkgConfig)
pkg_check_modules(brpc REQUIRED IMPORTED_TARGET brpc)
target_link_libraries(target PkgConfig::brpc)

使用 PkgConfig 文件添加库

简版

文档

1
2
3
4
5
6
7
8
# search
find_package(PkgConfig REQUIRED)
pkg_check_modules(SDL2 REQUIRED sdl2)

# link
target_link_libraries(testapp ${SDL2_LIBRARIES})
target_include_directories(testapp PUBLIC ${SDL2_INCLUDE_DIRS})
target_compile_options(testapp PUBLIC ${SDL2_CFLAGS_OTHER})

自己添加 target

find_package

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
# 完整签名
find_package(<PackageName> [version] [EXACT] [QUIET]
[REQUIRED] [[COMPONENTS] [components...]]
[OPTIONAL_COMPONENTS components...]
[CONFIG|NO_MODULE]
[GLOBAL]
[NO_POLICY_SCOPE]
[BYPASS_PROVIDER]
[NAMES name1 [name2 ...]]
[CONFIGS config1 [config2 ...]]
[HINTS path1 [path2 ... ]]
[PATHS path1 [path2 ... ]]
[REGISTRY_VIEW (64|32|64_32|32_64|HOST|TARGET|BOTH)]
[PATH_SUFFIXES suffix1 [suffix2 ...]]
[NO_DEFAULT_PATH]
[NO_PACKAGE_ROOT_PATH]
[NO_CMAKE_PATH]
[NO_CMAKE_ENVIRONMENT_PATH]
[NO_SYSTEM_ENVIRONMENT_PATH]
[NO_CMAKE_PACKAGE_REGISTRY]
[NO_CMAKE_BUILDS_PATH] # Deprecated; does nothing.
[NO_CMAKE_SYSTEM_PATH]
[NO_CMAKE_INSTALL_PREFIX]
[NO_CMAKE_SYSTEM_PACKAGE_REGISTRY]
[CMAKE_FIND_ROOT_PATH_BOTH |
ONLY_CMAKE_FIND_ROOT_PATH |
NO_CMAKE_FIND_ROOT_PATH])

CONFIG 命令

1
find_package(PackageName CONFIG)

CONFIG 命令会尝试搜索包提供的 <PackageName>Config.cmake<lowercasePackageName>-config.cmake 文件,并把包含该文件的文件夹路径赋值给 <PackageName>_DIR<PackageName>_CONFIG 保存配置文件的完整路径。

默认搜索路径

指定搜索路径

1
2
find_package (<package> PATHS paths... NO_DEFAULT_PATH)
# NO_DEFAULT_PATH: 不使用默认路径

设置变量

1
2
# 不管是否找到都会设置
{PackageName}_FOUND

库管理

ExternalProject_Add

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
include(ExternalProject)

set(target spdlog)
set(CMAKE_ARGS
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_INSTALL_PREFIX=${DEPS_PREFIX}
-DCMAKE_INSTALL_LIBDIR=lib
-DBUILD_STATIC_LIB=ON
-DBUILD_SHARED_LIB=OFF)
ExternalProject_Add(
${target}_build
GIT_REPOSITORY https://github.com/gabime/spdlog.git
GIT_TAG v1.9.2
CMAKE_ARGS ${CMAKE_ARGS}
)

# 指定 libary 安装文件夹,统一在 lib/lib64
-DCMAKE_INSTALL_LIBDIR=lib

# 参数
ExternalProject_Add(
<target-name>
GIT_REPOSITORY <git-repo-address>
GIT_TAG <git-tag>
PREFIX <prefix-path> # 创建 build、src 等目录所在的位置, 不是安装的路径
INSTALL_DIR <install_dir> # 不是安装的位置,作为属性,可用 ExternalProject_Get_Property 获取,在 CONFIGURE_COMMAND 等中指定 prefix
CMAKE_ARGS ${CMAKE_ARGS}
)

传入 CMAKE_C_FLAGS / CMAKE_CXX_FLAGS

1
2
3
4
5
6
7
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -lstdc++ -ldl")
ExternalProject_Add(... CMAKE_ARGS -DCMAKE_C_FLAGS=${CMAKE_C_FLAGS})

# 注意: 下面方式均不可
ExternalProject_Add(... CMAKE_ARGS -DCMAKE_C_FLAGS=-lstdc++ -ldl)
ExternalProject_Add(... CMAKE_ARGS -DCMAKE_C_FLAGS="-lstdc++ -ldl")
ExternalProject_Add(... CMAKE_ARGS -DCMAKE_C_FLAGS='-lstdc++ -ldl')

AddLibrary

1
2
3
4
5
6
7
8
9
add_library(${TGT} STATIC IMPORTED GLOBAL)
set_target_properties(${TGT} PROPERTIES
IMPORTED_LOCATION "${TGT_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}${TGT}${CMAKE_STATIC_LIBRARY_SUFFIX}"
INCLUDE_DIRECTORIES ${TGT_PREFIX}/include
INTERFACE_INCLUDE_DIRECTORIES ${TGT_PREFIX}/include
)

# INTERFACE_INCLUDE_DIRECTORIES
set_target_propterties 添加 INTERFACE_INCLUDE_DIRECTORIES, 在 target_link_libraries 时,不需要再 include 库的头文件

FetchContent_Declare

FetchContent_Declare 通常和 FetchContent_MakeAvailable 一块使用。

FetchContent_MakeAvailable 是 cmake 3.14 引入的。

FetchContent_Declare 的参数参考 ExternalProject_Add,和 ExternalProject_Add 相比,屏蔽了下面的命令。

  • CONFIGURE_COMMAND
  • BUILD_COMMAND
  • INSTALL_COMMAND
  • TEST_COMMAND

URL 指定本地文件

1
2
3
4
FetchContent_Declare(
boost
URL file:///tmp/boost-submodule-boost-1.80.0-1.tar.gz
)

指定 CMakeLists.txt 路径

如果 CMakeLists.txt 文件不在仓库根目录下,可以用 SOURCE_SUBDIR 来指定子路径。

1
2
3
4
5
6
7
include(FetchContent)
FetchContent_Declare(
protobuf
GIT_REPOSITORY https://github.com/protocolbuffers/protobuf.git
GIT_TAG ae50d9b9902526efd6c7a1907d09739f959c6297 # v3.15.0
SOURCE_SUBDIR cmake
)

Configure 阶段让 Target 可用

方法 (function)

1
2
function(FNAME)
endfunction(FNAME)

作用域

方法有独立的作用域,可以访问父级作用域内的变量。在函数内定义的变量,对父级作用域不可访问。如果需要修改父级作用域变量,需要使用 PARENT_SCOPE。

1
SET(VAR vALUEe PARENT_SCOPE)

参数

参数列表指定

1
2
3
4
5
function(ARG version url flag)
message(STATUS "version: ${version}, url: ${url}, flag: ${flag}")
endfunction(ARG)

ARG(1.0.0 www.so.com true)

非参数列表

首先了解在函数内定义的默认变量。

  • ARGC,参数数量
  • ARGN,参数,去掉声明的参数的参数列表
  • ARGV,参数,全部参数
  • ARG0,ARG1 …
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

function(ARG4)
set(options OPTIONAL FAST)
set(oneValueArgs NAME URL)
set(multiValueArgs KEY)
cmake_parse_arguments(PREFIX "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
message(STATUS "FAST=${PREFIX_FAST} NAME=${PREFIX_NAME} URL=${PREFIX_URL} KET=${PREFIX_KEY}")
endfunction(ARG4)

ARG4(
FAST
NAME beijing
URL www.so.com
KEY weight price
)
# FAST=TRUE NAME=beijing URL=www.so.com KET=weight;price

ARG4(
URL www.so.com
KEY band price
)
# FAST=FALSE NAME= URL=www.so.com KET=band;price

宏 (macro)

1
2
macro(MName)
endmacro(MName)

Macro 和 function 比较相似,区别如下。

  • macro 和调用域共享变量的作用域,function 则有独立的作用域

参考

编译

指定 Target

1
2
3
4
# pwd: {project}/build
cmake ..
cmake --build . --target {target}
cmake --build . --target mongoc_mongoc

优化

ccache

1
2
3
4
5
# 安装
sudo yum install ccache

# 配置
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache

安装

指定安装目录。

1
2
3
4
# 系统路径
cmake --install {build-dir} --prefix "/usr"
# 示例
cmake --install build --prefix "$PWD"

安装头文件

1
2
# 使用 install
install(DIRECTORY include/ DESTINATION include) # FILES_MATCHING PATTERN "*.h"

brpc c++

描述

brpc c++ 是百度基于c++编写的RPC框架,文档参考这里

编译

编译文档参考这里。编译brpc之前,首先需要准备依赖,brpc一下如下库。

  • gflags:定义全局变量
  • protobuf:序列化消息及服务接口
  • leveldbrpcz 需要,记录RPC踪迹

vim 插件

Command-T

安装

安装Ruby

  • 源码安装

    1
    2
    3
    $ ./configure --prefix=/home/work/sunzhenkai/bin/ruby --enable-shared
    $ make -j24
    $ make install
  • 使用ruby-install安装

异常

  • 找不到 libruby.so.***

    1
    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/work/sunzhenkai/bin/ruby/lib
  • Command-T plugin error: could not load the C extension

    1
    2
    $ cd /path/to/command-t
    $ rake make

Github

使用

NERDTree

安装

1
Plugin 'scrooloose/nerdtree'

使用

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
#### 注册热键
map <C-n> :NERDTreeToggle<CR> # ctrl+n 唤起 NERDTree

#### 切换工作台和目录
ctrl + w + h 光标 focus 左侧树形目录
ctrl + w + l 光标 focus 右侧文件显示窗口
ctrl + w + w 光标自动在左右侧窗口切换
ctrl + w + r 移动当前窗口的布局位置

# 常用
m * 文件操作:复制、删除、移动等
I * 显示或者不显示隐藏文件
r * 刷新光标所在的目录
C * 将根路径设置为光标所在的目录
u * 设置上级目录为根路径
cd * 设置当前工作路径

o 在已有窗口中打开文件、目录或书签,并跳到该窗口
go 在已有窗口 中打开文件、目录或书签,但不跳到该窗口
t 在新 Tab 中打开选中文件/书签,并跳到新 Tab
T 在新 Tab 中打开选中文件/书签,但不跳到新 Tab
i split 一个新窗口打开选中文件,并跳到该窗口
gi split 一个新窗口打开选中文件,但不跳到该窗口
s vsplit 一个新窗口打开选中文件,并跳到该窗口
gs vsplit 一个新 窗口打开选中文件,但不跳到该窗口
! 执行当前文件
O 递归打开选中 结点下的所有目录
x 收起当前打开的目录
X 收起所有打开的目录
e 以文件管理的方式打开选中的目录
D 删除书签
P 大写,跳转到当前根路径
p 小写,跳转到光标所在的上一级路径
K 跳转到第一个子路径
J 跳转到最后一个子路径
<C-j> 和 <C-k> 在同级目录和文件间移动,忽略子目录和子文件
U 设置上级目录为跟路径,但是维持原来目录打开的状态
R 刷新当前根路径
f 打开和关闭文件过滤器
q 关闭 NERDTree
A 全屏显示 NERDTree,或者关闭全屏

vim-javacomplete2

安装

1
Plugin 'artur-shaik/vim-javacomplete2'

YouCompleteMe

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1 下载
## .vimrc
Plugin 'Valloric/YouCompleteMe'
## vim
:PluginInstall

# 2 Mac OS
## 2.1 依赖
$ brew install mono
$ brew install go
## 2.2 编译
$ cd ~/.vim/bundle/YouCompleteMe
$ git submodule update --init --recursive
$ ./install.py --all --clangd-completer

vim tips

窗口

1
2
3
4
5
6
7
8
:tabnew			新建窗口
g t 下一个窗口
g T 上一个窗口

# 调整高度
:resize/res 60/+5/-5 调整窗口高度
# 宽度
:vertical resize 80

命令

1
2
3
4
5
6
7
8
9
10
11
;  			重复上一个动作
:!! 重复上一条命令
:shell 运行shell

# run shell commands
## 1
C-z vim 后台运行
fg 调回vim

## 2
:!<cmd>

移动

行内移动

1
2
3
4
5
6
7
8
9
10
11
12
h	 左移一位
l 右移一位
0 行首
$ 行尾
^ 当前行的第一个非空白符位置
fx 移动当当前行的下一个x处
Fx 移动当当前行的上一个x处
tx 移动到x的左边一个位置
w 往后移动一个词
b 往前移动一个词
) 移动到下一个句子
( 移动到上一个句子

文件内移动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<C-F>  向下移动一屏
<C-B> 向上移动一屏
G 移动到文件末尾
nG 移动到第n行
gg 文件首
H 移动光标到屏幕上部
M 移动光标到屏幕中部
L 移动光标到屏幕底部
* 移动到光标所在字符串的下个位置
# 移动到光标所在字符串的上个位置
/s 向后搜索字符串s
?s 向前搜索字符串s
ma 打标签,标签名为a
`a 跳转到标签a
`. 跳转到上次编辑的地方

滚动

1
2
3
4
ctrl+b    向下滚动一页
ctrl+f 向上滚动一页
ctrl+u 向上滚动半页
ctrl+d 向下滚动半页

注意 ctrl+b 和 tmux 快捷键冲突

编辑

复制

1
2
3
4
5
6
7
8
9
yy      拷贝当前行
{n}yy 拷贝 n 行
y$ 拷贝到行尾
y^ 拷贝到行首
yiw 拷贝词
# 复制一个当前单次
byw b: 到单次首; y: yank; w: for word
# 复制一定行数范围的文本
{no1}Gy{no2}

剪切/删除

1
2
3
dd     剪切当前行
{n}dd 剪切 n 行
d$ 剪切到行尾

删除指定行

1
2
3
4
5
6
7
8
9
10
11
12
13
# pattern 删除
:g/{pattern}/d # 删除所有包含 pattern 的行
:g!/{pattern}/d # 删除所有不包含 pattern 的行
# g! 等价与 v
:v/{pattern}/d # 删除所有不包含 pattern 的行
# 多个 pattern
:v/{pattern}\|{pattern}\|{pattern}/d # \| 或

# 示例
:g/profile/d
:g/^\s*$/d
:g!/^\s*"/d
:v/error\|warn\|fail/d

粘贴

1
2
3
4
5
P    光标前插入
p 光标后插入

## command mode 粘贴内容
C-r" ": default register

使用系统粘贴命令导致的 indent 异常

1
2
3
:set paste  # 切换到粘贴模式
# 粘贴完后, 切换回来
:set nopaste

编辑

1
i  从光标位置输入

查找替换

文件内查找

1
2
3
4
5
# change to command mode
/{pattern}
# 下一个/上一个
n next
N previous

跨文件查找

1
2
3
4
5
6
7
:vimgrep /{pattern}/g [file]    
# :cd # 先设置当前工作路径
# :vimgrep /foobar/g ** # 查找所有文件
:cn[f] 下一个匹配[文件]
:cp[f] 上一个匹配[文件]
:cr/cla 回到开始/结束
:copen 打开匹配列表

统计有多少匹配

1
:%s/{pattern}//gn

替换

1
2
:%s/foo/bar/g
:5,10s/foo/bar/gc # with confirm

多行注释

注释

1
2
3
4
5
6
Esc
Ctrl + v
Shift + i (I)
# select multi lines
# input comments
Esc

取消注释

1
2
3
4
Esc
Ctrl + v
# select
d / x

插件

NERDTree

目录

1
2
3
4
5
6
7
8
9
10
11
# 目录
## NERDTree
r 刷新光标所在的目录
C 将根路径设置为光标所在的目录
u 设置上级目录为根路径
cd 设置当前工作路径
m 文件操作:复制、删除、移动、创建等
P 大写,跳转到当前根路径
p 小写,跳转到光标所在的上一级路径
x 收起当前打开的目录
X 收起所有打开的目录

打开文件

1
2
3
4
5
6
7
8
o       在已有窗口中打开文件、目录或书签,并跳到该窗口
go 在已有窗口 中打开文件、目录或书签,但不跳到该窗口
t 在新 Tab 中打开选中文件/书签,并跳到新 Tab
T 在新 Tab 中打开选中文件/书签,但不跳到新 Tab
i 水平切分已有窗口并打开文件,并跳到该窗口
gi 水平切分已有窗口并打开文件,但不跳到该窗口
s 垂直切分已有窗口并打开文件,并跳到该窗口
gs 垂直切分已有窗口并打开文件,但不跳到该窗口

vim usage

[toc]

简介

三种模式:

  • 输入模式(Insert mode
  • 末行模式(Last line mode
  • 命令模式(Command mode

切换:

按键 到达的模式
i,o,r 输入模式 / 编辑模式
: 末行模式 / 指令列模式
ESC 命令模式 / 一般模式

命令

基础

命令 说明
:e filename Open filename for edition
:w Save file
:q Exit Vim
:q! Quit without saving
:x Write file (if changes has been made) and exit
:sav filename Saves file as filename
. Repeats the last change made in normal mode
5. Repeats 5 times the last change made in normal mode

移动

命令 说明
k or Up Arrow move the cursor up one line
j or Down Arrow move the cursor down one line
e move the cursor to the end of the word
b move the cursor to the begining of the word
0 行首
$ 行尾
G 文尾
gg 文首
L move the cursor to the end of the file
:59 跳转到指定(这里是第59)行
`20 `
% Move cursor to matching parenthesis
[[ Jump to function start
[{ Jump to block start

搜索

命令 说明
/word Search word from top to bottom
?word Search word from bottom to top
* Search the word under cursor
/\cstring Search STRING or string, case insensitive
/jo[ha]n Search john or joan
/\< the Search the, theatre or then
/the\> Search the or breathe
/\< the\> Search the
/\< ¦.\> Search all words of 4 letters
/\/ Search fred but not alfred or frederick
/fred|joe Search fred or joe
/\<\d\d\d\d\> Search exactly 4 digits
/^\n\{3} Find 3 empty lines
:bufdo /searchstr/ Search in all open files
bufdo %s/something/somethingelse/g Search something in all the open buffers and replace it withsomethingelse

剪切(删除)

1
2
3
4
5
6
7
#### 删除某行至文件结尾
G # 转到文件结尾
:10,.d # 删除第10行至当前行(文件结尾)

#### 删除10行至20行
20G # 跳转至第20行
:10,.d # 删除第10行至当前行
命令 说明 示例
dd 剪切当前行
ndd n表示大于1的数字,剪切n行
dw 从光标处剪切至一个单子/单词的末尾,包括空格
de 从光标处剪切至一个单子/单词的末尾,不包括空格
d$ 从当前光标剪切到行末
d0 从当前光标位置(不包括光标位置)剪切至行首
d3l 从光标位置(包括光标位置)向右剪切3个字符
d5G 将当前行(包括当前行)至第5行(不包括它)剪切
d3B 从当前光标位置(不包括光标位置)反向剪切3个单词
dH 剪切从当前行至所显示屏幕顶行的全部行
dM 剪切从当前行至命令M所指定行的全部行
dL 剪切从当前行至所显示屏幕底的全部行
n1,n2d 剪切n1到n2行 1,10d
n,$d 剪切从某行开始至文本末尾 8,$d: 删除第8行至末尾

复制

命令 说明 示例
yy 复制当前行
nyy n表示大于1的数字,复制n行
yw 从光标处复制至一个单子/单词的末尾,包括空格
ye 从光标处复制至一个单子/单词的末尾,不包括空格
y$ 从当前光标复制到行末
y0 从当前光标位置(不包括光标位置)复制之行首
y3l 从光标位置(包括光标位置)向右复制3个字符
y5G 将当前行(包括当前行)至第5行(不包括它)复制
y3B 从当前光标位置(不包括光标位置)反向复制3个单词

粘贴

命令 说明
p 小写p代表贴至游标后(下),因为游标是在具体字符的位置上,所以实际是在该字符的后面
P 大写P代表贴至游标前(上)

整行的复制粘贴在游标的上(下)一行,非整行的复制则是粘贴在游标的前(后).

撤销与恢复

  • u 撤销上一步的操作
  • Ctrl+r 恢复上一步被撤销的操作

替换

利用:substitute命令, 可以将指定的字符替换成其他字符. 通常, 我们会使用命令的缩写形式:s, 格式如下:

:[range] s/search/replace/[flags] [count]

其中, range是指定范围, 也就是在那些行做替换. 而后是将字符串from替换成字符串to.

替换标记

默认情况下, 替换命令仅将本行中第一个出现的字符替换成给定字符. 如果我们想要将所有的字符都替换成给定字符, 可以在命令中使用g(global)标记:

:%s/from/to/g

标记(flags)包括:

  • g(global): 将所有的字符都替换成给定字符

  • p(print): 是要求打印所做的改动

  • c(confirm): 是要求在做出改动以前先询问

  • i(ignorecase): 不区分大小写

1
2
3
4
5
6
7
8
9
:1,$ s/Professor/Teacher/gc	# 显示将要做改动的文本并要求确认
replace with Teacher (y/n/a/q/l/^E/^Y)? # 提示
# y Yes:执行这个替换
# n No:取消这个替换
# a All:执行所有替换而不要再询问
# q Quit:退出而不做任何改动
# l Last:替换完当前匹配点后退出
# CTRL-E 向上翻滚一行
# CTRL-Y 向下翻滚一行

指定范围

  • 默认当前行
  • $ : 至文档结尾
  • % : 整个文件

如果没有在命令中指定范围, 那么将只会在当前行进行替换操作. 以下命令将把当前行中的I替换为We. 命令中的/i标记, 用于指定忽略大小写.

:s/I/We/gi

以下命令将文中所有的字符串idiots替换成managers:

:1,$s/idiots/managers/g

通常我们用%指代整个文件作为替换范围:

:%s/search/replace/g

以下命令指定只在第5 - 15行间进行替换:

:5,15s/dog/cat/g

以下命令指定只在当前行在内的以下四行内进行替换:

:s/hello/hi/g4

以下命令指定只在后续9行内进行替换:

:,.+8s/dog/cat/g

你还可以将特定字符做为替换范围。比如,将SQL语句从FROM至分号部分中的所有等号(=)替换为不等号(<>):

:/FORM/,/;/=/<>/g

可视化选择复制

在可视化模式下, 首先选择替换范围, 然后输入 : 进入命令模式, 就可以利用s命令在选中的范围内进行文本替换.

精确替换

在搜索sig时, 也将匹配sig, signature, signing等多个单词. 如果希望精确替换某个单词, 可以使用\<来匹配单词的开头, 并用\>匹配单词的结尾:

:s/\<term\>/replace/gc

多项替换

如果想将单词Kang和Kodos都替换为alien, 那么可以使用 | 进行多项替换:

%s/Kang\|Kodos/alien/gc

变量替换

使用以下命令可以将文字替换为变量的内容:

%s!\~!\= expand($HOME)!g

示例

命令 说明 中文
:%s/old/new/g Replace all occurences of old by new in file 使用 new 替换文件所有出现的 old
:%s/onward/forward/gi Replace onward by forward, case unsensitive 使用 forward 替换掉 onward,大小写不敏感
:%s/old/new/gc Replace all occurences with confirmation
:2,35s/old/new/g Replace all occurences between lines 2 and 35
:5,$s/old/new/g Replace all occurences from line 5 to EOF
:%s/^/hello/g Replace the begining of each line by hello
:%s/$/Harry/g Replace the end of each line by Harry
:%s/onward/forward/gi Replace onward by forward, case unsensitive
:%s/ *$//g Delete all white spaces
:g/string/d Delete all lines containing string
:v/string/d Delete all lines containing which didn’t contain string
:s/Bill/Steve/ Replace the first occurence of Bill by Steve in current line 用Steve替换每一行的第一个Bill
:s/Bill/Steve/g Replace Bill by Steve in current line
:%s/Bill/Steve 使用Steve替换所有行的第一个Bill
:%s/Bill/Steve/g Replace Bill by Steve in all the file
:%s/^M//g Delete DOS carriage returns (^M)
:%s/\r/\r/g Transform DOS carriage returns in returns
:%s#<[^>]\+>##g Delete HTML tags but keeps text
:%s/^\(.*\)\n\1$/\1/ Delete lines which appears twice
Ctrl+a Increment number under the cursor
Ctrl+x Decrement number under cursor
ggVGg? Change text to Rot13

示例

1
2
3
4
:s/Linux/RHEL5		# 指将当前行中的第一个linux换为RHEL5
:s/Linux/RHEL5/g # 指将当前行中所有的linux换为RHEL5
:%s/Linux/RHEL5 # 指将文件中每一行的第一个linux换为RHEL5
:%s/Linux/RHEL5/g # 整个文档范围内的linux换为RHEL5

大小写

命令 说明
Vu Lowercase line
VU Uppercase line
g~~ Invert case
vEU Switch word to uppercase
vE~ Modify word case
ggguG Set all text to lowercase
gggUG Set all text to uppercase
:set ignorecase Ignore case in searches
:set smartcase Ignore case in searches excepted if an uppercase letter is used
:%s/\<./\u&/g Sets first letter of each word to uppercase
:%s/\<./\l&/g Sets first letter of each word to lowercase
:%s/.*/\u& Sets first letter of each line to uppercase
:%s/.*/\l& Sets first letter of each line to lowercase

读写文件

命令 说明
:1,10 w outfile Saves lines 1 to 10 in outfile
:1,10 w >> outfile Appends lines 1 to 10 to outfile
:r infile Insert the content of infile
:23r infile Insert the content of infile under line 23

文件浏览器

命令 说明
:e . Open integrated file explorer
:Sex Split window and open integrated file explorer
:Sex! Same as :Sex but split window vertically
:browse e Graphical file explorer
:ls List buffers
:cd .. Move to parent directory
:args List files
:args *.php Open file list
:grep expression *.php Returns a list of .php files contening expression
gf Open file name under cursor

和 Unix 系统交互

命令 说明
:!pwd Execute the pwd unix command, then returns to Vi
!!pwd Execute the pwd unix command and insert output in file
:sh Temporary returns to Unix
$exit Retourns to Vi

对齐

命令 说明
:%!fmt Align all lines
!}fmt Align all lines at the current position
5!!fmt Align the next 5 lines

Tabs/Windows

命令 说明
:tabnew Creates a new tab
gt Show next tab
:tabfirst Show first tab
:tablast Show last tab
:tabm n(position) Rearrange tabs
:tabdo %s/foo/bar/g Execute a command in all tabs
:tab ball Puts all open files in tabs
:new abc.txt Edit abc.txt in new window

多文件

1

分屏显示

命令 说明
:e filename Edit filename in current window
:split filename Split the window and open filename
ctrl-w up arrow Puts cursor in top window
ctrl-w ctrl-w Puts cursor in next window
ctrl-w_ Maximize current window vertically
ctrl-w| Maximize current window horizontally
ctrl-w= Gives the same size to all windows
10 ctrl-w+ Add 10 lines to current window
:vsplit file Split window vertically
:sview file Same as :split in readonly mode
:hide Close current window
:­nly Close all windows, excepted current
:b 2 Open #2 in this window

调整窗口大小

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
## 高度
:resize 60
:res 60
:res +5
:res -5

## 宽度
:vertical resize 80
:vertical resize +5
:vertical resize -5

## hot key
nnoremap <silent> <Leader>+ :exe "resize " . (winheight(0) * 3/2)<CR>
nnoremap <silent> <Leader>- :exe "resize " . (winheight(0) * 2/3)<CR>

## ps
default <Leader> is \

自动完成

命令 说明
Ctrl+n Ctrl+p (in insert mode) Complete word
Ctrl+x Ctrl+l Complete line
:set dictionary=dict Define dict as a dictionnary
Ctrl+x Ctrl+k Complete with dictionnary

Marks

命令 说明
m {a-z} Marks current position as {a-z}
' {a-z} Move to position {a-z}
'' Move to previous position

缩写

命令 说明
:ab mail mail@provider.org Define mail as abbreviation of mail@provider.org

文本缩进

缩进多行

  • 按v进入visual状态,选择多行,用>或<缩进或缩出.
命令 说明
:set autoindent Turn on auto-indent
:set smartindent Turn on intelligent auto-indent
:set shiftwidth=4 Defines 4 spaces as indent size
ctrl-t, ctrl-d Indent/un-indent in insert mode
>> Indent
<< Un-indent
=% Indent the code between parenthesis
1GVG= Indent the whole file
1
2
3
4
5
6
7
8
9
# 缩进指定行
# In command mode
5>>

# In Visual Mode
j # 下一行
j # 下一行
j # 下一行
> # 缩进

语法高亮

命令 说明
:syntax on Turn on syntax highlighting
:syntax off Turn off syntax highlighting
:set syntax=perl Force syntax highlighting

重复执行

  • .: 命令可以重复上次普通命令
  • @: 重复上次ex命令。
  • @@: 重复执行。

参考

示例配置

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
121
122
123
124
125
126
127
128
129
130
"设置语言为英文
let $LANG = 'en' "set message language
set langmenu=en "set menu's language of gvim. no spaces beside '='

"兼容性设置
set nocompatible
set backspace=indent,eol,start

"if has("win32")
" set encoding=gbk
" set fileencoding=ansi
" set termencoding=gbk
"else
" set encoding=utf-8
" set fileencoding=ansi
" set termencoding=utf-8
"endif

:filetype plugin indent on "打开文件类型检测
set showcmd "在Vim窗口的右下角显示一个完整的命令已经完成的部分
set completeopt=longest,menu "打开文件类型检测, 加了这句才可以用智能补全
set mouse=a "使用鼠标
set selection=exclusive
set selectmode=mouse,key
set tabstop=4 "设置tab
set softtabstop=4
set shiftwidth=4
syntax on "语法高亮
set showcmd "显示输入的命令
set autoindent "自动缩进
set cindent
set smartindent
set showmatch "高亮显示匹配的括号
set number "显示行号
colorscheme desert "设置配色方案
set guifont=consolas "设置字体
"set guifont=Courier_New:h12:b "设置字体示例
set history=1000 "设置历史
"设置状态栏
set laststatus=2
set statusline=%F%m%r%h%w\ [FORMAT=%{&ff}]\ [TYPE=%Y]\[POS=%l,%v][%p%%]\ %{strftime(\"%d/%m/%y\ -\ %H:%M\")}

autocmd GUIEnter * simalt ~x "窗口最大化



"自定义函数 - 新建文件时自动添加头部信息或者按F10添加头信息
"新建.c,.h,.sh,.java文件,自动插入文件头
autocmd BufNewFile *.cpp,*.[ch],*.sh,*.java exec ":call AppendHeadInfo()"
map<F10> :call AppendHeadInfo()<CR>
"定义函数AppendHeadInfo,自动插入文件头
func AppendHeadInfo()
"如果文件类型为.sh文件
let filename=expand("%")
let len=strlen(filename)
let posPoint=strridx(filename,'.')
let fileformat=strpart(filename,posPoint+1,len)
if fileformat == 'sh'
call setline(1,"\#########################################################################")
call append(line("."), "\# File Name: ".expand("%"))
call append(line(".")+1, "\# Author: bovenson")
call append(line(".")+2, "\# Email: szhkai@126.com")
call append(line(".")+3, "\# Created Time: ".strftime("%Y-%m-%d %H:%M:%S"))
call append(line(".")+4, "\#########################################################################")
call append(line(".")+5, "\#!/bin/bash")
call append(line(".")+6, "")
else
call setline(1, "\/*************************************************************************")
call append(line("."), " > File Name: ".expand("%"))
call append(line(".")+1, " > Author: bovenson")
call append(line(".")+2, " > Email: szhkai@126.com")
call append(line(".")+3, " > Created Time: ".strftime("%Y-%m-%d %H:%M:%S"))
call append(line(".")+4, " ************************************************************************/")
call append(line(".")+5, "")
endif
if fileformat == 'cpp'
call append(line(".")+6, "#include <iostream>")
call append(line(".")+7, "using namespace std;")
call append(line(".")+8, "")
call append(line(".")+9, "int main()")
call append(line(".")+10, "{")
call append(line(".")+11, " return 0;")
call append(line(".")+12, "}")
call append(line(".")+13, "")
endif
if fileformat == 'c'
call append(line(".")+6, "#include <stdio.h>")
call append(line(".")+7, "")
call append(line(".")+8, "int main()")
call append(line(".")+9, "{")
call append(line(".")+10, " return 0;")
call append(line(".")+11, "}")
call append(line(".")+12, "")
endif
"if &filetype == 'java'
" call append(line(".")+6,"public class ".expand("%")." {")
" call append(line(".")+7,"}")
"endif
"新建文件后,自动定位到文件末尾
autocmd BufNewFile * normal G
endfunc

"自定义函数 - 程序的一键运行与调试
"按F5编译运行
map <F5> :call CompileRunGcc()<CR>
func! CompileRunGcc()
exec "w"
if &filetype == 'c'
exec "!g++ % -o %<"
exec "! ./%<"
elseif &filetype == 'cpp'
exec "!g++ % -o %<"
exec "! ./%<"
elseif &filetype == 'java'
exec "!javac %"
exec "!java %<"
elseif &filetype == 'sh'
:!./%
elseif &filetype == 'py'
exec "!python %"
exec "!python %<"
endif
endfunc
"C,C++的调试
map <F8> :call Rungdb()<CR>
func! Rungdb()
exec "w"
exec "!g++ % -g -o %<"
exec "!gdb ./%<"
endfunc

go 工程

Layout

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
.
├── api # 符合 OpenAPI/Swagger 规范的服务接口
├── assets # 项目中使用的其他资源,如媒体资源等
├── build # 打包和持续集成; 将容器、包(deb、rpm、pkg)、脚本等放在 build/package; ci 放在 build/ci
├── cmd # 项目主要应用程序; 如, cmd/app/main.go & cmd/tool/main.go
├── configs # 配置文件
├── deployments # Iaas,Paas,系统和容器编排部署配置和模板
├── docs # 设计和用户文档
├── examples # 样例
├── init # 系统初始化(systemd、upstart、sysv) 及进程管理(runit、supervisord)配置
├── internal # 内部代码,不希望被他人导入的代码
├── pkg # 外部应用程序可以使用的库代码
├── scripts # 用于执行构建、安装等操作的脚本
├── test # 外部测试应用程序和测试数据
├── third_party # 外部辅助工具
├── tools # 此项目支持的工具
├── vendor # 应用程序依赖关系
├── web # Web 应用程序的特定组件, 静态资源、前端模板等
├── website # github pages / 网站数据
├── Makefile # 打包
├── go.mod # 模块信息
├── LICENSE.md
└── README.md

Start

1
$ go mod init github.com/sunzhenkai/go-hello-world

添加依赖

1
$ go get github.com/*/*

Reference

go startup

[TOC]

安装

1
2
# mac
$ brew install go

编译

1
2
3
4
5
$ go build main.go
$ go build -o output-file-name main.go

# 打印 gc 信息
$ go build -gcflags="-m" main.go

示例

代码

1
2
3
4
5
6
7
package main

import "fmt"

func main() {
fmt.Println("Hello World!")
}

运行

1
2
$ go run hello.go
Hello World!

编译

1
$ go build hello.go

添加依赖库实例

1
# main.go

依赖

使用 go get 下载公开库,该命令会把依赖下载至第一个 GOPATH 下的 src 目录下。

参数 说明
-v 打印详情日志
-d 只下载不安装
-u 下载丢失的包,不更新

示例

1
2
$ go get [dep-name]				# 安装单个依赖
$ go get -d -v ./... # 递归地下载当前目录下所有文件的依赖

Package

报名尽量使用单个词。

1
package http;

私有库

terminal prompts disabled

1
2
3
4
go: gitlab.company.com/org/pkg@v0.0.1: reading https://goproxy.cn/gitlab.company.com/org/pkg/@v/v0.0.1.mod: 404 Not Found
server response:
not found: gitlab.company.com/org/pkg@v0.0.1: invalid version: git fetch -f origin refs/heads/*:refs/heads/* refs/tags/*:refs/tags/* in /tmp/gopath/pkg/mod/cache/vcs/1b2a69e43fbd284ebef999cca485d367b743c300d2970b093def252bae54d3ef: exit status 128:
fatal: could not read Username for 'http://gitlab.company.com': terminal prompts disabled

私有项目,默认走 goproxy,故找不到 pkg。

1
2
3
4
5
# 设置 pkg 路径为私有库
go env -w GOPRIVATE="gitlab.company.com/org"

# get
GIT_TERMINAL_PROMPT=1 go get

或者使用 ssh 认证。

1
git config --global --add url."git@your-repo.com:".insteadOf "https://your-repo.com/"

Env

1
2
3
4
5
6
7
8
9
10
11
# 查看 go env
$ go env
GO111MODULE=""
GOARCH="amd64"
...

# 设置
$ go env -w GOPROXY="..."

# 取消设置
$ go env -u GOPROXY

Proxy

1
2
3
4
5
# 设置 proxy
$ go env -w GOPROXY="..."

# 不使用 proxy 的仓库
$ go env -w GOPRIVATE "git.a.com,git.b.com,..."

Mod

1
2
3
4
5
6
7
8
# 下载依赖
go mod download

# 整理依赖
go mod tidy

# 清楚缓存(删除所有下载的库,谨慎操作)
go clean -modcache

参考