├── .gitignore ├── 01-introduction.md ├── 02-Install.md ├── 03-getting-started.md ├── 04-using-package.md ├── 05-creating-packages.md ├── 06-uploading-packages.md ├── 07-developing-packages.md ├── 08-package-apps-and-devtools.md ├── 09-versioning.md ├── 10-master-conan.md ├── 11-systems-and-cross-building.md ├── 12-extending-conan.md ├── 13-integrations.md ├── 14-confituration.md ├── 15-howtos.md ├── 16-reference.md ├── README.md ├── SUMMARY.md ├── _book ├── .gitignore ├── 01-introduction.html ├── 02-Install.html ├── 03-getting-started.html ├── 04-using-package.html ├── 05-creating-packages.html ├── 06-uploading-packages.html ├── 07-developing-packages.html ├── 08-package-apps-and-devtools.html ├── 09-versioning.html ├── 10-master-conan.html ├── 11-systems-and-cross-building.html ├── 12-extending-conan.html ├── 13-integrations.html ├── 14-confituration.html ├── 15-howtos.html ├── 16-reference.html ├── gitbook.md ├── gitbook │ ├── fonts │ │ └── fontawesome │ │ │ ├── FontAwesome.otf │ │ │ ├── fontawesome-webfont.eot │ │ │ ├── fontawesome-webfont.svg │ │ │ ├── fontawesome-webfont.ttf │ │ │ ├── fontawesome-webfont.woff │ │ │ └── fontawesome-webfont.woff2 │ ├── gitbook-plugin-ancre-navigation │ │ ├── lib │ │ │ ├── config.js │ │ │ ├── log.js │ │ │ └── plugin.js │ │ └── style │ │ │ └── plugin.css │ ├── gitbook-plugin-code │ │ ├── plugin.css │ │ └── plugin.js │ ├── gitbook-plugin-fontsettings │ │ ├── fontsettings.js │ │ └── website.css │ ├── gitbook-plugin-lunr │ │ ├── lunr.min.js │ │ └── search-lunr.js │ ├── gitbook-plugin-multipart │ │ └── multipart.css │ ├── gitbook-plugin-prism │ │ ├── prism-coy.css │ │ ├── prism-dark.css │ │ ├── prism-funky.css │ │ ├── prism-okaidia.css │ │ ├── prism-solarizedlight.css │ │ ├── prism-tomorrow.css │ │ ├── prism-twilight.css │ │ └── prism.css │ ├── gitbook-plugin-search-pro │ │ ├── jquery.mark.min.js │ │ ├── search.css │ │ └── search.js │ ├── gitbook-plugin-sharing-plus │ │ └── buttons.js │ ├── gitbook-plugin-splitter │ │ ├── splitter.css │ │ └── splitter.js │ ├── gitbook-plugin-tbfed-pagefooter │ │ └── footer.css │ ├── gitbook-plugin-toggle-chapters │ │ ├── toggle.css │ │ └── toggle.js │ ├── gitbook.js │ ├── images │ │ ├── apple-touch-icon-precomposed-152.png │ │ └── favicon.ico │ ├── style.css │ └── theme.js ├── images │ ├── conan-artifactory_ce.png │ ├── conan-binary_mgmt.png │ ├── conan-build_once.png │ ├── conan-info_deps_html_graph.png │ ├── conan-install_flow.png │ ├── conan-local_cache_cmake.png │ ├── conan-multi_conf_packages.png │ ├── conan-package_create_flow.png │ ├── conan-single_conf_packages.png │ ├── conan-systems.png │ └── jfrog_conan_logo.png ├── index.html ├── search_index.json └── search_plus_index.json ├── book.json ├── gitbook.md └── images ├── conan-artifactory_ce.png ├── conan-binary_mgmt.png ├── conan-build_once.png ├── conan-info_deps_html_graph.png ├── conan-install_flow.png ├── conan-local_cache_cmake.png ├── conan-multi_conf_packages.png ├── conan-package_create_flow.png ├── conan-single_conf_packages.png ├── conan-systems.png └── jfrog_conan_logo.png /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | .DS_Store -------------------------------------------------------------------------------- /01-introduction.md: -------------------------------------------------------------------------------- 1 | # Conan介绍 2 | 3 | [Conan](https://docs.conan.io/en/latest/introduction.html)是一个可以帮C/C++进行依赖管理的包管理器。它是免费、开源、跨平台的。目前支持Windows, Linux, OSX, FreeBSD, Solaris等平台。同时也支持嵌入式、移动端(IOS,Andriod)、或者直接基于裸机之上的目标程序开发。它当前支持各种构件系统,例如CMake,Visual Studio(MSBuild),Makefiles,Scons等等。 4 | 5 | Conan是分布式的,它允许运行自己私有的包管理器托管自己私有的包和二进制文件。Conan是基于二进制管理的,它可以为包生成各种不同配置、不同体系架构或者编译器版本的二进制文件。 6 | 7 | Conan相对比较成熟和稳定,有一个全职团队在维护它。Conan保证前向兼容,社区相对成熟,从开源到商业公司都有使用。它有一个官方的中央仓 [ConanCenter](https://conan.io/center/)。 8 | 9 | Conan的源码遵循 MIT license,位于[github : https://github.com/conan-io/conan](https://github.com/conan-io/conan)。 10 | 11 | ## 分布式的包管理器 12 | 13 | Conan是分布式的,遵循C/S架构。客户端可以从不同的远端server上获取或上传包。和git的`git pull`和`git push`类似。 14 | 15 | Conan的服务端主要负责包的存储,并不构建和产生包。包产生于Conan的客户端,包括包中的二进制也是在客户端编译而成。 16 | 17 | ![conan system](./images/conan-systems.png) 18 | 19 | 上图描述了conan的主要组成: 20 | 21 | - Conan Client:Conan的客户端。它是一个基于命令行的程序,支持包的创建和使用。Conan客户端有一个包的本地缓存,因此你可以完全离线的创建和测试和使用本地的包。 22 | 23 | - JFrog Artifactory Community Edition (CE):[JFrog Artifactory Community Edition (CE)](https://conan.io/downloads.html)是官方推荐的用于私有部署的Conan服务器程序。这个是JFrog Artifactory的免费社区版,包含了WebUI、LDAP协议、拓扑管理、REST API以及能够存储构建物的通用仓库。下载Docker Image:`docker pull docker.bintray.io/jfrog/artifactory-cpp-ce`,运行方式参见[文档](https://www.jfrog.com/confluence/display/RTF5X/Installing+with+Docker)。 24 | 25 | - Conan Server:这是一个与Conan Client一起发布的小的服务端程序。它是一个Conan服务端的开源实现,只包含服务端的基本功能,没有WebUI以及其它高级功能。 26 | 27 | - [ConanCenter](https://conan.io/center/):这是官方的中央仓,用于管理社区贡献的各种流行开源库,例如Boost,Zlib,OpenSSL,Poco等。 28 | 29 | ## 基于二进制的包管理 30 | 31 | Conan最强大的特性使它能为任何可能的平台和配置生成和管理预编译的二级制文件。使用预编译的二进制文件可以避免用户反复的从源码进行构建,节省大量的开发以及持续集成服务器用于构建的时间,同时也提高了交付件的可重现性和可跟踪性。 32 | 33 | Conan中的包由一个"conanfile.py"定义。该文件定义了包的依赖、包含的源码、以及如何从源码构建出二进制文件。一个包的"conanfile.py"配置可以生成任意数量的二进制文件,每个二进制可以面向不同的平台和配置(操作系统、体系结构、编译器、以及构件类型等等)。二进制的创建和上传,在所有平台上使用相同的命令,并且都是基于一套包的源码产生的。使用Conan不用为不同的操作系统提供不同的解决方案。 34 | 35 | ![Conan二进制管理](./images/conan-binary_mgmt.png) 36 | 37 | 使用Conan从服务器安装包也很高效。只用从服务端下载所需平台和配置对应的二进制文件即可,不用下载所有的二进制。如果所有的二进制都不可用,也可以用客户端的源码重新构建包。 38 | 39 | ## 支持所有的平台,构建系统以及编译器 40 | 41 | Conan可以工作在Windows, Linux (Ubuntu, Debian, RedHat, ArchLinux, Raspbian), OSX, FreeBSD, 以及 SunOS系统上。因为它是可移植的,其实它可以运行在所有可以运行python的平台上。它的目标是针对所有存在的平台,从裸机到桌面端、移动端、嵌入式以及交叉编译。 42 | 43 | Conan支持当前所有的构建系统。它内建了与当前最流行的构建系统的集成,例如CMake、Visual Studio (MSBuild)、 Autotools、 Makefiles, SCons等等。Conan并不强制所有的包都是用相同的构建系统,每个包可以使用自己的构架系统,并且可以依赖于使用不同构建系统的其它包。Conan也支持与其它构建系统继承,包括一些专有的构建系统。 44 | 45 | Conan支持管理任何编译器的任何版本,包含主流的gcc、cl.exe、clang、apple-clang、intel等,支持它们的各种配置、版本、运行时和C++标准库。Conan也支持各种客户自定义的编译器配置。 46 | 47 | ## 稳定性 48 | 49 | 从Conan1.0之后,开发团队保证Conan的前向兼容性,新的版本不破坏之前的用户配置和包构建发布。而使用老的Conan版本并不保证可以兼容新的包定义。 50 | 51 | Conan需要Python3来运行,对Python2的支持到2020年1月1日。从Conan 1.22.0版本开始,不再保证Python2的支持。 52 | 53 | ## 社区 54 | 55 | Conan当前被 Audi、 Continental、 Plex、 Electrolux、 Mercedes-Benz等公司以及全球数万开发者用于生产环境中。 56 | 57 | Conan在[github](https://github.com/conan-io/conan)上拥有3.6k的星,许多客户为[ConanCenter](https://conan.io/center/)创建各种流行的开源库的包,超过1000名Conan用户在slack上的CppLang Slack#Conan频道中帮助回答问题。 58 | 59 | -------------------------------------------------------------------------------- /02-Install.md: -------------------------------------------------------------------------------- 1 | # 安装 2 | 3 | - 通过pip安装(官方推荐) 4 | 5 | 需要Python版本大于等于3.5。Python 3.4以及Python2的支持将被废弃。新的Python都会预装pip,所以应该可以直接运行pip。 6 | 7 | ```sh 8 | pip install conan 9 | ``` 10 | 11 | 如果你的机器同时有python2和python3,可能需要运行: 12 | 13 | ```sh 14 | pip3 install conan 15 | ``` 16 | 17 | - OSX安装 18 | 19 | MAC上可以使用brew直接安装: 20 | 21 | ```sh 22 | brew update 23 | brew install conan 24 | ``` 25 | 26 | - 二进制安装 27 | 28 | 也可以从conan官网上直接下载二进制安装:https://conan.io/downloads.html 29 | 30 | - 源码安装 31 | 32 | 确保你预装了python和pip。 33 | 34 | ```sh 35 | git clone https://github.com/conan-io/conan.git 36 | cd conan 37 | pip install -r conans/requirements.txt 38 | ``` 39 | 40 | 创建一个脚本,将其加入你的path路径中: 41 | 42 | ```sh 43 | #!/usr/bin/env python 44 | 45 | import sys 46 | 47 | conan_repo_path = "/home/your_user/conan" # ABSOLUTE PATH TO CONAN REPOSITORY FOLDER 48 | 49 | sys.path.append(conan_repo_path) 50 | from conans.client.command import main 51 | main(sys.argv[1:]) 52 | ``` 53 | 54 | - 测试conan安装OK: 55 | 56 | ```sh 57 | $ conan 58 | ``` 59 | 60 | 打印类似如下,说明安装OK 61 | 62 | ```sh 63 | Consumer commands 64 | install Installs the requirements specified in a recipe (conanfile.py or conanfile.txt). 65 | config Manages Conan configuration. 66 | get Gets a file or list a directory of a given reference or package. 67 | info Gets information about the dependency graph of a recipe. 68 | ... 69 | ``` 70 | 71 | - 升级 72 | 73 | 最简单的方式: 74 | 75 | ```sh 76 | $ pip install conan --upgrade 77 | ``` 78 | -------------------------------------------------------------------------------- /03-getting-started.md: -------------------------------------------------------------------------------- 1 | # 快速开始 2 | 3 | 以开发一个“MD5计算器”程序为例,它依赖了一个非常流行的C++的网络库[Poco](https://pocoproject.org/)来帮助它完成功能。 4 | 5 | 这个例子使用CMake作为构建工具,也可以使用别的构建工具。 6 | 7 | 这个例子的源码在github上,所以你也可以直接把代码clone下来,不用自己手动敲代码。 8 | 9 | ```sh 10 | $ git clone https://github.com/conan-io/examples.git && cd examples/libraries/poco/md5 11 | ``` 12 | 13 | [https://github.com/conan-io/examples.git](https://github.com/conan-io/examples.git)仓库里面有conan各种用法的例子,建议clone下来系统的学习一下。 14 | 15 | 继续回到本例,按照以下步骤执行。 16 | 17 | ## 运行示例 18 | 19 | - 首先创建一个目录,在里面创建一个`md5.cpp`文件,包含以下内容: 20 | 21 | ```cpp 22 | // md5.cpp 23 | 24 | #include "Poco/MD5Engine.h" 25 | #include "Poco/DigestStream.h" 26 | 27 | #include 28 | 29 | 30 | int main(int argc, char** argv) 31 | { 32 | Poco::MD5Engine md5; 33 | Poco::DigestOutputStream ds(md5); 34 | ds << "abcdefghijklmnopqrstuvwxyz"; 35 | ds.close(); 36 | std::cout << Poco::DigestEngine::digestToHex(md5.digest()) << std::endl; 37 | return 0; 38 | } 39 | ``` 40 | 41 | - 我们知道这个程序依赖Poco库,所以先让conan从ConanCenter中查找阿和库。 42 | 43 | ```sh 44 | $ conan search poco --remote=conan-center 45 | Existing package recipes: 46 | 47 | poco/1.8.1 48 | poco/1.9.3 49 | poco/1.9.4 50 | poco/1.10.0 51 | poco/1.10.1 52 | ``` 53 | 54 | 这里需要指定`--remote=conan-center`,否则conan将从本地cache中查找。 55 | 56 | - 选择一个Poco的版本,可以查看它的包描述。 57 | 58 | ```sh 59 | $ conan inspect poco/1.9.4 60 | name: poco 61 | version: 1.9.4 62 | url: https://github.com/conan-io/conan-center-index 63 | homepage: https://pocoproject.org 64 | license: BSL-1.0 65 | author: None 66 | description: Modern, powerful open source C++ class libraries for building network- and internet-based applications that run on desktop, server, mobile and embedded systems. 67 | topics: ('conan', 'poco', 'building', 'networking', 'server', 'mobile', 'embedded') 68 | generators: cmake 69 | exports: None 70 | exports_sources: CMakeLists.txt 71 | short_paths: False 72 | apply_env: True 73 | build_policy: None 74 | revision_mode: hash 75 | settings: ('os', 'arch', 'compiler', 'build_type') 76 | options: 77 | cxx_14: [True, False] 78 | enable_apacheconnector: [True, False] 79 | enable_cppparser: [True, False] 80 | enable_crypto: [True, False] 81 | [...] 82 | default_options: 83 | cxx_14: False 84 | enable_apacheconnector: False 85 | enable_cppparser: False 86 | enable_crypto: True 87 | [...] 88 | ``` 89 | 90 | 从如上包描述中,也可以大致看出来conan的包的元信息能够描述的内容以及能力。 91 | 92 | - 接下来,可以为md5程序指定依赖以及构建了。在目录下创建一个`conanfile.txt`文件,描述如下: 93 | 94 | ```toml 95 | [requires] 96 | poco/1.9.4 97 | 98 | [generators] 99 | cmake 100 | ``` 101 | 102 | 这里构建指定的是cmake,所以需要提前预装cmake工具。在过程中将会产生一个`conanbuildinfo.cmake`文件,包含一些列CMake变量,指定了构建的include路径、库名称等,用于构建过程。 103 | 104 | - 下一步,我们将要安装以来的包,以及为其产生用于构建的各种信息文件。 105 | 106 | > 注意,如果你使用的GCC版本大于等于5.1,Conan为了后向兼容设置compile.libcxx为老的ABI,你需要使用下面的命令作以修改: 107 | > ```sh 108 | > $ conan profile new default --detect # Generates default profile detecting > GCC and sets old ABI 109 | > $ conan profile update settings.compiler.libcxx=libstdc++11 default # Sets libcxx to C++11 ABI 110 | > ``` 111 | 112 | 执行以下命令安装依赖: 113 | 114 | ```sh 115 | $ mkdir build && cd build 116 | $ conan install .. 117 | ``` 118 | 119 | 通过打印输出可以看到,Conan除了安装了直接依赖的Poco,还安装了间接依赖的OpenSSL和zlib库。同时它为我们的构建系统产生了conanbuildinfo.cmake文件。 120 | 121 | - 现状,为MD5程序创建自己的构建文件。如下创建CmakeLists.txt文件,包含conan自动生成的conanbuildinfo.cmake文件。 122 | 123 | ```cmake 124 | cmake_minimum_required(VERSION 2.8.12) 125 | project(MD5Encrypter) 126 | 127 | add_definitions("-std=c++11") 128 | 129 | include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) 130 | conan_basic_setup() 131 | 132 | add_executable(md5 md5.cpp) 133 | target_link_libraries(md5 ${CONAN_LIBS}) 134 | ``` 135 | 136 | - 现在可以构建并运行最终的MD5计算器程序了。 137 | 138 | ```sh 139 | $ cmake .. -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release 140 | $ cmake --build . 141 | ... 142 | [100%] Built target md5 143 | $ ./bin/md5 144 | c3fcd3d76192e4007dfb496cca67e13b 145 | ``` 146 | 147 | ## 依赖的安装 148 | 149 | 运行`conan install`命令,帮我们下载了依赖的Poco程序包,及其间接依赖的OpenSSL以及Zlib库。Conan自动根据我们的平台下载合适的二进制(Conan首次运行时会自动检测平台配置)。在这个过程中,conan会在当前目录下(示例中的build目录)创建conanbuildinfo.cmake文件,在该文件中能够看到各种CMake变量;还有一个conaninfo.txt文件,保存了各种配置、依赖和构建选项信息。 150 | 151 | > 注意: 152 | > Conan会自动根据对系统的检测结果(OS、编译器、架构等等)产生默认的profile配置。这些信息会打印在`conan install`的开始。强烈建议你review下这些选项,然后根据需要进行调整。具体调整方式可以参考[这里](https://docs.conan.io/en/latest/getting_started.html#getting-started-other-configurations)。 153 | 154 | 了解`conan install`的过程是很重要的。当命令执行时,`/.conan/profiles/default`文件里面的配置将会被命令行程序应用。 155 | 156 | ![conan install flow](./images/conan-install_flow.png) 157 | 158 | 举个例子,当命令`conan install .. --settings os="Linux" --settings compiler="gcc"`运行时,一些步骤将被执行: 159 | 160 | - 检查对应的包的说明文件(例如 poco/1.9.4)是否已经在本地缓存中存在。如果第一次运行conan,缓存会是空的; 161 | - 从远程的中央仓查找包的说明文件。默认从[conan-center](https://bintray.com/conan/conan-center)查找,这个可以改。 162 | - 如果中央仓中有包的说明文件,conan客户端下载以及将其存在缓存中; 163 | - 根据包的说明文件以及输入配置(Linux,GCC),conan在本地缓存查找与包对应的二进制; 164 | - 如果本地缓存没有,conan在中央仓查找对应的二进制包,并下载; 165 | - 最终,根据`[generator]`的描述,将为构建系统产生需要的文件; 166 | 167 | Conan Server上维护了针对主流编译器和版本的二进制包,如果特定配置的二进制包不存在conan将会抛出一个错误。你可以使用`conan install .. --build=missing`来从源码构建你需要的二进制包,当然这需要你要的二进制配置被包的说明文件所支持。 168 | 169 | ## 按照自定义配置构建包 170 | 171 | 在这个例子中,我们使用conan检查的系统默认配置做的构建。 172 | 173 | `conan install`运行前需要先定义好profile,conan会自动检测系统(编译器、体系架构等等)并将对应的profile存在`~/.conan/profiles/default`文件中。你可以根据你的需要编辑这个文件,配置新的一系列profile。 174 | 175 | 如果我们配置了一个32-bit的GCC构建类型的profile,起名为`gcc_x86`。我们可以如下运行: 176 | 177 | ```sh 178 | conan install .. --profile=gcc_x86 179 | ``` 180 | 181 | > 提醒:强烈建议通过`conan config install`命令使用和管理[Profiles](https://docs.conan.io/en/latest/reference/profiles.html#profiles) 182 | 183 | 同样,用户可以通过`conan install`的`--setting`参数更改profile中的部分配置。例如,想要构建32位的版本,也可以使用如下命令: 184 | 185 | ```sh 186 | conan install .. --settings arch=x86 187 | ``` 188 | 189 | 上面的命令将会使用`--settings arch=x86`配置替代默认profile中的配置,然后在本地安装一个不同的二进制包。 190 | 191 | 为了能够使用32位的二进制包,你还需要调整你的本地工程构建配置: 192 | 193 | - 对于windows,将CMake的构建改为调用`Virual Studio 14`; 194 | - Linux上,需要在CMakeLists.txt中加入`-m32`;如 `SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32")`,包括加入到`CMAKE_C_FLAGS, CMAKE_SHARED_LINK_FLAGS and CMAKE_EXE_LINKER_FLAGS`。我们随后会展示这件事也可以用conan自动的做。 195 | - 在macOS上,你需要定义`-DCMAKE_OSX_ARCHITECTURES=i386` 196 | 197 | ## 查看依赖 198 | 199 | 获取下来的包存在本地缓存(一般在`.conan/data`目录下),以供别的工程复用。这样在没有网络的时候依然可以清空工程继续工作。搜索本地缓存的包用该命令 `conan search "*"`。 200 | 201 | 查看一个二进制包的细节可以用`conan search poco/1.9.4@`。 202 | 203 | 包末尾的`@`符号用于指定特定的版本,否则conan会以模式搜索的方式,返回匹配“poco/1.9.4”的所有包。 204 | 205 | 可以通过`conan info `命令查看当前工程的所有依赖包的细节,这个命令需要在指定工程conanfile.txt文件所在的路径。 206 | 207 | 可以使用`conan info --graph=file.html`来生成依赖图,可以指定Dot或者HTML格式。 208 | 209 | ![conan info deps html graph](./images/conan-info_deps_html_graph.png) 210 | 211 | ## 查找包 212 | 213 | Conan默认配置的从[Conan Center](https://bintray.com/conan/conan-center)查找和安装包。 214 | 215 | 你可以使用如下命令在Conan Center中查找包: 216 | 217 | ```sh 218 | $ conan search "open*" --remote=conan-center 219 | Existing package recipes: 220 | 221 | openal/1.18.2@bincrafters/stable 222 | openal/1.19.0@bincrafters/stable 223 | openal/1.19.1 224 | opencv/2.4.13.5@conan/stable 225 | opencv/3.4.3@conan/stable 226 | opencv/3.4.5@conan/stable 227 | opencv/4.0.0@conan/stable 228 | opencv/4.0.1@conan/stable 229 | opencv/4.1.0@conan/stable 230 | opencv/4.1.1@conan/stable 231 | openexr/2.3.0 232 | openexr/2.3.0@conan/stable 233 | openexr/2.4.0 234 | openjpeg/2.3.0@bincrafters/stable 235 | openjpeg/2.3.1 236 | openjpeg/2.3.1@bincrafters/stable 237 | openssl/1.0.2s 238 | ... 239 | ``` 240 | 241 | 可以看到上面搜索出来的二进制包,有的以`@`加一个后缀结束。这个后缀`user/channel`字段,一般用于组织或个人更改了包的配置后,用于和原包做区分的(在包名和版本号之外)。 -------------------------------------------------------------------------------- /04-using-package.md: -------------------------------------------------------------------------------- 1 | # 使用包 2 | 3 | 本节一起看看如何使用Conan建立工程以及管理依赖。 4 | 5 | ## 安装依赖 6 | 7 | 前面通过`conan install`命令下载Poco库并构建了一个案例。 8 | 9 | 在这个例子中,打开生成的`conanbuildinfo.cmake`文件,将会发现里面有很多CMake的变量声明。例如`CONAN_INCLUDE_DIRS_ZLIB`,描述了zlib库头文件的包含路径,而`CONAN_INCLUDE_DIRS`定义了所有依赖的头文件的包含路径。 10 | 11 | ![conan-local_cache_cmake](./images/conan-local_cache_cmake.png) 12 | 13 | 如果检查这些变量定义的全路径,会发现都位于``目录下。这些路径构成了本地缓存,所有的包配置以及二进制文件都存储在这里,因此相同的包不会重复下载。通过`conan search`可以查看本地缓存,通过`conan remove`可以从缓存删除指定的包。 14 | 15 | 跳转到`conanbuildinfo.cmake`文件中指定的目录下,你可以找到每个包的头文件和生成的库文件。 16 | 17 | 当在命令行执行`conan install pocp/1.9.4@`的时候,Conan会将Poco包以及它的依赖包(openssl/1.0.2t and zlib/1.2.11)下载到本地缓存目录,并会在安装过程中打印出安装目录。相比一个一个的安装依赖,我们推荐使用`conanfile.txt`文件描述依赖并整体安装。下面介绍`conanfile.txt`文件的格式。 18 | 19 | ### Requires 20 | 21 | `conanfile.txt`文件的`[requires]`段用来描述依赖,例如: 22 | 23 | ```toml 24 | [requires] 25 | mypackage/1.0.0@company/stable 26 | ``` 27 | 28 | 在这个例子中: 29 | 30 | - `mypackage`是依赖的包的名字; 31 | - `1.0.0`表示包的版本。版本可以是任何的字符串,不一定非得是数字。例如可以用“develop”或“master”。另外包也可以被覆盖,所以可以使用“nightly”或者“weekly”表示它是周期产生的; 32 | - `company`代表包的所有者。这个字段可以作为包的命名空间,允许不同的用户有同名的包。 33 | - `stable`表示包的渠道(channel)。这个字段可以为相同的包提供一种可替换的版本,可以使用任何的字符串。例如常用的“stable”或者“testing”。也可以用于表示包为不同的目的做的修订(例如:包的版本不变,但是包的配置修改了); 34 | 35 | `@`符号后面的是可选的,官方将其叫做`user/chanel`描述。这是个实验特性,未来可能会变化。如果包创建和上传的时候没有指定`user`和`channel`描述,当我们指定依赖的时候就忽略`user/chanel`。如下: 36 | 37 | ```toml 38 | [requires] 39 | packagename/1.2.0 40 | ``` 41 | 42 | `[requires]`后面的依赖可以有多项,而且还可以覆盖依赖包的依赖。比如本例中,Conan安装了Poco以及所有它间接依赖的包(openssl/1.0.2t,zlib/1.2.11)。 43 | 44 | 现在假设OpenSSL库有新的版本发布,Conan对应有了新的包。我们不需要等着[poco项目](https://bintray.com/pocoproject/conan/Poco%3Apocoproject)更新一个使用最新OpenSSL的新的POCO包。 45 | 46 | 我们可以简单的在`[require]`下面指明新的OpenSSL版本即可: 47 | 48 | ```toml 49 | [requires] 50 | poco/1.9.4 51 | openssl/1.0.2u 52 | ``` 53 | 54 | `openssl/1.0.2u`版本将会覆盖POCO中原先依赖的`openssl/1.0.2t`。 55 | 56 | 再比如,我们想要试试最新的zlib库的alpha特性,我们可以将该库替换成其它用户或者渠道(channel)的。 57 | 58 | ```toml 59 | [requires] 60 | poco/1.9.4 61 | openssl/1.0.2u 62 | zlib/1.2.11@otheruser/alpha 63 | ``` 64 | 65 | ### Generators 66 | 67 | Conan从conanfile.txt中的`[generator]`段中读取配置,然后为每个generator生成文件临时文件。这些文件一般创建在独立的“build”目录下,不需要版本控制。这些文件是按照`conan install`运行时指定的配置(Debug/Release, x86/x86_64等等)生成的,如果配置变了,这些文件响应的也会变化。 68 | 69 | 关于generator更详细的信息,可以访问官方[generator](https://docs.conan.io/en/latest/reference/generators.html#generators-reference)的文档。 70 | 71 | ### Options 72 | 73 | 我们看到在执行`conan install`的时候可以指定配置。例如`conan install .. -s build_type=Debug`。这里指定的一般都是客户机器上的项目级别的配置,一般没法在包配置中指定默认值。例如,在包配置中指定使用“Visual Studio”作为默认编译期就不合理,因为类似这些配置最好由最终用户指定,否则对于在linux工作上的用户就不友好。 74 | 75 | 但是包配置中的`[options]`最好用于指定包普遍适用的配置,以及指定默认值。例如一个包可以指定默认为静态链接,这样用户一般情况就不用再指定了。 76 | 77 | 可以使用类似`conan get poco/1.9.4@`的命令查看指定包的的options。也可以通过`conan inspect`命令,如下: 78 | 79 | ```sh 80 | $ conan inspect poco/1.9.4@ -a=options 81 | $ conan inspect poco/1.9.4@ -a=default_options 82 | ``` 83 | 84 | 我们可以在使用中修改某个包的默认配置。例如指定poco为动态链接: 85 | 86 | ```toml 87 | [requires] 88 | poco/1.9.4 89 | 90 | [generators] 91 | cmake 92 | 93 | [options] 94 | poco:shared=True # PACKAGE:OPTION=VALUE 95 | openssl:shared=True 96 | ``` 97 | 98 | 下来就可以按照新的配置在build目录下构建工程了(如果不是Windows用户的修改下面命令中指定的CMake Generator)。 99 | 100 | ```sh 101 | $ conan install .. 102 | $ cmake .. -G "Visual Studio 14 Win64" 103 | $ cmake --build . --config Release 104 | ``` 105 | 106 | 除了在conanfile.txt中修改options,也可以直接在命令行中指定: 107 | 108 | ```sh 109 | $ conan install .. -o poco:shared=True -o openssl:shared=True 110 | # or even with wildcards, to apply to many packages 111 | $ conan install .. -o *:shared=True 112 | ``` 113 | 114 | 随后Conan将会安装这些包的动态库二进制文件,最终的md5计算器程序构建时将会链接这些动态库。最后,我们执行md5程序: 115 | 116 | ```sh 117 | $ ./bin/md5 118 | ``` 119 | 120 | 意外的是程序出错了,提示没有找到动态库。动态库是在运行时查找的,所以当我们运行md5程序的时候,它由于找不到动态库而出错。 121 | 122 | 我们可以通过命令查看可执行程序依赖的动态库: 123 | 124 | ```sh 125 | $ cd bin 126 | $ objdump -p md5 127 | ... 128 | Dynamic Section: 129 | NEEDED libPocoUtil.so.31 130 | NEEDED libPocoXML.so.31 131 | NEEDED libPocoJSON.so.31 132 | NEEDED libPocoMongoDB.so.31 133 | NEEDED libPocoNet.so.31 134 | NEEDED libPocoCrypto.so.31 135 | NEEDED libPocoData.so.31 136 | NEEDED libPocoDataSQLite.so.31 137 | NEEDED libPocoZip.so.31 138 | NEEDED libPocoFoundation.so.31 139 | NEEDED libpthread.so.0 140 | NEEDED libdl.so.2 141 | NEEDED librt.so.1 142 | NEEDED libssl.so.1.0.0 143 | NEEDED libcrypto.so.1.0.0 144 | NEEDED libstdc++.so.6 145 | NEEDED libm.so.6 146 | NEEDED libgcc_s.so.1 147 | NEEDED libc.so.6 148 | ``` 149 | 150 | ### Imports 151 | 152 | 动态库需要加入到系统路径中才能被找到,Linux上一般在环境变量`LD_LIBRARY_PATH`中指定,OSX系统中则是`DYLD_LIBRARY_PATH`,而Windows则是加入系统`PATH`中。一般情况下将动态库拷贝到这些路径下,就可以被查找到。但是Conan尽量避免与这些不同OS的环境配置相关。 153 | 154 | 在Windows和OSX下,最简单的方式是将动态库拷贝到可执行程序所在的目录下,这样它们就可以被可执行程序找到,而不用去修改系统路径。 155 | 156 | 利用conanfile.txt的`[import]`段可以自动完成这件事。例如: 157 | 158 | ```toml 159 | [requires] 160 | poco/1.9.4 161 | 162 | [generators] 163 | cmake 164 | 165 | [options] 166 | poco:shared=True 167 | openssl:shared=True 168 | 169 | [imports] 170 | bin, *.dll -> ./bin # Copies all dll files from packages bin folder to my "bin" folder 171 | lib, *.dylib* -> ./bin # Copies all dylib files from packages lib folder to my "bin" folder 172 | ``` 173 | 174 | 这个例子中指定将包的所有dll和dylib文件拷贝到当前工程的bin目录下。可以打开本地缓存(~/.conan/data)看看,一般包结构里面*.dll将会在"/bin"目录下,而其它的库文件则在"/lib"目录下。 175 | 176 | 增加了`import`配置后,再次在build目录下执行`conan install ..`,随后再运行程序`./bin/md5`,可以发现程序运行正常了。 177 | 178 | 查看"build/bin"目录,可以看到需要的动态库被拷贝在了这里。 179 | 180 | 官方认为`[import]`段是一个更通用的将库引入当前工程的方式。用这个方式可以把可执行程序要的其它依赖拷贝到bin目录下,例如声音、图片、为测试准备的静态文件等等。 181 | 182 | ## 使用profiles 183 | 184 | 目前我们一直使用的默认配置进行构建,配置存在`~/.conan/profiles/default`文件中。 185 | 186 | 但是,对一个大的工程,配置会越来越复杂,很难用一套配置满足所有需求。我们需要有种简单的方式在多种配制间切换。一种简单的方式是定义多个profile。 187 | 188 | 一个profile文件包含预定义好的`settings`, `options`, `environment variables` 以及`build_requires`。例如: 189 | 190 | ```toml 191 | [settings] 192 | setting=value 193 | 194 | [options] 195 | MyLib:shared=True 196 | 197 | [env] 198 | env_var=value 199 | 200 | [build_requires] 201 | Tool1/0.1@user/channel 202 | Tool2/0.1@user/channel, Tool3/0.1@user/channel 203 | *: Tool4/0.1@user/channel 204 | ``` 205 | 206 | Options可以指定通配符,表示将一个选项应用于许多的包。例如: 207 | 208 | ```sh 209 | [options] 210 | *:shared=True 211 | ``` 212 | 213 | 以下是个clang_3.5的完整的例子: 214 | 215 | ```toml 216 | [settings] 217 | os=Macos 218 | arch=x86_64 219 | compiler=clang 220 | compiler.version=3.5 221 | compiler.libcxx=libstdc++11 222 | build_type=Release 223 | 224 | [env] 225 | CC=/usr/bin/clang 226 | CXX=/usr/bin/clang++ 227 | ``` 228 | 229 | Profile文件可以存在默认的profile的目录下,或者其它工程目录下任何位置。使用profile指定的配置,可以通过命令行参数指定profile文件,例如: 230 | 231 | ```sh 232 | $ conan create . demo/testing -pr=clang_3.5 233 | ``` 234 | 235 | 继续使用Poco的例子。我们可以定义一个profile文件保存各种目标配置,然后在install的时候通过命令行参数传递给conan。 236 | 237 | 一个将依赖安装为动态库以及debug模式的profile文件(debug_shared)如下: 238 | 239 | ```toml 240 | include(default) 241 | 242 | [settings] 243 | build_type=Debug 244 | 245 | [options] 246 | poco:shared=True 247 | poco:enable_apacheconnector=False 248 | openssl:shared=True 249 | ``` 250 | 251 | 然后可以用下面的命令安装依赖: 252 | 253 | ```sh 254 | $ conan install .. -pr=debug_shared 255 | ``` 256 | 257 | 我们还能够创建一个profile文件(poco_clang_3.5)使用另外一个编译器版本,并将其存在工程目录下,例如: 258 | 259 | ```toml 260 | include(clang_3.5) 261 | 262 | [options] 263 | poco:shared=True 264 | poco:enable_apacheconnector=False 265 | openssl:shared=True 266 | ``` 267 | 268 | 然后指定安装依赖使用这个新的profile: 269 | 270 | ```sh 271 | $ conan install .. -pr=../poco_clang_3.5 272 | ``` 273 | 274 | 可以给命令行传入多个profile,应用的配置将会是组合多个profile后的结果,并且根据profile文件的顺序: 275 | 276 | ```sh 277 | $ conan install .. -pr=../poco_clang_3.5 -pr=my_build_tool1 -pr=my_build_tool2 278 | ``` 279 | 280 | [Profiles](https://docs.conan.io/en/latest/reference/profiles.html#profiles)的reference文档中有其完整的介绍,它可以为指定的库指定对应的编译器和环境变量等。还有一个命令[`conan profile`](https://docs.conan.io/en/latest/reference/commands/misc/profile.html#conan-profile)可以用来查看和管理profile。还可以使用命令[`conan config install`](https://docs.conan.io/en/latest/reference/commands/consumer/config.html#conan-config-install)命令来共享和安装profile。 281 | 282 | ## 工作流 283 | 284 | 本节总结了用户使用conan与其它工具结合安装和消费已存在的包的一般工作流程。如论是处理单配置项目,还是多配置项目,都推荐你在项目根目录中使用conanfile(.py或.txt)来配置项目。 285 | 286 | ### 单配置 287 | 288 | 当你的工程使用单一配置时,conanfile如我们前面示例中的那样,会相对比较简单。在前面的例子中,通过`conan install ..`在build目录下产生了conaninfo.txt以及conanbuildinfo.cmake文件用于支持项目构建。注意build目录是一个临时目录,不需要对其进行版本管理。 289 | 290 | 独立于源码外的build目录(Out-of-source builds)也是支持的。如下例子: 291 | 292 | ```sh 293 | $ git clone git@github.com:conan-io/examples 294 | $ cd libraries/poco 295 | $ conan install ./md5 --install-folder=md5_build 296 | ``` 297 | 298 | 这将产生如下的目录结构: 299 | 300 | ``` 301 | md5_build 302 | conaninfo.txt 303 | conanbuildinfo.txt 304 | conanbuildinfo.cmake 305 | md5 306 | CMakeLists.txt # If using cmake, but can be Makefile, sln... 307 | README.md 308 | conanfile.txt 309 | md5.cpp 310 | ``` 311 | 312 | 现在你可以如下构建: 313 | 314 | ```sh 315 | $ cd md5_build 316 | $ cmake ../md5 -G "Visual Studio 15 Win64" # or other generator 317 | $ cmake --build . --config Release 318 | $ ./bin/md5 319 | > c3fcd3d76192e4007dfb496cca67e13b 320 | ``` 321 | 322 | 通过上面的方式我们创建了一个和工程源码分离的build目录,这样的好处是我们可以容易的对各种配置进行试验,我们清空某一个build不会影响到别的build目录。例如我们可以增加一个Debug类型的build目录: 323 | 324 | ```sh 325 | $ rm -rf * 326 | $ conan install ../md5 -s build_type=Debug 327 | $ cmake ../md5 -G "Visual Studio 15 Win64" 328 | $ cmake --build . --config Debug 329 | $ ./bin/md5 330 | > c3fcd3d76192e4007dfb496cca67e13b 331 | ``` 332 | 333 | ### 多配置 334 | 335 | 你可以在源码内或者源码外管理多个不同的配置,并且可以在它们之间切换而不用每次重新调用`conan install`。当然如果已经执行过`conan install`,然后在本机其它项目中使用相同的配置,再次执行`conan install`将会非常快,因为相关的包已经被安装在本地缓存中了(而不是某个项目内部)。 336 | 337 | ```sh 338 | $ git clone git@github.com:conan-io/examples 339 | $ cd libraries/poco 340 | $ conan install md5 -s build_type=Debug -if md5_build_debug 341 | $ conan install md5 -s build_type=Release -if md5_build_release 342 | 343 | $ cd md5_build_debug && cmake ../md5 -G "Visual Studio 15 Win64" && cd ../.. 344 | $ cd md5_build_release && cmake ../md5 -G "Visual Studio 15 Win64" && cd ../.. 345 | ``` 346 | 347 | 注意,上面的例子中`--install-folder`被缩写为`-if`。 348 | 349 | 现在,项目目录将会如下: 350 | 351 | ``` 352 | md5_build_debug 353 | conaninfo.txt 354 | conanbuildinfo.txt 355 | conanbuildinfo.cmake 356 | CMakeCache.txt # and other cmake files 357 | md5_build_release 358 | conaninfo.txt 359 | conanbuildinfo.txt 360 | conanbuildinfo.cmake 361 | CMakeCache.txt # and other cmake files 362 | example-poco-timer 363 | CMakeLists.txt # If using cmake, but can be Makefile, sln... 364 | README.md 365 | conanfile.txt 366 | md5.cpp 367 | ``` 368 | 369 | 现在你可以选择到任何一个配置目录下执行构建,因为conan将具体不同的配置文件生成在不同的目录下。 370 | 371 | ```sh 372 | $ cd md5_build_debug && cmake --build . --config Debug && cd ../.. 373 | $ cd md5_build_release && cmake --build . --config Release && cd ../.. 374 | ``` 375 | 376 | 注意这里需要修改例子中代码中的CMake文件的`include()`,加上cmake二进制的目录宏,否则cmake构建的时候将找不到对应的conanbuildinfo.cmake文件。 377 | 378 | ```cmake 379 | include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) 380 | conan_basic_setup() 381 | ``` 382 | 383 | ## 调试包 384 | 385 | 要能够调试一个包的源码,调试器需要能找到源码以及可供调试的信息(Visual Studio中的pdb文件,对于MAC和Unix系统,位于库本身中)。 386 | 387 | 一般情况下,Conan的包中没有包含调试信息文件;就算包含,每个用户的本地的缓存路径也可能不同。而且一般包都是由CI(持续集成)服务器自动构建的,供调试的信息的路径一般也和开发者机器上的不一样。 388 | 389 | 这里一个做法是在开发者的机器上直接编译一个可供调试的包,在Conan下这很简单: 390 | 391 | ```sh 392 | conan install --build --profile 393 | ``` 394 | 395 | 这个命令将会触发库的构建发生在开发者的机器上,因此二进制指向的源码目录就是确定的,调试器将能够根据调试信息找到源码。 396 | 397 | -------------------------------------------------------------------------------- /06-uploading-packages.md: -------------------------------------------------------------------------------- 1 | # 上传包 2 | 3 | 这一节一起来看看如何将包上传到指定的远端包仓库。 4 | 5 | ## 远端仓库 6 | 7 | 在前面我们创建的包都是存在本地缓存中,一般在`~/.conan/data`。现在我们想将它们上传到Conan Server上,以便可以在别的机器和项目上使用它们。 8 | 9 | Conan的包可以被上传到提前配置好名字和URL的不同远端仓上。这些远端仓是用来存储包的二进制的服务器。 10 | 11 | 有好几种的远端仓。对于私有开发有以下选择: 12 | 13 | - Artifactory Community Edition for C/C++ : 它是一个完全免费的Conan服务端仓库,推荐公司或者团队使用它作为自己的私有仓库。它有一个web UI,先进的鉴权和权限管理,较好的性能和扩展性,对应的REST API,以及能够作为服务器存储交付件(如tar包,Zip文件等)。 14 | 15 | - Artifactory Pro:Artifactory Pro可以管理所有主要包格式的二进制仓库。它是推荐给企业的更专业的包管理仓库。具体可以在[Artifactory documentation](https://www.jfrog.com/confluence/display/JFROG/JFrog+Artifactory)了解更多。 16 | 17 | - Conan server:简单免费开源的、MIT license的服务端,和conan客户端一起发布。 18 | 19 | 对于包的分发,可以使用Bintray。 Bintray是一个云平台,可以让你对发布、存储、分发软件具有完全的控制。您可以在Bintray中创建二进制存储库来共享Conan包,甚至创建一个组织。它对于开源软件包是免费的,并免费提供分发到C和C++社区的推荐服务器。 20 | 21 | ### Conan-center 22 | 23 | Conan-center(https://bintray.com/conan/conan-center)是主要的官方仓库,提供服务给开源的Conan包。它是本地的默认远端仓配置,如下也可以手动将其添加到配置:`conan remote add conan-center https://conan.bintray.com`。 24 | 25 | 在Conan-center中当前有两种不同类型的包: 26 | 27 | - 完全引用的包: 例如"pkg/version@user/channel"。这种包是用户在自己的Bintray仓库中创建的,然后包含在这里。现在不推荐继续使用这种包的分发方式了。 28 | 29 | - 没有`user/channel`的包:能够直接以`pkg/version`方式使用的包,这种包是通过Conan的C3I(Conan-Center Continuous Integration)服务自动根据conan-center-index从github上创建出来的。 30 | 31 | ### Bintray的社区仓库 32 | 33 | 有几个很流行的社区仓库,它们也提供conan用户下载自己感兴趣的包。这些仓库并不属于conan团队。 34 | 35 | - Bincrafters : https://bintray.com/bincrafters/public-conan 36 | 37 | Bincrafters团队构建了许多二进制软件包给OSS社区。这个仓库包含了大量的Conan包。 38 | 39 | 如下命令可以将这个仓库添加到Conan中: 40 | 41 | ```sh 42 | $ conan remote add bincrafters https://api.bintray.com/conan/bincrafters/public-conan 43 | ``` 44 | 45 | - Conan Community: https://bintray.com/conan-community/conan 46 | 47 | 这是conan开发者组织的,用于将包发布于Conan Center前测试和孵化包的。这个仓库包含一些成熟过程中的包,可能还不能工作或者特性不齐全。 48 | 49 | 如下命令将这个仓库添加到Conan中: 50 | 51 | ```sh 52 | $ conan remote add conan-community https://api.bintray.com/conan/conan-community/conan 53 | ``` 54 | 55 | ## 将包上传到远端库 56 | 57 | 首先可以列出目前配置的远端库: 58 | 59 | ```sh 60 | $ conan remote list 61 | ``` 62 | 63 | 如下可以将任意的远端库添加到你的机器上: 64 | 65 | ```sh 66 | $ conan remote add my_local_server http://localhost:9300 67 | ``` 68 | 69 | 可以搜索远端库: 70 | 71 | ```sh 72 | $ conan search -r=my_local_server 73 | ``` 74 | 75 | 现在,上传包配置以及所有生成的二进制包到远端。本例中,我们使用`my_local_server`表示指定的远端库,你可以换成其它的: 76 | 77 | ```sh 78 | $ conan upload Hello/0.1@demo/testing --all -r=my_local_server 79 | ``` 80 | 81 | 上述命令可能会弹出要求输入用户名和密码。默认的Conan sever有一个demo/demo账户供我们测试。 82 | 83 | `--all`选项指示上传包配置以及所有的二进制。忽略`--all`选项将会只上传包的配置文件。可以控制上传的二进制,`--package`允许显示指定上传的包的package ID;`--query`可以指定query参数,例如`arch=armv8 and os=Linux`,则会上传所有匹配的二进制包。在shell中如下调用:`conan upload -q 'arch=x86_64 and os=Linux' ...`。 84 | 85 | 这时就可以在自己机器或者局域网中其它机器上查找到这个包了: 86 | 87 | ```sh 88 | $ conan search Hello/0.1@demo/testing -r=my_local_server 89 | ``` 90 | 91 | 如果上传包失败,可以再次尝试。Conan会记录住当前上传进度以及只上传缺失的文件。 92 | 93 | 现在我们可以检查是否可以下载并使用上传的包。我们首先把本地的包删掉,否则就不会从远端下载。 94 | 95 | ```sh 96 | $ conan remove "Hello*" 97 | $ conan search 98 | ``` 99 | 100 | 我们可以复用之前Hello包里的测试工程,而指定不将Hello包构建及导出。 101 | 102 | ```sh 103 | $ conan create . demo/testing --not-export --build=never 104 | ``` 105 | 106 | 这时我们会看到conan从本地server上下载对应的二进制包。 107 | 108 | ## 使用Bintray 109 | 110 | 在Bintray上,你可以创建和管理许多的个人免费的Conan仓库。对于一个OSS账户,所有你上传的包都是公开的,其他人可以简单的将你的仓库添加到他们的Conan远端仓,然后使用你发布的包。 111 | 112 | 为了能够让团队在开源工程上协作,你可以在Bintray上创建组织,然后加入成员。这样你们就可以共同创建和编辑你们组织仓库内的包了。 113 | 114 | ### 上传包至Bintray 115 | 116 | - 在Bintray上创建一个开源账户。打开[https://bintray.com/signup/oss](https://bintray.com/signup/oss),提交表单创建账户; 117 | 118 | - 创建conan仓库。在你的账户下创建一个conan仓库,如果你的仓库需要和别人协作,则需要先创建一个组织(organization); 119 | 120 | - 将创建的conan仓库添加到本地配置中:`conan remote add `; 库的URL地址在页面中点击“set me up”按钮可以看到; 121 | 122 | - 获得你的API key:你的API key是Bintray认证conan客户端的密码,不是你登录Bintray的密码。在Bintray上点击`Edit Your Profile`,然后在你的账户配置中获取你的API key; 123 | 124 | - 设置你的证书,增加Conan用户,使用API key,你的远端库和Bintray的用户名:`conan user -p -r `; 125 | 126 | 设置成功后,你的Conan客户端将会按照以下顺序从远端仓解析以及下载安装包: 127 | - conan-center 128 | - 你自己的仓库 129 | 130 | 如果想要将自己的仓库放在前面,可以在添加仓库时使用`--insert`命令行参数: 131 | 132 | ```sh 133 | $ conan remote add --insert 0 134 | $ conan remote list 135 | : [Verify SSL: True] 136 | conan-center: https://conan.bintray.com [Verify SSL: True] 137 | ``` 138 | 139 | ### 贡献包到Conan-center 140 | 141 | 对Conan-center贡献包,可以直接通过向[https://github.com/conan-io/conan-center-index](https://github.com/conan-io/conan-center-index)提交pull request。C3I(Conan-center Continuous Integration)服务将会自动从合并后的pull request构建二进制包,并上传能到Conan-center。具体的操作见github上的[https://github.com/conan-io/conan-center-index/wiki](https://github.com/conan-io/conan-center-index/wiki)。 142 | 143 | ## Artifactory Community Edition 144 | 145 | C/C++的Artifactory Community Edition(CE)社区版本,推荐给想要做包的私有存储的团队或公司使用。社区版是完全免费的,它包含Web UI、鉴权与权限管理,很好的心梗与扩展性、REST API、一个命令行工具、以及能够存储任何源码和二进制的仓库。 146 | 147 | 本节简单介绍下的Artifactory CE,更详细的推荐查看[Artifactory的文档](https://www.jfrog.com/confluence/)。 148 | 149 | ### 运行 Artifactory CE 150 | 151 | 有好几种下载和运行Artifactory CE的方法。最简单的是下载zip包,解压后安装。压缩包里面还包含了一个Docker Image,也可以从docker启动。 152 | 153 | ![](./images/conan-artifactory_ce.png) 154 | 155 | 一旦运行起来,访问“http://localhost:8081”,使用默认用户名密码:`admin:password`登录。 156 | 157 | ### 创建和使用Conan仓库 158 | 159 | 从页面导航到`Admin -> Repositories -> Local`,然后点击"New",再出现的对话框中选Conan,输入Repository Key,为创建的仓库起个名字。你可以根据需要按照不同的工作流、团队或者工程创建多个仓库。 160 | 161 | 然后就需要配置客户端了。在仓库页面点击"Set Me Up"拷贝正确的远端库的地址,然后配置Conan客户端: 162 | 163 | ```sh 164 | $ conan remote add artifactory http://localhost:8081/artifactory/api/conan/conan-local 165 | ``` 166 | 167 | 现在你可以往仓库中上传、下载或者查询包了。 168 | 169 | ```sh 170 | $ conan upload "*" --all -r=artifactory 171 | $ conan search "*" -r=artifactory 172 | ``` 173 | 174 | ### 从其它服务端迁移包 175 | 176 | 如果你有了自己的包服务仓库,可能需要从别的仓库(例如Conan-center)把包迁移过来。包的迁移并不复杂,只用使用Conan客户端将包下载下来,然后重新上传到新的服务器上即可。 177 | 178 | 有个Python脚本会更高效一些。下例中我们分别用'local'和'artifactory'表示两个仓库: 179 | 180 | ```python 181 | import os 182 | import subprocess 183 | 184 | def run(cmd): 185 | ret = os.system(cmd) 186 | if ret != 0: 187 | raise Exception("Command failed: %s" % cmd) 188 | 189 | # Assuming local = conan_server and artifactory remotes 190 | output = subprocess.check_output("conan search -r=local --raw") 191 | packages = output.splitlines() 192 | 193 | for package in packages: 194 | print("Downloading %s" % package) 195 | run("conan download %s -r=local" % package) 196 | 197 | run("conan upload \"*\" --all --confirm -r=artifactory") 198 | ``` 199 | 200 | ## 使用conan_server 201 | 202 | conan_server是免费开源版本的Conan远端库实现。它是一个简单的应用程序,和Conan的客户端一起绑定安装。conan_server仅建议用来测试,或者对小团队使用。大多数场合下更推荐使用免费的Artifactory Community Edition搭建私有的包仓库。 203 | 204 | 如果已经下载安装过Conan本地程序了的话,那么运行conan_server仅需要一行命令:`conan_server`。 205 | 206 | ### 从源码运行 (linux) 207 | 208 | 从源码运行conan_server,可以用WSGI(例如gunicorn)启动它,这样会更加的稳定。 209 | 210 | ```sh 211 | $ git clone https://github.com/conan-io/conan.git 212 | $ cd conan 213 | $ git checkout master 214 | $ pip install -r conans/requirements.txt 215 | $ pip install -r conans/requirements_server.txt 216 | $ pip install gunicorn 217 | ``` 218 | 219 | 下载好源码后,如下将其启动在9300端口: 220 | 221 | ```sh 222 | $ gunicorn -b 0.0.0.0:9300 -w 4 -t 300 conans.server.server_launcher:app 223 | ``` 224 | 225 | ### 服务配置 226 | 227 | 可以在`~/.conan_server/server.conf`中对服务进行配置,配置好后重新启动服务load新的配置。 228 | 229 | ```toml 230 | [server] 231 | jwt_secret: MnpuzsExftskYGOMgaTYDKfw 232 | jwt_expire_minutes: 120 233 | 234 | ssl_enabled: False 235 | port: 9300 236 | public_port: 237 | host_name: localhost 238 | 239 | store_adapter: disk 240 | authorize_timeout: 1800 241 | 242 | # Just for disk storage adapter 243 | disk_storage_path: ~/.conan_server/data 244 | disk_authorize_timeout: 1800 245 | 246 | updown_secret: NyiSWNWnwumTVpGpoANuyyhR 247 | 248 | 249 | [write_permissions] 250 | # "opencv/2.3.4@lasote/testing": default_user,default_user2 251 | 252 | [read_permissions] 253 | # opencv/1.2.3@lasote/testing: default_user default_user2 254 | # By default all users can read all blocks 255 | */*@*/*: * 256 | 257 | [users] 258 | demo: demo 259 | ``` 260 | 261 | 关于配置的含义,以及如何将conan_server配置在Nginx中,请参考具体的[配置文档](https://docs.conan.io/en/latest/uploading_packages/running_your_server.html)。 262 | -------------------------------------------------------------------------------- /09-versioning.md: -------------------------------------------------------------------------------- 1 | # 版本管理 2 | 3 | // TODO 4 | -------------------------------------------------------------------------------- /10-master-conan.md: -------------------------------------------------------------------------------- 1 | # Conan进阶 2 | 3 | // TODO -------------------------------------------------------------------------------- /11-systems-and-cross-building.md: -------------------------------------------------------------------------------- 1 | # 系统及交叉编译 2 | 3 | // TODO -------------------------------------------------------------------------------- /12-extending-conan.md: -------------------------------------------------------------------------------- 1 | # 扩展Conan 2 | 3 | // TODO -------------------------------------------------------------------------------- /13-integrations.md: -------------------------------------------------------------------------------- 1 | # 集成 2 | 3 | // TODO -------------------------------------------------------------------------------- /14-confituration.md: -------------------------------------------------------------------------------- 1 | # 配置 2 | 3 | // TODO 4 | -------------------------------------------------------------------------------- /15-howtos.md: -------------------------------------------------------------------------------- 1 | # 使用技巧 2 | 3 | // TODO -------------------------------------------------------------------------------- /16-reference.md: -------------------------------------------------------------------------------- 1 | # 引用 2 | 3 | // TODO -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Conan Tutorial 2 | 3 | ![conan logo](./images/jfrog_conan_logo.png) 4 | 5 | 由于工作原因,常常会在各种编程语言技术栈下切换。每次切回到C/C++技术栈下,都会为C/C++语言缺乏一个好用的包管理器而不适应好一阵。 6 | 7 | 包管理器的存在可以让程序功能单元的组织满足闭包化(隐藏源码、依赖和构建细节)、契约化(显示的API导出、变更和版本管理)以及标准化(体验一致的本地客户端、中央仓、及其在工具约束下的标准开发活动等等)。这些都能够让软件功能的复用变得更加黑盒和简单,降低程序的复用成本。 8 | 9 | 这些优点对于社区化开发是非常重要的。离散的社区团队之间需要有一种标准和契约,可以低成本的信任和依赖别人发布的代码和更新。关于C/C++语言需要一个好用的包管理器这件事,已经被社区呼吁很久了。 10 | 11 | 大多数C/C++程序员都羡慕过NodeJS下有npm、RUST下有cargo,就连一贯采用单一代码库的google也因为社区的需要从golang 1.11版本开始引入了`go modules`机制来支持包管理。回过头来我们不禁会问,为C/C++语言做一款好用的包管理器真的有那么难吗? 12 | 13 | 答案是确实挺难!这里面涉及到多方面的原因,而且不仅仅是技术的原因。 14 | 15 | 首先是C/C++语言自身的问题。C/C++程序的构建是底层相关的,这导致当你创建一个包的时候,必须考虑目标的操作系统、体系架构、以及构建时使用的编译器类型和版本、构建类型(Debug/Release)等一系列影响依赖方能否正常使用的因素。 16 | 17 | 此外,你还需要关注一些包自身的属性:是纯头文件库、静态库还是动态库,以及包的构建参数(比如优化级别、是否开启exception和rtti的编译选项...)、还有指定裁剪性(特性宏)等配置。 18 | 19 | 另外,由于C/C++的标准库(glibc和libstdc++)存在版本兼容性问题,以及C++存在ABI兼容性问题,这会让包的版本管理超越语义化版本([SemVer](https://semver.org/))所能解决的问题范围,这导致包的创建者需要在发包的时候为包的兼容性做更多的考虑。 20 | 21 | 最后,由于C/C++语言在语法上缺乏包级别的模块化机制,会让包的符号冲突以及依赖解决变得困难。 22 | 23 | 如果上述还不够,那么在这些的基础上,再加上交叉编译的场景,绝对会让一个通用C/C++包管理器的复杂度超过其它任何语言。 24 | 25 | 上述问题,整个C/C++社区中的组织和开发者一直都在努力解决。然而不像别的语言(golang属于google,rust属于社区),C/C++是由标准委员会和各个编译器工具背后的商业组织共同推动的(主要是C++,但是C受制于不同的Linux发行版本和工具链),所以无论是从效率还是结果上都不是那么好。 26 | 27 | 所以这个社区是分裂的,只用看看有多少种编译构建系统就知道了:gcc、clang、intel、qcc、Visual Studio(MSBuild)、Makefiles、Ninja、Scons、CMake...。同样,在缺少通用包管理的情况下,大家对于代码复用的解决方式也发展出了各种模式。 28 | 29 | 首先是基于源码的复用方式。项目只要划好模块,定义好各自的模块目录以及share的头文件目录,然后就可以分工合作了。 30 | 31 | 这种方式的问题是代码都在单一代码库中,可以直接看到对方的源码。由于互相之间的依赖是隐式的,导致不容易对代码做溯源和裁剪。当然这首先是个设计问题,但是这种复用方式让工具不容易对现状作出有效的可视化和约束管理。 32 | 33 | 在这种方式下,大家很容易商量出一个公共的common头文件目录,将每个模块公开的头文件都放里面(因为成本很低,无论是手动还是构建过程中自动完成)。任何一个模块依赖别人似乎都很简单,但是最后所有模块都耦合到了一起。 34 | 35 | 而且这种方式下,代码库会膨胀的很快,所有变更最终都会拥挤到一条效率不高的持续集成流水线上。由于依赖的隐式化,为持续集成流水线做分层和优化需要花费比较大的精力。 36 | 37 | 后来围绕着Git,人们发展出了一些能够优化“基于源码复用”的工具,如`git submodule`、`git subtree`、`git repo`等。这些工具可以把代码分布到不同的git仓库和分支中,能为每个代码仓搭建自己的CI流水线。但是这些方式没有从根本上解决依赖白盒化的问题。由于在使用这些工具的时候,大家仍然优先倾向将所有源码拉到一起后再进行构建,因此每个库的独立构建、测试和发布其实是缺乏原动力的。 38 | 39 | 一些构建工具的发展,为C/C++的代码复用引入了更好的方式。例如[CMake](https://cmake.org/)从3.0版本开始被称之为“Modern CMake”,是因为它引入了target的概念,以及基于target建立起了构建的依赖可见性和传播控制机制。这些都更好的支持了代码在构建上的模块化,号称“everything is a (self-contained) target”。另外,借助CMake的[ExternalProject](https://cmake.org/cmake/help/latest/module/ExternalProject.html)和[find_package](https://cmake.org/cmake/help/latest/command/find_package.html)特性,使得我们可以从指定的http或者git分支下载、构建、安装和引用代码库。由于CMake的广泛流行,目前这已经成为C/C++开源社区的事实标准。关于Modern CMake的用法和最佳实践,可以看看这篇文章:[《Modern CMake最佳实践》](https://www.jianshu.com/p/8abf754654c4)。 40 | 41 | 另外,Google的[Bazel](https://bazel.build/)也具有类似的模块化构建和依赖管理的能力,在某些方面它还要更强大一些,并且支持云构建和缓存。但是由于其它一些原因,并没有大规模流行起来。我的好朋友刘光聪写过系列文章对bazel做过分析和介绍,具体可以看看: [《Bazel是把双刃剑》](https://www.jianshu.com/p/ab5ef02bfa2c)。 42 | 43 | 上述构建工具提供的代码复用能力,使得C/C++从代码的白盒复用往黑盒复用上迈进了一大步:代码的发布方至少要保证自己代码库的构建闭包性。但是这种复用方式,对于间接依赖的管理仍旧是不足的。我们需要一种能力,可以通过全链条的依赖解析,进行依赖溯源、冲突判决,以及基于变更进行最小范围的重构建和发布管理。 44 | 45 | 所以,包管理器在C/C++社区很早就有了。包管理器通过让包显示化的描述自己的元信息:名称、版本、构建方式、以及所有的依赖包的版本信息,标准化了包的构建、发布和复用方式,以及自动化的对依赖和变更做管理。 46 | 47 | 遗憾的是如我们前面所说,C/C++的构建以及二进制兼容性的外部影响因素太多,所以现有被广泛使用的包管理器往往是局限于某种系统类型内的。例如Linux下主流的rpm和deb就分别面向不同的linux发行版(如Fedora和Ubuntu,当然可以扩展)。这种方式简化了C/C++的构建和二进制兼容性的管理(还包括标准库的兼容性管理),因此让包管理器的设计和使用变得容易。遗憾的是,这样的包管理器对于更广泛的社区化开发是不够的。 48 | 49 | 不过,社区一直没有停止过努力的脚步。[Biicode](https://biicode.github.io/biicode/)是一款探索以源码发包的现代化C/C++包管理器,但遗憾的是这个项目由于经营原因在2015年关闭了。Biicode在关闭前开源了它所有的源码,刚好那个时候我也和几个朋友也一起创建了一个C/C++包管理的项目`CUP`,遗憾的是由于精力原因这个项目一直未能完成。 50 | 51 | 幸运的是,后来我看到了[conan](https://docs.conan.io/en/latest/introduction.html),一款出色的开源C/C++包管理器。它吸收了很多现代化包管理器的设计思想,探索解决通用C/C++包管理器的各种挑战,而且每个方面都解决的很不错。 52 | 53 | 借用Conan文档中的介绍:“Conan is a dependency and package manager for C and C++ languages. It is free and open-source, and it works in all platforms,also integrates with all build systems...”。 54 | 55 | Conan支持交叉编译,如果获取匹配的二进制包失败会尝试从源码进行构建。除了基本的包管理能力外,conan试图内置以包管理为中心的开发最佳实践,包括内置的代码布局(layout)、构建、包测试、发布、以及与Git、IDE、CI和部署工具的集成。这些都让C/C++开发逐渐有了类似于在RUST下使用Cargo的感觉。我把这些归为是现代化包管理器应有的能力,当然conan还有一些工作要做,包括语言自身的完善(例如C++20标准引入的module机制),但目前的使用体验已经不错了。唯独可能会对使用者造成门槛的是,conan的包配置描述需要使用python。 56 | 57 | [https://ccup.github.io/conan-docs-zh/](https://ccup.github.io/conan-docs-zh/)是我在官网学习Conan的过程中,一边学习一边翻译记录的结果。最初的目的是通过翻译让自己对看过的东西加深印象,虽然还没有完全完成,但还是先稍加整理提供给有需要的同学吧。 58 | 59 | 在翻译记录的过程中,我根据个人的感觉对内容做了些取舍。中间有很小的部分加了点个人的理解,以使得整体更加易懂。因此,这个手册不保证更新以及和官网完全一致,有精力的同学还是推荐大家尽可能阅读[官方文档](https://docs.conan.io/)。 60 | 61 | 最后,还想讨论一个话题,那就是在集中管控式的大型C/C++项目中有没有必要将类似于Conan这样的包管理能力内置于开发过程中。 62 | 63 | 和社区化开发不同,这些项目可以通过集中的项目管理手段协调内部的协作和复用,再加上一些我们前面提的源码和模块化构建的技术手段,大多数时候确实可以不需要包管理器。但是我见过很多大型C/C++项目,代码动辄百万、千万,涉及很多可复用的功能单元,由于缺乏包管理器对依赖进行显示化管理,最后内部依赖混乱复杂,以至于源码的追溯性和构建的可重复性都变得困难。这些项目为了解决问题,会自行制定代码标准,开发内部工具,但是做的很多工作在我看来都是使用一款包管理器就可以解决的。 64 | 65 | 当然,这些项目宁愿自行定义标准和开发工具,而不使用包管理,是有原因的。首先,包管理一般具有侵入性,引入包管理势必需要改造现有的开发和协作模式,对于遗留系统的改造成本可能会比较大。另外,采用包管理还可能会让跨模块的变更变得低效。 66 | 67 | 大多数项目在初期时候,变化方向不明确,因此系统内部结构是不稳定的。在中期结构稳定后,可能又缺乏演进式设计和重构能力,对软件结构的划分未必能保证低耦合。如果当大多数变更都需要跨越多个包的时候,采用包管理这种隔离性强的方式,反而会增大协作沟通成本,降低效率。幸运的是,conan提供了[editable mode package](https://docs.conan.io/en/latest/developing_packages/editable_packages.html)和[workspace](https://docs.conan.io/en/latest/developing_packages/workspaces.html)的特性(RUST的cargo也提供了这个特性),来让多包协作的修改变得稍微容易一些。 68 | 69 | 许多编程语言都把包管理器作为一个抓手,围绕着包开发来打造贯穿整个开发过程的最佳实践和辅助工具。包管理的引入会将原有的软件模块团队的交付终点,从仅仅将代码合入到代码库,延长到了需要保证构建、测试、打包和发布成功,并且满足包版本的发布契约(验收测试和契约测试),从而真正意义上的使能团队独立流水线,推动了团队的devops能力。 70 | 71 | 因此我觉得,随着C/C++包管理器的成熟,以及对软件开发过程支持的更加完善,会有越来越多的C/C++新项目逐步开始使用包管理器。而那些改造负担大,或者已经有自己的标准和工具来替代包管理能力的项目,也不妨多关注C/C++社区现代化包管理的现状和进展,从中学习和借鉴一些经验,让自己的标准和工具做的更好。 72 | 73 | 最后,再说一句,包管理只是一系列工具以及基于这些工具所构建的公共能力,它早已被证明不是银弹!包划分的好不好依然依赖于软件设计能力,这在某种程度上和微服务是一样的。你一定听说过很多关于服务拆分不好带来问题的故事吧,幸运的是包划分不好的成本比这低一些,但仍旧是有成本的。 74 | 75 | --- 76 | 77 | 本系列文档的github库地址:[https://github.com/ccup/conan-docs-zh/](https://github.com/ccup/conan-docs-zh/)。 -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | * [Introduction](README.md) 4 | * [Conan介绍](01-introduction.md) 5 | * [安装](02-Install.md) 6 | * [快速开始](03-getting-started.md) 7 | * [使用包](04-using-package.md) 8 | * [创建包](05-creating-packages.md) 9 | * [上传包](06-uploading-packages.md) 10 | * [开发包](07-developing-packages.md) 11 | * [打包应用与开发工具](08-package-apps-and-devtools.md) 12 | * [版本管理](09-versioning.md) 13 | * [Conan进阶](10-master-conan.md) 14 | * [系统及交叉编译](11-systems-and-cross-building.md) 15 | * [扩展Conan](12-extending-conan.md) 16 | * [集成](13-integrations.md) 17 | * [配置](14-confituration.md) 18 | * [使用技巧](15-howtos.md) 19 | * [引用](16-reference.md) 20 | 21 | -------------------------------------------------------------------------------- /_book/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | .DS_Store -------------------------------------------------------------------------------- /_book/13-integrations.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 集成 · Conan Tutorial 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 | 100 | 101 | 102 | 360 | 361 | 362 |
363 | 364 |
365 | 366 |
367 | 368 | 369 | 370 | 379 | 380 | 381 | 382 | 383 |
384 |
385 | 386 |
387 |
388 | 389 |
390 | 391 |

