01-basic
cmake学习项目github地址
A-hello-cmake
-
CMakelist.txt存放了所有的cmake命令,makefile是由CMakelist.txt生成的 -
CMakelist.txt里面一般包含这几个基本的命令 cmake_minimum_required(VERSION 3.5) #指定了运行此文件所需要的最小cmake版本
project (hello_cmake) #指定项目名
add_executable(hello_cmake main.cpp) #从指定的源文件构建可执行文件,在本例中为main.cpp。add_executable()函数的第一个参数是要构建的可执行文件的名称,第二个参数是要编译的源文件列表
-
运行cmake命令的根目录叫做CMAKE_BINARY_DIR,是所有二进制文件的根文件夹。CMake支持就地和外部构建来生成二进制文件 -
就地构建就是直接在CMakelist.txt所在目录构建,外部构建一般就是创建一个build目录,然后去build目录中构建(一般推荐这种方式)
B-hello-headers
-
项目结构 B-hello-headers$ tree
.
├── CMakeLists.txt
├── include
│ └── Hello.h
└── src
├── Hello.cpp
└── main.cpp
-
cmake的一些目录变量
变量 | 描述 |
---|
CMAKE_SOURCE_DIR | 根源目录 | CMAKE_CURRENT_SOURCE_DIR | 如果使用子项目和目录,则为当前源目录。 | PROJECT_SOURCE_DIR | 当前 cmake 项目的源目录。 | CMAKE_BINARY_DIR | 根二进制/构建目录,这是运行 cmake 命令的目录。 | CMAKE_CURRENT_BINARY_DIR | 当前所在的构建目录。 | PROJECT_BINARY_DIR | 当前项目的构建目录。 |
-
CMakelist.txt cmake_minimum_required(VERSION 3.5)
project (hello_headers)
set(SOURCES #创建SOURCE变量
src/Hello.cpp
src/main.cpp
)
add_executable(hello_headers ${SOURCES})
# 给hello_header设置头文件
# 这样在执行g++编译命令时会带上-I/directory/path,这个命令会指定头文件的路径
target_include_directories(hello_headers
PRIVATE
${PROJECT_SOURCE_DIR}/include
)
C-static-library
-
项目结构 $ tree
.
├── CMakeLists.txt
├── include
│ └── static
│ └── Hello.h
└── src
├── Hello.cpp
└── main.cpp
-
CMakelist.txt cmake_minimum_required(VERSION 3.5)
project(hello_library)
#将Hello.cpp制作成静态库
add_library(hello_library STATIC
src/Hello.cpp
)
# 给这个库设置头文件,让编译器可以找到
# 下面设置成PUBLIC使得所有链接这个库的文件在编译时,编译器都可以在这个include目录里面去找头文件
# 除了PUBLIC,还要PRIVATE(只能编译这个库时去这个头文件目录找)和INTERFACE(只有在被链接时,去这个头文件目录找)
target_include_directories(hello_library
PUBLIC
${PROJECT_SOURCE_DIR}/include
)
add_executable(hello_binary
src/main.cpp
)
# 指定链接这个静态库
target_link_libraries( hello_binary
PRIVATE
hello_libiary
)
-
补充知识,gcc编译时的头文件搜索路径,参考gcc 头文件搜索路径
D-shared-library
-
项目结构 $ tree
.
├── CMakeLists.txt
├── include
│ └── shared
│ └── Hello.h
└── src
├── Hello.cpp
└── main.cpp
-
将Hello做成动态链接库,然后链接到main中。 -
CMakelist.txt cmake_minimum_required(VERSION 3.5)
project(hello_library)
add_library(hello_library SHARED
src/Hello.cpp
)
add_library(hello::library AIAS hello_library) #给hello_library起别名hello:library
target_include_directories(hello_library
${PROJECT_SRC_DIR}/include
)
#创建可执行文件
add_execuable(hello_binary
src/main.cpp
)
target_link_libraries(hello_binary
PRIVATE
hello::library
)
E-installing
-
项目结构 $ tree
.
├── cmake-examples.conf
├── CMakeLists.txt
├── include
│ └── installing
│ └── Hello.h
├── README.adoc
└── src
├── Hello.cpp
└── main.cpp
-
cmake提供对于make install的支持,默认的安装路径由CMAKE_INSTALL_PREFIX 指定,可以使用cmake .. -DCMAKE_INSTALL_PREFIX=/install/location 来设置,Linux默认安装目录是/usr/local/下面(这个目录就是用来安装一些用户软件之类的) -
CMakelist.txt cmake_minimum_required(VERSION 3.5)
project(cmake_examples_install)
############################################################
# Create a library
############################################################
#Generate the shared library from the library sources
add_library(cmake_examples_inst SHARED
src/Hello.cpp
)
target_include_directories(cmake_examples_inst
PUBLIC
${PROJECT_SOURCE_DIR}/include
)
############################################################
# Create an executable
############################################################
# Add an executable with the above sources
add_executable(cmake_examples_inst_bin
src/main.cpp
)
# link the new hello_library target with the hello_binary target
target_link_libraries( cmake_examples_inst_bin
PRIVATE
cmake_examples_inst
)
############################################################
# Install
############################################################
#将cmake_examples_inst_bin安装到${CMAKE_INSTALL_PREFIX}/bin目录下
#这里${CMAKE_INSTALL_PREFIX}默认就是/usr/local
install (TARGETS cmake_examples_inst_bin
DESTINATION bin)
# Library
# 此处在windows上可以无法运行
# 将cmake_examples_inst安装到${CMAKE_INSTALL_PREFIX}/lib目录下
install (TARGETS cmake_examples_inst
LIBRARY DESTINATION lib)
#安装头文件
install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/
DESTINATION include)
# 安装配置文件
install (FILES cmake-examples.conf
DESTINATION etc)
-
安装完成后,在build目录中运行LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib cmake_examples_inst_bin ,指定去LD_LIBRARY_PATH环境变量下寻找动态链接库文件,不过不设置环境变量。 -
补充知识,Linux动态链接库的默认搜索路径 (1) 编译目标代码时指定的动态库搜索路径(-Wl,-rpath 指定); (2) 环境变量LD_LIBRARY_PATH指定的动态库搜索路径; (3) 配置文档/etc/ld.so.conf中指定的动态库搜索路径; (4) 默认的动态库搜索路径/lib; (5) 默认的动态库搜索路径/usr/lib.
更改默认安装位置
-
下面的代码可以用来更改默认安装位置CMAKE_INSTALL_PREFIX if( CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT )
message(STATUS "Setting default CMAKE_INSTALL_PREFIX path to ${CMAKE_BINARY_DIR}/install")
set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/install" CACHE STRING "The path to use for make install" FORCE)
endif()
-
DESTDIR的使用 GNU Make中,有许多约定俗成的东西,比如这个DESTDIR:用于加在要安装的文件路径前的一个前缀变量。 比如,我们本地编译了一个第三方库,但需要对其打包发布给其他人使用,一方面如果我们安装到默认目录,比如/usr,这时,安装后的文件一但数量很大,则打包时很难找全;或者我们在configure时指定了–prefix,或cmake时指定了CMAKE_INSTALL_PREFIX,则pc文件内的编译依赖关系又会出错,变成了我们指定的那个路径,使用起来会很不方便。此时,DESTDIR就会派上用场。 DESTDIR只在make install时起作用,且和Makefile是由什么工具生成的没有关系,用法如下:
make install DESTDIR=/tmp/stage
在configure或cmake时,指定了要安装的路径后,以这种方式make install安装的文件会通通安装到以$CUSTOM_PREFIX为前缀的目录中(比如这个例子就是/tmp/stage/usr/local),这样,开发者直接对这目录中的文件打包,即可发布使用。 $ tree /tmp/stage
/tmp/stage
└── usr
└── local
├── bin
│ └── cmake_examples_inst_bin
├── etc
│ └── cmake-examples.conf
└── lib
└── libcmake_examples_inst.so
卸载
- 默认情况下,CMake 不提供"make uninstall"。我们不希望"make uninstall"从系统中删除有用的文件。如果你想在你的项目中有一个"uninstall"目标,那么没有人会阻止你提供一个。你需要删除 install_manifest.txt 文件中列出的文件。
对于非Unix用户
-
首先在项目顶层目录下创建文件cmake_uninstall.cmake.in : if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt")
message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt")
endif()
file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files)
string(REGEX REPLACE "\n" ";" files "${files}")
foreach(file ${files})
message(STATUS "Uninstalling $ENV{DESTDIR}${file}")
if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
exec_program(
"@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
OUTPUT_VARIABLE rm_out
RETURN_VALUE rm_retval
)
if(NOT "${rm_retval}" STREQUAL 0)
message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}")
endif()
else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
message(STATUS "File $ENV{DESTDIR}${file} does not exist.")
endif()
endforeach()
-
然后在 CMakeLists.txt中加入以下代码,这下你就创建了"uninstall"目标 # uninstall target
if(NOT TARGET uninstall)
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
IMMEDIATE @ONLY)
add_custom_target(uninstall
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
endif()
对于Unix用户
- 对于Unix用户在shell中输入
xargs rm < install_manifest.txt 进行卸载
F-build-type
-
CMake有很多内置的构建配置,可以用来编译你的项目。它们指定了优化级别,以及是否将调试信息包含在二进制文件中。 所提供的级别是:
-
Release——在编译器中添加-O3 -DNDEBUG 标志 -
Debug——添加-g标志 -
MinSizeRel——添加-Os -DNDEBUG -
RelWithDebInfo——添加-O2 -g -DNDEBUG 标志 -
CMakelist.txt cmake_minimum_required(VERSION 3.5)
# 设置默认build类型
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
message("Setting build type to 'RelWithDebInfo' as none was specified.")
set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Choose the type of build." FORCE)
# Set the possible values of build type for cmake-gui
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release"
"MinSizeRel" "RelWithDebInfo")
endif()
# Set the project name
project (build_type)
# Add an executable
add_executable(cmake_examples_build_type main.cpp)
E-compile-flags
- cmake支持设置编译标志(flags)
- 使用
target_compile_definitions() - 使用变量
CMAKE_C_FLAGS 和CMAKE_CXX_FLAGS
设置 每个目标的C++ Flags
设置默认C++ Flags
-
默认的CMAKE_CXX_FLAGS要么为空,要么包含构建类型(build type)的一些标志。要设置额外的默认编译标志,你可以在你的顶层CMakeLists.txt中添加以下内容: set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DEX2" CACHE STRING "Set C++ Compiler Flags" FORCE)
-
上述命令中的值CACHE STRING “Set c++ Compiler Flags” FORCE用于强制在CMakeCache.txt文件中设置该变量,详见https://cmake.org/cmake/help/v3.0/command/set.html -
同样地,可以使用CMAKE_C_FLAGS设置C编译器标志,使用CMAKE_LINKER_FLAGS设置链接器标志
设置cmake标志
cmake .. -DCMAKE_CXX_FLAGS="-DEX3"
CMakelist.txt
cmake_minimum_required(VERSION 3.5)
# Set a default C++ compile flag
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DEX2" CACHE STRING "Set C++ Compiler Flags" FORCE)
# Set the project name
project (compile_flags)
# Add an executable
add_executable(cmake_examples_compile_flags main.cpp)
#设置编译标志
target_compile_definitions(cmake_examples_compile_flags
PRIVATE EX3
)
H-third-party-library
- 这一部分主要是查找并链接第三方库。几乎所有重要的项目都要求包含第三方库、头文件或程序。CMake支持使用
find_package() 函数找到这些第三方库的路径,这个函数将以FindXXX.cmake 的格式从CMAKE_MODULE_PATH 的文件夹列表中搜索CMake模块。在linux上,默认的搜索路径将包括/usr/share/cmake/Modules,在我的系统上,这包括对大约142个公共第三方库的支持。
寻找Package
find_package(Boost 1.46.1 REQUIRED COMPONENTS filesystem system)
- 参数解释:
- Boost——库的名字,
FindXXX.cmake 这里的XXX就是Boost,即FindBoost.cmake 。如果系统上安装了Boost这个第三方库,进入/usr/share/cmake/Modules目录里面,就会发现FindBoost.cmake 这个文件。 - 1.46.1——要求所查找的Boost的最低版本
- REQUIRED——说明这个模块是必需的,如果找不到就会失败。
- COMPONENTS——要在库中查找的组件列表,这里就是filesystem和system。
检测是否找到了这个包
- 大多数包含的包将设置一个变量
XXX_FOUND ,该变量可用于检查该包在系统上是否可用(这个例子里就是Boost_FOUND )
if(Boost_FOUND)
message ("boost found")
include_directories(${Boost_INCLUDE_DIRS})
else()
message (FATAL_ERROR "Cannot find Boost")
endif()
导出变量
- 在找到一个包之后,它通常会导出一些变量,这些变量可以告诉用户在哪里可以找到库、头文件或可执行文件。与
XXX_FOUND 变量类似,它们是特定于包的,通常在FindXXX.cmake 的顶部进行记录。这个例子导出了Boost_INCLUDE_DIRS (指示boost头文件的路径)
别名(Alias)和导入目标
-
大多数现代的CMake库在它们的模块文件中导入别名目标。导入目标的好处是,它们还可以填充include目录和链接库。 -
例如,从CMake的3.5+版本开始,Boost模块就支持这一点。与为库使用自己的ALIAS目标类似(参见前面的D-shared-library),模块中的ALIAS可以使引用找到的目标变得更容易。在Boost的情况下,使用Boost::标识符导出所有目标,然后使用子系统的名称。例如,你可以使用:
Boost::boost 仅使用头文件Boost::system 使用boost system库Boost::filesystem 使用filesystem库 -
和之前使用自己定义的shared-library类似,这些库也是有它们的依赖项的,链接这些库也会自动添加这些依赖项(和shared-library中自动继承那些链接库包含的头文件类似)。
不使用别名(Alias)
-
虽然大多数现代库使用导入的目标,但并不是所有的模块都更新了。在库没有更新的情况下,你通常会发现以下可用变量:
xxx_INCLUDE_DIRS ——指向库的包含目录的变量。xxx_LIBRARY ——指向库路径的变量 这些可以添加到你的target_include_directories 和target_link_libraries
target_include_directories( third_party_include
PRIVATE ${Boost_INCLUDE_DIRS}
)
target_link_libraries( third_party_include
PRIVATE
${Boost_SYSTEM_LIBRARY}
${Boost_FILESYSTEM_LIBRARY}
)
CMakelist.txt
cmake_minimum_required(VERSION 3.5)
# Set the project name
project (third_party_include)
# 查找Boost库
find_package(Boost 1.46.1 REQUIRED COMPONENTS filesystem system)
# 判断查找成功没有
if(Boost_FOUND)
message("boost found.")
else()
message(FATAL_ERROR"Cannot find Boost")
endif()
# 添加可执行文件
add_executable(third_patry_include main.cpp)
# 链接这个第三方库
target_link_libraries(third_patry_include
PRIVATE:
Boost::filesystem
)
I-Compiling with clang
- 当使用cmake进行构建时,可以设置C或C++编译器,这个例子和第一个hello-cmake的例子很相似,只不过这个例子展示了将默认的gcc编译器变为clang的基本方法。
编译选项
设置flags
- 下面这段命令指定了使用clang和clang++来编译
cmake .. -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
J-building-with-ninja
- 如前所述,CMake是一个元构建系统(meta-build system),可以用来为许多其他构建工具创建构建文件,这个例子展示了如何让CMake使用ninja构建工具。
生成器(Generators)
-
CMake生成器负责为底层构建系统编写输入文件(例如Makefiles),运行cmake --help 将显示可用的生成器。对于cmake v2.8.12.2,我的系统支持的生成器包括: Generators
The following generators are available on this platform:
Unix Makefiles = Generates standard UNIX makefiles.
Ninja = Generates build.ninja files (experimental).
CodeBlocks - Ninja = Generates CodeBlocks project files.
CodeBlocks - Unix Makefiles = Generates CodeBlocks project files.
Eclipse CDT4 - Ninja = Generates Eclipse CDT 4.0 project files.
Eclipse CDT4 - Unix Makefiles
= Generates Eclipse CDT 4.0 project files.
KDevelop3 = Generates KDevelop 3 project files.
KDevelop3 - Unix Makefiles = Generates KDevelop 3 project files.
Sublime Text 2 - Ninja = Generates Sublime Text 2 project files.
Sublime Text 2 - Unix Makefiles
= Generates Sublime Text 2 project files.Generators
命令行(Command-Line)构建工具生成器
- 这些生成器用于命令行构建工具,比如Make和Ninja。选择的工具链必须在用CMake生成构建系统之前配置好。
- 支持的生成器包括:
- Borland Makefiles
- MSYS Makefiles
- MinGW Makefiles
- NMake Makefiles
- NMake Makefiles JOM
- Ninja
- Unix Makefiles
- Watcom WMake
IDE构建工具生成器
- 这些生成器适用于集成开发环境(IDE),其中包含自己的编译器。例如,Visual Studio和Xcode就包含了一个本地编译器。
- 支持的生成器包括:
- Visual Studio 6
- Visual Studio 7
- Visual Studio 7 .NET 2003
- Visual Studio 8 2005
- Visual Studio 9 2008
- Visual Studio 10 2010
- Visual Studio 11 2012
- Visual Studio 12 2013
- Xcode
其他生成器
- 这些生成器创建了一个配置,用于使用另一个IDE工具,并且必须包含在IDE或命令行生成器中。
- 支持的生成器包括:
- CodeBlocks
- CodeLite
- Eclipse CDT4
- KDevelop3
- Kate
- Sublime Text 2
调用一个生成器
K-impoerted-targets
- 正如前面在第三方库(H-third-party-library)中提到的,更新版本的CMake允许使用导入的ALIAS目标链接第三方库。
导入目标
-
导入的目标是由FindXXX模块导出的只读目标。要包含boost文件系统,你可以这样做: target_link_libraries(imported_targets
PRIVATE
Boost::filesystem
)
-
这将自动链接Boost::filesystem和Boost::system库,同时也包含Boost include目录
L-cpp-standard
- 由于c++ 11和c++ 14的发布,一个常见的用例是调用编译器来使用这些标准。随着CMake的发展,它增加了一些特性来简化这一过程,而CMake的新版本改变了实现这一过程的方式。下面的例子展示了设置c++标准的不同方法,以及它们可以使用的CMake版本。例子包括:
i-comm-method
- 这个例子展示了一个设置c++标准的常用方法。这可以用于大多数版本的CMake。但是,如果你的目标是CMake的最新版本,有更方便的方法可用。
检查编译flags
-
CMake支持使用传入CMAKE_CXX_COMPILER_FLAG函数的任何标志来编译程序,然后将结果存储在传入的变量中,例如: include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
这个例子将会尝试用标志"-std=c++11"编译并将结果存在变量COMPILER_SUPPORTS_CXX11 中。include(CheckCXXCompilerFlag) 告诉CMake包含这个函数以使其可用。
添加flag
-
一旦你确定编译是否支持一个标志,你就可以使用标准的cmake方法将这个标志添加到目标中。在本例中,我们使用CMAKE_CXX_FLAGS将该标志传播到所有目标。 if(COMPILER_SUPPORTS_CXX11)#
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
elseif(COMPILER_SUPPORTS_CXX0X)#
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
else()
message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
endif()
-
上面的例子只检查编译标志的gcc版本,并支持从c++11回退到标准化前的c++0x标志。在实际使用中,你可能想检查C14,或者添加对设置编译的不同方法的支持,例如-std=gnu11 -
CMakelist.txt # Set the minimum version of CMake that can be used
# To find the cmake version run
# $ cmake --version
cmake_minimum_required(VERSION 2.8)
# Set the project name
project (hello_cpp11)
# try conditional compilation
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
# check results and add flag
if(COMPILER_SUPPORTS_CXX11)#
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
elseif(COMPILER_SUPPORTS_CXX0X)#
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
else()
message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
endif()
# Add an executable
add_executable(hello_cpp11 main.cpp)
ii-cxx-standard
-
这个例子展示了如何使用CMAKE_CXX_STANDARD 变量来设置c++标准,这是从CMake 3.1版本开始的。 -
设置CMAKE_CXX_STANDARD 变量会使得在所有目标上使用CXX_STANDARD 属性。这将使得CMake在编译时设置适当的标志。
CMAKE_CXX_STANDARD 变量会匹配最合适的标准,而不报错。例如,如果你请求-std=gnu11 ,你可能会得到-std=gnu0x 。这可能会在编译时导致意外的失败。 -
CMakelist.txt # Set the minimum version of CMake that can be used
# To find the cmake version run
# $ cmake --version
cmake_minimum_required(VERSION 3.1)
# Set the project name
project (hello_cpp11)
# set the C++ standard to C++ 11
set(CMAKE_CXX_STANDARD 11)
# Add an executable
add_executable(hello_cpp11 main.cpp)
iii-compile-features
-
这个例子展示了如何使用target_compile_features 函数来设置c++标准。这是从CMake 3.1版本开始的。 -
在目标上调用target_compile_features 函数将查看传入的特性,并确定要为目标使用的正确编译器标志。 target_compile_features(hello_cpp11 PUBLIC cxx_auto_type)
-
与其他target_* 函数一样,您可以为所选目标指定特性的作用域范围。这将为目标填充INTERFACE_COMPILE_FEATURES 属性。 -
可用特性的列表可以从CMAKE_CXX_COMPILE_FEATURES 变量中找到。你可以使用以下代码获得可用特性的列表: message("List of compile features: ${CMAKE_CXX_COMPILE_FEATURES}")
-
CMakelist.txt # Set the minimum version of CMake that can be used
# To find the cmake version run
# $ cmake --version
cmake_minimum_required(VERSION 3.1)
# Set the project name
project (hello_cpp11)
# Add an executable
add_executable(hello_cpp11 main.cpp)
# set the C++ standard to the appropriate standard for using auto
target_compile_features(hello_cpp11 PUBLIC cxx_auto_type)
# Print the list of known compile features for this version of CMake
message("List of compile features: ${CMAKE_CXX_COMPILE_FEATURES}")
|