在之前的博客《conan入门(四):conan 引用第三方库示例》中我们以cJSON为例说明了如何在项目中引用一个conan 包。
如何将自己设计的模块以conan包形式打包提供给第三方(客户/同事)使用?
本文将以一个基于 cJSON 库实现字符串解析的模块 jsonlib,说明如何将自己的模块封装成conan提供给第三方使用。
示例的所有源码都保存在GIT仓库 conan_example的jsonlib分支,请直接克隆代码到本地:
git clone https://gitee.com/l0km/conan_example.git -b jsonlib
# -b jsonlib 切换到 jsonlib 分支,等价于 'git checkout jsonlib' 命令
jsonlib是个很简单的库,只有两个文件:jsonlib.h,jsonlib.c
,只实现了一个功能从JSON字符串中解析name指定的字段内容,结果保存到输出缓冲区,JSON解析的工作实际上是调用cJSON来实现的。以下是实现源码。
/*
* jsonlib.h
* conan example for library
* Created on: 2022/02/18
* Author: guyadong
*/
#ifndef SRC_JSONLIB_H_
#define SRC_JSONLIB_H_
#ifndef JSONLIB_DLL_DECL
# if defined(_WIN32) && !defined(__CYGWIN__) && defined(JSONLIB_IS_DLL)
# if defined(JSONLIB_EXPORTS)
# define JSONLIB_DLL_DECL __declspec(dllexport)
# else
# define JSONLIB_DLL_DECL __declspec(dllimport)
# endif
# else
# define JSONLIB_DLL_DECL
# endif
#endif
#include <stddef.h>
#ifdef __cplusplus
extern "C"
{
#endif
//************************************
// 从JSON字符串中解析name指定的字段内容,结果保存到输出缓冲区
// @param const char * cjs JSON 字符串
// @param const char * name 字段名
// @param char * buffer [out] 输出缓冲区
// @param size_t bufsz 输出缓冲区长度
// @return int 成功返回0,否则返回-1
//************************************
JSONLIB_DLL_DECL int jlib_parse_field(const char* cjs,const char*name, char*buffer,size_t bufsz);
#ifdef __cplusplus
}
#endif
#endif /* SRC_JSONLIB_H_ */
#include "jsonlib.h"
#include <cjson/cJSON.h>
#include <string.h>
#include <stdlib.h>
JSONLIB_DLL_DECL int jlib_parse_field(const char* cjs,const char*name, char*buffer,size_t bufsz)
{
int c = -1;
if(cjs && name && buffer && bufsz)
{
cJSON* j = cJSON_Parse(cjs);
if(j)
{
cJSON* item = cJSON_GetObjectItem(j,name);
if(item)
{
char* s = cJSON_PrintUnformatted(item);
if(s && strlen(s) < bufsz)
{
strncpy(buffer, s, bufsz);
c = 0;
}
free(s);
}
}
cJSON_Delete(j);
}
return c;
}
cmake_minimum_required(VERSION 3.10.3)
# 3.0以上版本才允许使用VERSION option
project(JsonTest VERSION 1.0.0 LANGUAGES C)
find_package(cJSON REQUIRED)
add_library(jsonlib src/jsonlib.c)
set_target_properties(jsonlib PROPERTIES PUBLIC_HEADER "src/jsonlib.h")
target_link_libraries(jsonlib PUBLIC cjson)
target_compile_definitions(jsonlib
PRIVATE $<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,SHARED_LIBRARY>:JSONLIB_EXPORTS>
PUBLIC $<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,SHARED_LIBRARY>:JSONLIB_IS_DLL>
)
现在 jsonlib.h,jsonlib.c,CMakeLists.txt三个文件构成了一个简单但完整的标准CMake项目。
conan_example
│ CMakeLists.txt
│
└─src
jsonlib.c
jsonlib.h
怎么把这个小项目封装为conan包呢?如果你看过我的上一篇博客《conan入门(六):conanfile.txt conanfile.py的区别》以及《conan入门(一):conan 及 JFrog Artifactory 安装》就差不多可以知道,一个conan包最关键的就是需要有一个conanfile.py
脚本来定义包的各种配置。
但是要手写一个conanfile.py好像还挺麻烦的,我对python也是只知皮毛。
显然手写是不可能的,conan new
命令就是用于创建一个新的conan配置文件模板。有了模板,在之上修改就可以了:
$ conan new jsonlib/1.0.0 --template cmake_lib
File saved: CMakeLists.txt
File saved: conanfile.py
File saved: src/jsonlib.cpp
File saved: src/jsonlib.h
File saved: test_package/CMakeLists.txt
File saved: test_package/conanfile.py
File saved: test_package/src/example.cpp
conan new jsonlib/1.0.0 --template cmake_lib
执行后生成的目录结构如下,可以看到conan很贴心的生成了conanfile.py,jsonlib的源码文件及CMakeLists.txt以及对应的测试代码(test_package)—这是一个完整的conan包项目框架,虽然它不是想我们需要的内容,但有了这个框架总比从头开始写要方便得多:
conan_example
│ CMakeLists.txt
│ conanfile.py
│
├─src
│ jsonlib.cpp
│ jsonlib.h
│
└─test_package
│ CMakeLists.txt
│ conanfile.py
│
└─src
example.cpp
上面执行
conan new
命令使用的 --template cmake_lib 参数是Conan的一项目还在实验中的功能,根据指定模板生成Conan项目框架,详细说明参见Conan官方文档:《Package scaffolding for conan new command》conan new
命令的详细说明参见Conan官方文档《conan new》
现在我们在Conan生成的项目框架上,把jsonlib装进去。
首先如下图修改conanfile.py
如下图将conan new 生成的CMakeLists.txt的内容合并到jsonlib的CMakeLists.txt中
src/jsonlib.c替换conan new
生成的src/jsonlib.cpp,
src/jsonlib.h 替换conan new
生成的src/jsonlib.h
将conan new
生成的test_package/src/example.cpp替换为如下代码,实现简单的jsonlib接口测试
test_package/src/example.cpp
#include <stdio.h>
#include <jsonlib.h>
int main(int argc, char** argv)
{
char jstr[] = "{\"hello\":\"world\"}";
printf("JSON:%s\n",jstr);
char fieldbuf[32];
int c = jlib_parse_field(jstr,"hello",fieldbuf,sizeof(fieldbuf));
if(0 == c){
printf("parsed 'hello' field: %s\n",fieldbuf);
}
return 0;
}
创建了conanfile.py后,编译conan包非常简单
# 注意是 conan_example jsonlib分支
$ cd conan_example
$ conan create .
Exporting package recipe
jsonlib/1.0.0 exports_sources: Copied 1 '.txt' file: CMakeLists.txt
jsonlib/1.0.0 exports_sources: Copied 1 '.c' file: jsonlib.c
jsonlib/1.0.0 exports_sources: Copied 1 '.h' file: jsonlib.h
jsonlib/1.0.0: A new conanfile.py version was exported
jsonlib/1.0.0: Folder: C:\Users\guyadong\.conan\data\jsonlib\1.0.0\_\_\export
jsonlib/1.0.0: Exported revision: 5f5439068d01c7592ebbcb724712ce89
.....
.....
Capturing current environment in deactivate_conanrunenv-release-x86_64.bat
Configuring environment variables
JSON:{"hello":"world"}
parsed 'hello' field: "world"
conan create .
根据配置文件 (同级文件夹下的conanfile.py) 构建二进制包conan create
命令行用法参见Conan官方文档《conan create》
conan create
执行成功后,就会将生成的二进制包保存在本地仓库$HOME/.conan/data/jsonlib/1.0.0/_/_package
执行conan search jsonlib/1.0.0@
会显示二进制包的信息
项目编译成功就可以上执行conan upload
传到私有制品库了:
conan upload jsonlib/1.0.0 -r ${repo} --all
# ${repo}为私有制品库的名字
–all 指定上传所有内容(配置文件conanfile.py,源码和二进制包),如果不指定些选项,只上传除二进制包之外的所有文件 关于
conan upload
命令的详细说明参见Conan官方文档:《conan upload》
上传成功进入JFrog Artifactory后台就可以看到已经上传的package
上面一套流程做完,可以总结一下,将一个已经有项目封装为conan包的过程:
开始麻烦些,需要用conan new
创建一个模板,然后修改模板,将自己的代码装进去。如果你对python很熟,可以不需要conan new
创建模板项目,自己手工改就好了。
后面就很简单 :
conan create
完成conan 二进制包编译
conan upload
负责 conan包的上传发布