集成

392 |

// TODO

393 |
Copyright © CCUP 2020 all right reserved,powered by Gitbook本书发布时间: 394 | 2020-03-08 23:34:06 395 |
396 | 397 |
398 | 399 |
400 |
401 |
402 | 403 |

results matching ""

404 |
    405 | 406 |
    407 |
    408 | 409 |

    No results matching ""

    410 | 411 |
    412 |
    413 |
    414 | 415 |
    416 |
    417 | 418 |
    419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 |
    434 | 435 | 441 |
    442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | -------------------------------------------------------------------------------- /_book/14-confituration.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 配置 · Conan Tutorial 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 | 100 | 101 | 102 | 360 | 361 | 362 |
    363 | 364 |
    365 | 366 |
    367 | 368 | 369 | 370 | 379 | 380 | 381 | 382 | 383 |
    384 |
    385 | 386 |
    387 |
    388 | 389 |
    390 | 391 |

    配置

    392 |

    // TODO

    393 |
    Copyright © CCUP 2020 all right reserved,powered by Gitbook本书发布时间: 394 | 2020-03-08 23:34:06 395 |
    396 | 397 |
    398 | 399 |
    400 |
    401 |
    402 | 403 |

    results matching ""

    404 |
      405 | 406 |
      407 |
      408 | 409 |

      No results matching ""

      410 | 411 |
      412 |
      413 |
      414 | 415 |
      416 |
      417 | 418 |
      419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 |
      434 | 435 | 441 |
      442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | -------------------------------------------------------------------------------- /_book/16-reference.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 引用 · Conan Tutorial 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 | 98 | 99 | 100 | 358 | 359 | 360 |
      361 | 362 |
      363 | 364 |
      365 | 366 | 367 | 368 | 377 | 378 | 379 | 380 | 381 |
      382 |
      383 | 384 |
      385 |
      386 | 387 |
      388 | 389 |

      引用

      390 |

      // TODO

      391 |
      Copyright © CCUP 2020 all right reserved,powered by Gitbook本书发布时间: 392 | 2020-03-08 23:34:06 393 |
      394 | 395 |
      396 | 397 |
      398 |
      399 |
      400 | 401 |

      results matching ""

      402 |
        403 | 404 |
        405 |
        406 | 407 |

        No results matching ""

        408 | 409 |
        410 |
        411 |
        412 | 413 |
        414 |
        415 | 416 |
        417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 |
        428 | 429 | 435 |
        436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | -------------------------------------------------------------------------------- /_book/gitbook.md: -------------------------------------------------------------------------------- 1 | ## gitbook quick start 2 | 3 | ### install nodejs 4 | 5 | ```sh 6 | sudo apt-get install nodejs 7 | sudo apt-get install npm 8 | ``` 9 | 10 | upgrade: 11 | 12 | ```sh 13 | sudo npm install npm -g 14 | npm install –g n stable 15 | ``` 16 | 17 | modify repo: 18 | 19 | ```sh 20 | npm get registry 21 | npm config set registry http://registry.npm.taobao.org/ 22 | # npm config set registry https://registry.npmjs.org/ 23 | ``` 24 | 25 | ### install gitbook 26 | 27 | ```sh 28 | npm install gitbook-cli -g 29 | gitbook -V 30 | ``` 31 | 32 | ### install plugins 33 | 34 | ```sh 35 | gitbook install 36 | ``` 37 | 38 | ### review book 39 | 40 | ```sh 41 | gitbook serve 42 | ``` 43 | 44 | ### generate book 45 | 46 | #### install ebook converter 47 | 48 | - download Calibre: https://calibre-ebook.com/download 49 | - install Calibre 50 | - config Calibre 51 | 52 | 53 | MACOS: 54 | 55 | ```sh 56 | sudo ln -s ~/Applications/calibre.app/Contents/MacOS/ebook-convert /usr/bin 57 | ``` 58 | 59 | Or: 60 | 61 | ```sh 62 | vi ~/.bash_profile 63 | 64 | export EBOOK_PATH=/Applications/calibre.app/Contents/MacOS 65 | export PATH=$PATH:$EBOOK_PATH 66 | 67 | source .bash_profile 68 | ``` 69 | 70 | #### generate pdf 71 | 72 | ```sh 73 | gitbook pdf 74 | ``` 75 | 76 | #### generate epub 77 | 78 | ```sh 79 | gitbook epub 80 | ``` -------------------------------------------------------------------------------- /_book/gitbook/fonts/fontawesome/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccup/conan-docs-zh/74f59b0ec07e832099a81002cedec9985c4b43ed/_book/gitbook/fonts/fontawesome/FontAwesome.otf -------------------------------------------------------------------------------- /_book/gitbook/fonts/fontawesome/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccup/conan-docs-zh/74f59b0ec07e832099a81002cedec9985c4b43ed/_book/gitbook/fonts/fontawesome/fontawesome-webfont.eot -------------------------------------------------------------------------------- /_book/gitbook/fonts/fontawesome/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccup/conan-docs-zh/74f59b0ec07e832099a81002cedec9985c4b43ed/_book/gitbook/fonts/fontawesome/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /_book/gitbook/fonts/fontawesome/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccup/conan-docs-zh/74f59b0ec07e832099a81002cedec9985c4b43ed/_book/gitbook/fonts/fontawesome/fontawesome-webfont.woff -------------------------------------------------------------------------------- /_book/gitbook/fonts/fontawesome/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccup/conan-docs-zh/74f59b0ec07e832099a81002cedec9985c4b43ed/_book/gitbook/fonts/fontawesome/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /_book/gitbook/gitbook-plugin-ancre-navigation/lib/config.js: -------------------------------------------------------------------------------- 1 | require('./log'); 2 | 3 | const defaultConfig = { 4 | showLevel: true, 5 | associatedWithSummary: true, 6 | printLog: false, 7 | multipleH1: true, 8 | mode: "float", 9 | float: { 10 | showLevelIcon: false, 11 | level1Icon: "fa fa-hand-o-right", 12 | level2Icon: "fa fa-hand-o-right", 13 | level3Icon: "fa fa-hand-o-right" 14 | }, 15 | pageTop: { 16 | showLevelIcon: false, 17 | level1Icon: "fa fa-hand-o-right", 18 | level2Icon: "fa fa-hand-o-right", 19 | level3Icon: "fa fa-hand-o-right" 20 | }, 21 | 22 | themeDefault: { 23 | showLevel: false 24 | } 25 | } 26 | 27 | function handler(defaultConfig, config) { 28 | if (config) { 29 | for (var item in defaultConfig) { 30 | if (item in config) { 31 | defaultConfig[item] = config[item]; 32 | } 33 | } 34 | } 35 | } 36 | 37 | function handlerAll(bookIns) { 38 | var config = bookIns.config.get('pluginsConfig')['ancre-navigation']; 39 | var themeDefaultConfig = bookIns.config.get('pluginsConfig')['theme-default']; 40 | handler(defaultConfig, config); 41 | handler(defaultConfig.themeDefault, themeDefaultConfig); 42 | 43 | if (config.isRewritePageTitle) { 44 | console.error("error:".error + 45 | "plugins[anchor-navigation-ex]:isRewritePageTitle," + 46 | "https://github.com/zq99299/gitbook-plugin-anchor-navigation"); 47 | console.log(""); 48 | console.error("error:".error + 49 | "Please check here https://github.com/zq99299/gitbook-plugin-anchor-navigation for the latest version of the configuration item"); 50 | } 51 | } 52 | 53 | module.exports = { 54 | config: defaultConfig, 55 | handler: handler, 56 | handlerAll: handlerAll 57 | } 58 | -------------------------------------------------------------------------------- /_book/gitbook/gitbook-plugin-ancre-navigation/lib/log.js: -------------------------------------------------------------------------------- 1 | var colors = require('colors'); 2 | colors.setTheme({ 3 | silly: 'rainbow', 4 | input: 'grey', 5 | verbose: 'cyan', 6 | prompt: 'grey', 7 | info: 'green', 8 | data: 'grey', 9 | help: 'cyan', 10 | warn: 'yellow', 11 | debug: 'blue', 12 | error: 'red' 13 | }); 14 | 15 | module.exports = colors; 16 | 17 | -------------------------------------------------------------------------------- /_book/gitbook/gitbook-plugin-ancre-navigation/lib/plugin.js: -------------------------------------------------------------------------------- 1 | var cheerio = require('cheerio'); 2 | var slug = require('github-slugid'); 3 | var Config = require('./config.js'); 4 | 5 | 6 | function handlerTocs($, page) { 7 | var config = Config.config; 8 | var tocs = []; 9 | var count = { 10 | h1: 0, 11 | h2: 0, 12 | h3: 0 13 | }; 14 | var titleCountMap = {}; 15 | var h1 = 0, h2 = 0, h3 = 0; 16 | $(':header').each(function (i, elem) { 17 | var header = $(elem); 18 | var id = addId(header, titleCountMap); 19 | 20 | if (id) { 21 | switch (elem.tagName) { 22 | case "h1": 23 | handlerH1Toc(config, count, header, tocs, page.level); 24 | break; 25 | case "h2": 26 | handlerH2Toc(config, count, header, tocs, page.level); 27 | break; 28 | case "h3": 29 | handlerH3Toc(config, count, header, tocs, page.level); 30 | break; 31 | default: 32 | titleAddAnchor(header, id); 33 | break; 34 | } 35 | } 36 | }); 37 | page.content = $.html(); 38 | return tocs; 39 | } 40 | 41 | function addId(header, titleCountMap) { 42 | var id = header.attr('id') || slug(header.text()); 43 | var titleCount = titleCountMap[id] || 0; 44 | titleCountMap[id] = titleCount + 1; 45 | if (titleCount) { 46 | id = id + '_' + titleCount; 47 | } 48 | header.attr("id", id); 49 | return id; 50 | } 51 | 52 | function titleAddAnchor(header, id) { 53 | header.prepend('' 55 | + '' 56 | + ''); 57 | } 58 | 59 | function handlerH1Toc(config, count, header, tocs, pageLevel) { 60 | var title = header.text(); 61 | var id = header.attr('id'); 62 | var level = ''; 63 | titleAddAnchor(header, id); 64 | tocs.push({ 65 | name: title, 66 | level: level, 67 | url: id, 68 | children: [] 69 | }); 70 | } 71 | 72 | function handlerH2Toc(config, count, header, tocs, pageLevel) { 73 | var title = header.text(); 74 | var id = header.attr('id'); 75 | var level = ''; 76 | 77 | if (tocs.length <= 0) { 78 | titleAddAnchor(header, id); 79 | return; 80 | } 81 | 82 | var h1Index = tocs.length - 1; 83 | var h1Toc = tocs[h1Index]; 84 | titleAddAnchor(header, id); 85 | h1Toc.children.push({ 86 | name: title, 87 | level: level, 88 | url: id, 89 | children: [] 90 | }); 91 | } 92 | 93 | function handlerH3Toc(config, count, header, tocs, pageLevel) { 94 | var title = header.text(); 95 | var id = header.attr('id'); 96 | var level = ''; 97 | 98 | if (tocs.length <= 0) { 99 | titleAddAnchor(header, id); 100 | return; 101 | } 102 | var h1Index = tocs.length - 1; 103 | var h1Toc = tocs[h1Index]; 104 | var h2Tocs = h1Toc.children; 105 | if (h2Tocs.length <= 0) { 106 | titleAddAnchor(header, id); 107 | return; 108 | } 109 | var h2Toc = h1Toc.children[h2Tocs.length - 1]; 110 | titleAddAnchor(header, id); 111 | h2Toc.children.push({ 112 | name: title, 113 | level: level, 114 | url: id, 115 | children: [] 116 | }); 117 | } 118 | 119 | function handlerFloatNavbar($, tocs, page) { 120 | var config = Config.config; 121 | var float = config.float; 122 | var level1Icon = ''; 123 | var level2Icon = ''; 124 | var level3Icon = ''; 125 | if (float.showLevelIcon) { 126 | level1Icon = float.level1Icon; 127 | level2Icon = float.level2Icon; 128 | level3Icon = float.level3Icon; 129 | } 130 | 131 | var html = "
          "; 132 | for (var i = 0; i < tocs.length; i++) { 133 | var h1Toc = tocs[i]; 134 | html += "
        • " + h1Toc.level + "" + h1Toc.name + "
        • "; 135 | if (h1Toc.children.length > 0) { 136 | html += "
            " 137 | for (var j = 0; j < h1Toc.children.length; j++) { 138 | var h2Toc = h1Toc.children[j]; 139 | html += "
          • " + h2Toc.level + "" + h2Toc.name + "
          • "; 140 | if (h2Toc.children.length > 0) { 141 | html += ""; 147 | } 148 | } 149 | html += "
          " 150 | } 151 | } 152 | 153 | html += "
        "; 154 | 155 | page.content = html + $.html(); 156 | } 157 | 158 | function handlerPageTopNavbar($, tocs, page) { 159 | var html = buildTopNavbar($, tocs, page); 160 | html += ""; 161 | page.content = html + $.html(); 162 | } 163 | 164 | function buildTopNavbar($, tocs, page) { 165 | var config = Config.config; 166 | var pageTop = config.pageTop; 167 | var level1Icon = ''; 168 | var level2Icon = ''; 169 | var level3Icon = ''; 170 | if (pageTop.showLevelIcon) { 171 | level1Icon = pageTop.level1Icon; 172 | level2Icon = pageTop.level2Icon; 173 | level3Icon = pageTop.level3Icon; 174 | } 175 | 176 | var html = "
          "; 177 | for (var i = 0; i < tocs.length; i++) { 178 | var h1Toc = tocs[i]; 179 | html += "
        • " + h1Toc.level + "" + h1Toc.name + "
        • "; 180 | if (h1Toc.children.length > 0) { 181 | html += "
            " 182 | for (var j = 0; j < h1Toc.children.length; j++) { 183 | var h2Toc = h1Toc.children[j]; 184 | html += "
          • " + h2Toc.level + "" + h2Toc.name + "
          • "; 185 | if (h2Toc.children.length > 0) { 186 | html += ""; 192 | } 193 | } 194 | html += "
          " 195 | } 196 | } 197 | 198 | html += "
        "; 199 | 200 | return html; 201 | } 202 | 203 | function start(bookIns, page) { 204 | var $ = cheerio.load(page.content); 205 | 206 | var tocs = handlerTocs($, page); 207 | 208 | if (tocs.length == 0) { 209 | page.content = $.html(); 210 | return; 211 | } 212 | if(!//.test(page.content)){ 213 | var config = Config.config; 214 | var mode = config.mode; 215 | if (mode == 'float') { 216 | handlerFloatNavbar($, tocs, page); 217 | } else if (mode == 'pageTop') { 218 | handlerPageTopNavbar($, tocs, page); 219 | } 220 | } 221 | 222 | var $x = cheerio.load(page.content); 223 | $x('extoc').replaceWith($x(buildTopNavbar($, tocs, page))); 224 | page.content = $x.html(); 225 | } 226 | 227 | module.exports = start; 228 | -------------------------------------------------------------------------------- /_book/gitbook/gitbook-plugin-ancre-navigation/style/plugin.css: -------------------------------------------------------------------------------- 1 | #anchor-navigation-ex-navbar { 2 | background-color: #fafafa; 3 | border: 1px solid rgba(0, 0, 0, .07); 4 | border-radius: 1px; 5 | -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); 6 | box-shadow: 0 6px 12px rgba(0, 0, 0, .175); 7 | background-clip: padding-box; 8 | padding: 5px 10px; 9 | position: fixed; 10 | /*background-color: rgba(255,255,255,0.98);*/ 11 | right: 50px; 12 | top: 68px; 13 | font-size: 12px; 14 | white-space: nowrap; 15 | z-index: 999; 16 | cursor: pointer; 17 | text-align: right; 18 | max-height: 70%; 19 | overflow-y: auto; 20 | overflow-x: hidden; 21 | } 22 | 23 | #anchor-navigation-ex-navbar ul { 24 | display: none; 25 | text-align: left; 26 | padding-right: 10px; 27 | padding-left: 10px; 28 | list-style-type: none; 29 | } 30 | 31 | #anchor-navigation-ex-navbar:hover ul { 32 | display: block; 33 | } 34 | 35 | #anchor-navigation-ex-navbar ul li a { 36 | text-decoration: none; 37 | border-bottom: none; 38 | font-size: 14px; 39 | color: #364149; 40 | background: 0 0; 41 | text-overflow: ellipsis; 42 | overflow: hidden; 43 | white-space: nowrap; 44 | position: relative; 45 | } 46 | 47 | #anchor-navigation-ex-navbar ul li a:hover { 48 | text-decoration: underline; 49 | } 50 | 51 | #anchor-navigation-ex-navbar ul li .title-icon { 52 | padding-right: 4px; 53 | } 54 | 55 | 56 | .book.color-theme-1 #anchor-navigation-ex-navbar { 57 | background-color: #111111; 58 | border-color: #7e888b; 59 | color: #afa790; 60 | } 61 | 62 | .book.color-theme-1 #anchor-navigation-ex-navbar ul li a { 63 | color: #877f6a; 64 | } 65 | 66 | .book.color-theme-2 #anchor-navigation-ex-navbar { 67 | background-color: #2d3143; 68 | border-color: #272a3a; 69 | color: #bcc1d2; 70 | } 71 | 72 | .book.color-theme-2 #anchor-navigation-ex-navbar ul li a { 73 | color: #c1c6d7; 74 | } 75 | 76 | 77 | #anchorNavigationExGoTop { 78 | position: fixed; 79 | right: 50px; 80 | bottom: 68px; 81 | background-color: #fafafa; 82 | border: 1px solid rgba(0, 0, 0, .07); 83 | border-radius: 1px; 84 | -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); 85 | box-shadow: 0 6px 12px rgba(0, 0, 0, .175); 86 | background-clip: padding-box; 87 | z-index: 999; 88 | cursor: pointer; 89 | font-size: 12px; 90 | padding: 5px 10px; 91 | color: #364149; 92 | } 93 | 94 | .book.color-theme-1 #anchorNavigationExGoTop { 95 | background-color: #111111; 96 | border-color: #7e888b; 97 | color: #afa790; 98 | } 99 | 100 | .book.color-theme-2 #anchorNavigationExGoTop { 101 | background-color: #2d3143; 102 | border-color: #272a3a; 103 | color: #bcc1d2; 104 | } 105 | 106 | a.anchor-navigation-ex-anchor { 107 | color: inherit !important; 108 | display: none; 109 | margin-left: -30px; 110 | padding-left: 40px; 111 | cursor: pointer; 112 | position: absolute; 113 | top: 0; 114 | left: 0; 115 | bottom: 0; 116 | } 117 | 118 | a.anchor-navigation-ex-anchor i { 119 | margin-left: -30px; 120 | vertical-align: middle; 121 | font-size: 16px !important; 122 | } 123 | 124 | h1, h2, h3, h4, h5, h6 { 125 | position: relative; 126 | } 127 | 128 | h1:hover a.anchor-navigation-ex-anchor, h2:hover a.anchor-navigation-ex-anchor, h3:hover a.anchor-navigation-ex-anchor, 129 | h4:hover a.anchor-navigation-ex-anchor, h5:hover a.anchor-navigation-ex-anchor, h6:hover a.anchor-navigation-ex-anchor { 130 | display: inline-block; 131 | } 132 | 133 | .book .book-body .page-wrapper .page-inner section.normal { 134 | overflow: visible; 135 | } 136 | 137 | 138 | #anchor-navigation-ex-pagetop-navbar{ 139 | border: 1px solid rgba(0, 0, 0, .07); 140 | border-radius: 1px; 141 | background-clip: padding-box; 142 | padding: 5px 10px; 143 | background-color: #fafafa; 144 | font-size: 12px; 145 | } 146 | 147 | #anchor-navigation-ex-pagetop-navbar ul { 148 | text-align: left; 149 | padding-right: 10px; 150 | padding-left: 10px; 151 | list-style-type: none; 152 | } 153 | 154 | #anchor-navigation-ex-pagetop-navbar:hover ul { 155 | display: block; 156 | } 157 | 158 | #anchor-navigation-ex-pagetop-navbar ul li a { 159 | text-decoration: none; 160 | border-bottom: none; 161 | font-size: 14px; 162 | color: #364149; 163 | background: 0 0; 164 | text-overflow: ellipsis; 165 | overflow: hidden; 166 | white-space: nowrap; 167 | position: relative; 168 | } 169 | 170 | #anchor-navigation-ex-pagetop-navbar ul li a:hover { 171 | text-decoration: underline; 172 | } 173 | 174 | #anchor-navigation-ex-pagetop-navbar ul li .title-icon { 175 | padding-right: 4px; 176 | } 177 | 178 | 179 | .book.color-theme-1 #anchor-navigation-ex-pagetop-navbar { 180 | background-color: #111111; 181 | border-color: #7e888b; 182 | color: #afa790; 183 | } 184 | 185 | .book.color-theme-1 #anchor-navigation-ex-pagetop-navbar ul li a { 186 | color: #877f6a; 187 | } 188 | 189 | .book.color-theme-2 #anchor-navigation-ex-pagetop-navbar { 190 | background-color: #2d3143; 191 | border-color: #272a3a; 192 | color: #bcc1d2; 193 | } 194 | 195 | .book.color-theme-2 #anchor-navigation-ex-pagetop-navbar ul li a { 196 | color: #c1c6d7; 197 | } 198 | 199 | 200 | 201 | 202 | 203 | 204 | -------------------------------------------------------------------------------- /_book/gitbook/gitbook-plugin-code/plugin.css: -------------------------------------------------------------------------------- 1 | #code-textarea { 2 | height: 0; 3 | position: fixed; 4 | top: -1000px; 5 | width: 0; 6 | } 7 | 8 | .code-wrapper { 9 | position: relative; 10 | } 11 | 12 | .code-wrapper i { 13 | color: #c1c7cd; 14 | cursor: pointer; 15 | font-size: 12px; 16 | font-weight: bold; 17 | position: absolute; 18 | right: 1em; 19 | top: 1em; 20 | } 21 | 22 | .code-wrapper pre { 23 | background: #f7f8f9; 24 | border-radius: 3px; 25 | counter-reset: line; 26 | font-size: 15px; 27 | } 28 | 29 | .code-wrapper pre > code > span.code-line:before { 30 | counter-increment: line; 31 | color: #c1c7cd; 32 | content: counter(line); 33 | display: inline-block; 34 | font-size: 12px; 35 | margin-right: 1.5em; 36 | width: 1em; 37 | } 38 | -------------------------------------------------------------------------------- /_book/gitbook/gitbook-plugin-code/plugin.js: -------------------------------------------------------------------------------- 1 | require(['gitbook', 'jQuery'], function(gitbook, $) { 2 | 3 | const TERMINAL_HOOK = '**[terminal]' 4 | 5 | var pluginConfig = {}; 6 | var timeouts = {}; 7 | 8 | function addCopyButton(wrapper) { 9 | wrapper.append( 10 | $('') 11 | .click(function() { 12 | copyCommand($(this)); 13 | }) 14 | ); 15 | } 16 | 17 | function addCopyTextarea() { 18 | 19 | /* Add also the text area that will allow to copy */ 20 | $('body').append('