Skip to content
This repository has been archived by the owner on May 28, 2019. It is now read-only.

broc tutorial

doublesongsong edited this page Dec 28, 2015 · 10 revisions

Copyright 2015, Baidu.com, Inc.

章节

Broc是什么


> Broc有那些优势
> 安装配置
> 术语
> 使用前必读
> broc命令
> BROC文件模板
> 使用场景
>> 场景1 如何生成BROC文件
>> 场景2 如何生成执行文件或静态库
>> 场景3 如何处理proto文件
>> 场景4 如何引入依赖模块
>> 场景5 标签CONFIGS支持哪些用法
>> 场景6 如何解决依赖冲突
>> 场景7 如何区分debug和release编译
>> 场景8 如何查看模块的依赖关系
>> 场景9 如何指定编译器的版本
>> 场景10 如何进行单元测试
>> 场景11 如何添加头文件搜索路径
>> 场景12 如何指定动态链接库和静态链接库
>> 场景13 开源代码是否需要也需要改造成Broc的构建方式

Broc是什么

broc是一款面向c/c++语言的构建工具,提供了编译、分支依赖、依赖模块下载等功能。不同于make, cmake等以库文件链接的编译方式,broc将依赖库源代码与程序源代码一并同时进行编译,这种方式可以避免程序源代码与依赖库编译选项或平台不一致导致的软件兼容性问题;broc支持构建和运行单元测试用例;借助Jenkins broc还能完成软件的持续集成

Broc有那些优势

  • 源码编译 broc支持将程序以及依赖库从源码状态进行编译,解决了c/c++程序因编译选项、操作系统平台或库文件版本不同而造成的兼容性问题;

  • 代码自动下载 broc支持自动checkout依赖模块源代码到本地然后进行编译,用户无需编写控制脚本来实现此功能;

  • 分支依赖 broc支持指定依赖库的主干、分支和TAG,broc能自动识别并解决库文件传递性依赖问题;

  • 语法简单 Makefile复杂的语法让人望而却步,而broc语法简洁,易上手;

  • 增量编译靠谱 make通过文件的修改时间来判断文件是否需要进行再编译,当文件版本回滚后或编译参数变更后,make不会对文件再次进行编译;broc除了使用文件的修改时间,还基于文件内容的哈希值以及编译参数作为增量编译的判断标准,增量编译的准确性会更高;

  • 支持protobuf broc内置对protobuf的支持,将proto文件编译成静态库.a文件一步完成

安装配置

系统要求

  • 操作系统 Linux

工具要求

  • Python<br> 版本 >= 2.6.6, 建议使用2.7版本
  • git<br> 版本 >= 2.1.2
  • svn<br> 版本 >= 1.7
  • gcc/g++<br> broc使用g++/gcc进行编译,需要事先安装好
  • protobuf<br> broc使用了protobuf,自带了protobuf的安装包

安装

  • 下载代码<br> git clone https://github.com/baidu/broc.git baidu/broc

  • 安装protobuf<br> 进入到tools目录执行sh install protobuf, protobuf被安装在$HOME/protobuf目录下

  • 处理proto文件<br> 进入到tools目录下面执行sh install proto, 生成broc自用的py文件

  • 设置PATH<br> 将broc的client目录设置到PATH中, 例如:export PATH=$HOME/broc/client:$PATH

##broc配置 broc安装完后,执行broc命令可以生成broc的配置文件,配置文件位于用户的$HOME目录下:~/.broc.rc 配置文件内容:

*[repo]


> svn_repo_domain = https://github.com
> git_repo_domain = https://github.com
> svn_postfix_branch = BRANCH
> svn_postfix_tag = PD_BL*
  • svn_repo_domain svn仓库域名
  • git_repo_domain git仓库域名
  • svn_postfix_branch svn url中branch名后缀
  • svn_postfix_tag svn url中tag名后缀

参数svn_repo_domain和git_repo_domain分别表示SVN和GIT代码库的域名,配置成自己域名即可。 一个依赖库出现不同版本时需要对版本进行对比,确定编译时要使用的依赖模块版本; SVN管理的依赖模块版本的对比是通过模块的svn url来完成的,因此broc要求svn url有统一的命名方法。在SVN URL中进行了详细说明,可以参照此方法根据自己的实际情况对 svn_postfix_branch, svn_postfix_tag进行配置。

术语

在阅读本章节前,需要对broc的术语有一个基本的了解,参照构建规则

源码编译

源码编译,简言之就是在程序编译时,将程序与依赖库一并从源码状态同时进行编译。 一般情况下,程序或多或少都会依赖第三方的库,这样就会遇到应用程序和库文件编译选项不同、库文件版本不同或操作系统平台不同而造成的兼容性问题,如何彻底解决这些问题呢?答案就是源码编译。编译应用程序时,将程序与依赖库一块从源码进行编译,应用程序与库文件在编译环境一致的情况下,兼容性的问题也就能彻底避免。 而这么做会把各个依赖库的编译工作从库的开发者身上,转移到每个应用程序的开发者身上,增加了编译的复杂度和工作量。broc可以完美的解决这一难题,通过BROC文件,broc管理应用程序和依赖库间的依赖关系以及各自的构建规则,broc自动checkout依赖库的源代码,与应用程序源代码一并完成编译。

分支依赖

分支依赖是指标签CONFIGS指定依赖库时,可以指定依赖库的某个branch和tag。主干可以看做是一种特殊的分支,主干依赖是分支依赖中一种特殊情况,在SVN中主干依赖是指主模块依赖依赖模块的trunk代码;在GIT中主干依赖是指主模块依赖依赖模块的master代码。主干依赖要求依赖模块保证最新的代码能够兼容之前所有已发布的版本,这对库的作者提出了很高的要求:依赖库的源代码从始至终都保持版本的兼容性。而实际的情况是库的作者会发布若干版本,在同一个大版本下能保证库文件的兼容性,broc提供了分支依赖,恰好支持这种情况。

检出路径

对SVN而言,模块的trunk, branch和tag的URL地址不一样,所以broc要求同一个模块在trunk, branch和tag三种情况下检出(checkout)到本地时,检出路径保持不变,同时还保留依赖模块的组织部门、产品线等信息。

对Git而言,模块的不同分支、TAG代码对应的URL地址一样, 只保留模块的组织部门、产品线信息即可。

SVN中代码的检出路径

模块app存在trunk,branch和tag, 其SVN路径分别为:

在以上url中: http://github.com是svn服务器域名 zeus/et/tools是产品线名 trunk, branches, tags分别表示对应的URL地址是模块的主干、分支和TAG app是模块名称 app_1-3-5_BRANCH是分支名 app_1-3-5_PD_BL是TAG名

在checkout app时,本地的检出路径为zeus/et/tools/app,检出路径中保留产品线、模块等相关信息。检出命令分别为:

GIT中代码的检出路径

broc推荐使用一个模块一个repo的模式,例如

在以上url中: http://git.baidu.com是域名 et/tools 是产品线名 app和broc是模块名

在Git下模块的分支、主干和TAG的url地址一样,所以checkout代码时,检出路径中去掉url中的域名,保留产品线名和模块名,检出目录变为et/tools/app,et/tools/broc

检出命令为:

产出目录

broc将编译结果保存在工作空间的broc_out目录下,目录broc_out 称之为产出目录。按照编译结果类型编译产出目录又细分为bin, lib, test等子目录, 详见产出目录

使用前必读

  • 代码纳入版本控制系统 broc要求代码必须在纳入SVN或Git的版本管理之下,并且本地的代码检出目录必须满足检出路径的要求,详情例子参考工作空间

  • BROC文件位置 BROC文件要放在代码的根目录下,详见BROC文件

  • 工作空间 工作空间(workspace)是broc进程当前的工作目录,用**$WORKSPACE表示工作空间目录的路径。 在workspace下存放项目以及所有依赖库的源代码,以及所有的构建产出。构建产出根目录使用$OUT_ROOT**表示,$OUT_ROOT=$WORKSPACE + "/" + "broc_out", 详情参考工作空间

  • 头文件包含 broc默认的头文件搜索路径包含两个:工作空间目录($WORKSPACE)和产出根目录($OUT_ROOT)。在包含头文件时,建议include头文件的检出路径,这样可以避免手动指定头文件搜索路径,详情参考头文件搜索路径 broc不自动添加依赖模块头文件路径到头文件搜索路径中,可以通过在标签INCLUDE和Include指定头文件搜索路径,详情参照INCLUDE/Include

  • 库文件链接 broc默认不链接CONFIGS标签中指定的依赖库. 如果需要链接,对静态库.a文件需要使用标签Libs进行指定;对于动态库.so文件,需要在LDFLAGS和LdFlags标签中作为链接参数进行指定,详情参考LDFLAGS/LDFlags

  • proto文件 broc将proto文件处理成静态库.a文件。 使用时要注意在标签PROTO_LIBRARY中需要指定protobuf的头文件搜索路径;详情参考proto文件

#broc命令 执行broc help输出broc命令帮助,最常用的是broc build

$ broc
Usage: broc <subcommand> [option] [args]
Type broc help <subcommand> for help on a specific subcommand
Available subcommands:
    build      : Build the specified targets
    test       : Build and runs the specified targets
    show-deps  : Print the dependency graph
    clean      : Remove output files
    scratch    : Create a BCLOUD template
    version    : Display the version
    config     : Display broc's config items
    help       : Print the help commands

#BROC文件模板 执行broc scratch会在当前目录下生成BROC文件模板

#edit-mode: -*- python -*-
#coding:utf-8

#set the search path of compiler comamnd
COMPILER("/usr/bin")

#Preprocessor flags.
CPPFLAGS('debug flags', 'release flags')

#C flags.
CFLAGS('debug flags', 'release flags')

#C++ flags.
CXXFLAGS('debug flags', 'release flags')

#-I path
INCLUDE('./include', 'a/b/c/include')

#link flags
LDFLAGS('-lpthread -lcrypto -lrt')

#trunk dependent module in svn
CONFIGS("app/foo/sky@trunk")
CONFIGS("app/foo/sky@trunk@12345")

#branch dependent module in svn
CONFIGS("app/foo/sky@sky_1-0-0-0_BRANCH")
CONFIGS("app/foo/sky@sky_1-0-0-0_BRANCH@12345")

#tag dependent module in svn
CONFIGS("app/foo/sky@sky_1-0-0-1_PD_BL")

#master dependent module in git
CONFIGS("sky@master@branch")

#branch dependent module in git
CONFIGS("sky@dev@branch")

#tag dependent module in git
CONFIGS("sky@v1.0.0@tag")

#mv file or dirctory to $OUT
PUBLISH("relative path to BROC", "$OUT")

#bin
APPLICATION('name',
            Sources("src/*.cpp"),
            LinkFlags("link flags"),
            Libs("$OUT_ROOT/a/b/c/output/lib/libutil.a"))
 
#ut bin
UT_APPLICATION('name',
               Sources("src/*.cpp"),
               LinkFlags("link flags"),
               Libs("$OUT_ROOT/a/b/c/output/lib/libutil.a"),
               UTArgs(""))
 
#static library file .a
#STATIC_LIBRARY('name',
               Sources("src/*.cpp"),
               Libs("$OUT_ROOT/a/b/c/output/lib/libutil.a"))
 
#proto static library file .a
PROTO_LIBRARY('name',
              'proto/*.proto',
               Proto_Flags(""),
               include(""),
               CppFlags("debug flags", "release flags"),
               CxxFlags("debug falgs","release flas"),
               Libs("$OUT_ROOT/a/b/c/output/lib/libutil.a"))

使用场景

场景1 如何生成BROC文件

执行broc scratch在当前目录下生成BROC文件模板。如果当前目录下已经存在BROC文件,给出提示不会生成。BROC文件存放在源代码的根目录下,所在的目录被认为是一个独立的构建单元,它的作用域范围包括该目录下所有文件和子目录。一个BROC文件中只能管理其作用域下的文件,在标签中指定这些文件时要使用以BROC文件目录为起点的相对路径。

BROC文件所在目录为/home/broc/test,BROC文件中Sources("src/*.cpp"), 表示子目录src下所有的cpp源文件

/home/broc/test
              |-- BROC
              `-- src
                  |-- hello.cpp
                  |-- hello.h
                  `-- main.cpp

场景2 如何生成可执行文件或静态库

我们创建一个演示模块app,模块的SVN路径为:http://https://github.com/zeus/et/tools/trunk/app url中域名为http://https://github.com/zeus, 模块的检出路径为et/tools/app, 假设workspace目录为 /home/broc/workspace checkout代码到本地:svn checkout http://https://github.com/zeus/et/tools/trunk/app /home/broc/workspace/et/tools/app

hello.cpp为

#include <stdio.h>
int main()
{
     printf("hello Broc\n");
     return 0;
}

要生成执行文件hello, BROC文件为:

#edit-mode: -*- python -*-
#coding:utf-8

APPLICATION('hello', Sources("hello.cpp"))

在目录/home/broc/workspace/et/tools/app下执行broc build,编译结束后可以发现在/home/broc/workspace/et/tools/app下多了一个output软连接,包含了对应的编译结果

output
|-- bin
|   `-- hello
`-- lib

如果想要生成静态库,只需将APPLICATION换成STATIC_LIBRARY即可

#edit-mode: -*- python -*-
#coding:utf-8

#APPLICATION('hello', Sources("hello.cpp"))
STATIC_LIBRARY('hello', Sources("hello.cpp"))

编译结束后,产出结果为:

output
|-- bin
|   `-- hello
`-- lib
	`-- libhello.a

场景3 如何处理proto文件

在场景2例子的基础上,说明在broc如何支持proto文件。 添加person.proto文件/home/broc/workspace/et/tools/app/proto/person.proto,其内容如下:

message Person 
{
	required string name = 1;
  	required int32 id = 2;
  	optional string email = 3;
}

在BROC文件中添加PROTO_LIBRARY, 修改如下:

#edit-mode: -*- python -*-
#coding:utf-8

PROTO_LIBRARY("Person", "proto/*.proto", Include("/opt/protobuf/include"))

APPLICATION("hello",
			Sources("hello.cpp", Include("/opt/protobuf/include")),
			Libs("$OUT_ROOT/et/tools/app/output/lib/libPerson.a", "/opt/protobuf/lib/libprotobuf.a"))

标签PROTO_LIBRARY用于处理proto文件并生成静态库Person,其中:

  • person是静态库名称;
  • proto/*.proto是所有待处理的proto文件,broc支持通配符"*"
  • Include中指定了protoc命令中--proto_path(-I)参数,该参数既用于proto文件的搜索路径,也用于相关头文件的搜索路径。因为需要用到protobuf自身的头文件,所以在Include中指定了/opt/protobuf/include, 这是演示环境下protobuf头文件所在目录。 标签APPLICATION用于生成执行文件,其中:
  • hello 为执行文件的名字
  • Sources标签指定了要编译的源文件以及相关的编译选项,同时也需要指定Include目录
  • Libs目录指定了执行文件hello要链接的库文件,链接产出目录下的静态库,需要使用库文件的产出路径,并且以$OUT_ROOT开头

修改hello.cpp文件

#include <stdio.h>
#include "broc_out/et/tools/app/proto/Person.pb.h"
int main()
{
	  Person person;
	  person.set_name("broc");
    person.set_id(1234);
    person.set_email("broc@baidu.com");
    return 0;
}

在目录/home/broc/workspace/et/tools/app执行broc build, 编译结束后,proto的源文件和头文件在对应的产出目录下:

  • workspace目录 /home/broc/workspace
  • proto文件目录 /home/broc/workspace/et/tools/app/proto
  • proto结果目录 /home/broc/workspace/broc_out/et/tools/app/proto

可以看到proto的结果目录与proto文件目录的相对路径多了一个产出根目录broc_out 编译结束后,编译结果为:

output
|-- bin
|   `-- hello
`-- lib
    `-- libPerson.a

场景4 如何引入依赖模块

在场景3的例子中,引入依赖模块ub来说明如何使用CONFIGS标签添加依赖库。 ub的SVN URL地址为: http://https://github.com/zeus/public/tags/ub/ub_1-2-46-0_PD_BL 在app的BROC中使用CONFIGS标签添加依赖库ub,BROC文件如下:

#edit-mode: -*- python -*-
#coding:utf-8

CONFIGS("public/ub@ub_1-2-46-0_PD_BL")
PROTO_LIBRARY("Person", "proto/*.proto", Include("/opt/protobuf/include"))
APPLICATION("hello",
			       Sources("hello.cpp", Include(CONVERT_OUT("proto"), "/opt/protobuf/include")),
           	 Libs("/opt/protobuf/lib/libprotobuf.a", "$OUT_ROOT/et/tools/app/output/lib/libPerson.a","$OUT_ROOT/public/ub/output/lib/libub.a")
			)

修改hello.cpp

#include <stdio.h>
#include "broc_out/et/tools/app/proto/Person.pb.h"
#include "public/ub/src/ub.h"
int main()
{
	printf("hello broc\n");
	Person person;
	person.set_name("John Doe");
	person.set_id(1234);
	person.set_email("jdoe@example.com");
	ub _ub;
	_ub.is_ub();
	return 0;
}

在目录/home/broc/workspace/et/tools/app 下运行broc build

[broc@192.168.1.100 app]$ pwd/home/broc/workspace/et/tools/app
[broc@192.168.1.100 app]$Broc build
[2015-11-11 13:04:03 140123441956576] Analyzing dependency ...
[2015-11-11 13:04:03 140123441956576] Analyzing dependency success
[2015-11-11 13:04:03 140123441956576] noBroc cache and create a empty one
[2015-11-11 13:04:03 1085495648] mkdir -pBroc_out/et/tools/app && /home/zss/broc/protobuf/bin/protoc --cpp_out=broc_out/et/tools/app  -	I=/opt/protobuf/include  -I=broc_out/et/tools/app/proto  -I=broc_out/et/tools/app -I=et/tools/app  -I=. et/tools/app/proto/*.proto
[2015-11-11 13:04:03 140123441956576] 4 threads to build ...
[2015-11-11 13:04:03 1172273504] [OK] None
[2015-11-11 13:04:03 1140803936] [OK] mkdir -pBroc_out/et/tools/app/proto && g++ -c -DZUES -I. -I/opt/protobuf/include -	Ibroc_out/et/tools/app/proto -Ibroc_out/et/tools/app -oBroc_out/et/tools/app/proto/4_Person_Person.pb.oBroc_out/et/tools/app/proto/Person.pb.cc[2015-11-11 13:04:03 1151293792] [OK] mkdir -pBroc_out/et/tools/app && g++ -c -DZUES -I. -Ibroc_out/et/tools/app/proto -I/opt/protobuf/include -oBroc_out/et/tools/app/2_hello_hello.o et/tools/app/hello.cpp
[2015-11-11 13:04:03 1161783648] [OK] mkdir -pBroc_out/public/ub/src && g++ -c -DZUES -I.  -oBroc_out/public/ub/src/4_ub_ub.o public/ub/src/ub.cpp
[2015-11-11 13:04:03 1161783648] [OK] mkdir -pBroc_out/et/tools/app/output/lib && ar rcsBroc_out/et/tools/app/output/lib/libPerson.aBroc_out/et/tools/app/proto/4_Person_Person.pb.o
[2015-11-11 13:04:03 1161783648] [OK] mkdir -pBroc_out/public/ub/output/lib && ar rcsBroc_out/public/ub/output/lib/libub.aBroc_out/public/ub/src/4_ub_ub.o
[2015-11-11 13:04:04 1172273504] [OK] mkdir -pBroc_out/et/tools/app/output/bin && g++ -DBROC -oBroc_out/et/tools/app/output/bin/helloBroc_out/et/tools/app/2_hello_hello.o -Xlinker "-(" /opt/protobuf/lib/libprotobuf.aBroc_out/et/tools/app/output/lib/libPerson.aBroc_out/public/ub/output/lib/libub.a -Xlinker "-)"
[zss@db-scm-labs06.db01.baidu.com app]$ tree output/
output/
|-- bin
|   `-- hello
`-- lib
 	`-- libPerson.a
2 directories, 2 files
[broc@192.168.1.100 app]$ output/bin/hello
hello broc

场景5 标签CONFIGS支持哪些用法

broc通过标签CONFIGS指定依赖模块 SVN 例子

CONFIGS("app/foo/sky@trunk")                      # 依赖trunk最新代码
CONFIGS("app/foo/sky@trunk@12345")                # 依赖trunk 版本号为12345的代码
CONFIGS("app/foo/sky@trunk@12346")                # 依赖trunk 版本号为12346的代码
CONFIGS("app/foo/sky@sky_1-0-0-0_BRANCH")         # 依赖sky的分支1-0-0-0_BRANCH的最新代码
CONFIGS("app/foo/sky@sky_1-0-0-0_BRANCH@12345")   # 依赖sky的分支1-0-0-0_BRANCH 版本号为12345的代码
CONFIGS("app/foo/sky@sky_1-0-0-0_BRANCH@12346")   # 依赖sky的分支1-0-0-0_BRANCH 版本号为12346的代码
CONFIGS("app/foo/sky@sky_1-0-0-0_PD_BL")          # 依赖sky的TAG 1-0-0-0_PD_BL
CONFIGS("app/foo/sky@sky_1-0-0-1_PD_BL")          # 依赖sky的TAG 1-0-0-1_PD_BL

GIT例子

CONFIGS("sky@master@branch")                      # 依赖主干最新代码
CONFIGS("sky@v1.0.0@tag")                         # 依赖TAG v1.0.0的代码
CONFIGS("sky@dev@branch")                         # 依赖分支为dev的最新代码
CONFIGS("sky@v1.0.3@tag")                         # 依赖TAG v1.0.3的代码

场景6 如何解决依赖模块冲突

broc解决依赖模块冲突详见依赖打平 如果存在依赖模块冲突、或者循环依赖的模块,broc会给出错误提示。在本地代码根目录下,隐藏文件.BROC.ORIGIN.DEPS存放打平前的依赖模块树,可以查看是那些模块造成了冲突或者循环依赖。

场景7 如何区分debug和release编译

通过broc编译参数–-debug, --release调整模块的编译模式,broc默认是debug模式编译。 标签CPPFLAGS, CFLAGS, CXXFLAGS, LDFLAGS, CppFlags, CFlags, CxxFlags, LDFlags同时接收debug和release参数,根据指定的编译模式选择生效的参数。标签详见标签说明

场景8 如何查看模块的依赖关系

在BROC的目录下有是三个隐藏文件.BROC.ORIGIN.DEPS, .BROC.PLANISHED.DEPS和.BROC.FILE.DEPS

  • .BROC.ORIGIN.DEPS保存打平之前模块间的依赖关系;
  • .BROC.PLANISHED.DEPS保存打平之后模块间的依赖关系
  • .BROC.FILE.DEPS保存目标文件间的依赖关系,其中括号[]括住的文件,表示最新一次编译时增量编译的文件

场景9 如何指定编译器的版本

broc使用g++/gcc进行代码编译,通过标签COMPILER_PATH指定编译命令目录路径即可

场景10 如何进行单元测试

broc支持在软件构建成功后运行单元测试用例,通过标签UT_APPLICATION来定义单元测试用例,使用命令broc test触发构建和单元测试。 broc默认开启4个线程运行单元测试用例,如果单测的例子少于4个,以单测用例的个数为准 broc不运行依赖库的单测用例,只运行主模块的单测用例。

场景11 如何添加头文件搜索路径

broc在构建默认添加$WORKDPACE和$OUT_ROOT到添头文件搜索路径中(即g++/gcc命令的-I参数), 在包含头文件时建议使用头文件的[检出路径][#检出路径]。 broc提供了标签来INCLUDE和Include来添加头文件搜索路径,INCLUDE为全局标签,Include为局部标签。 两个标签都存在时,Include会覆盖INCLUDE

场景12 如何连接动态链接库和静态链接库

broc默认不链接CONFIGS标签指定的依赖库,依赖库需要用户手动指定:

  • 静态库通过标签LIBS或Libs来指定
  • 动态库通过标签LDFLAGS或者LDFlags作为链接参数进行指定

在使用标签LIBS/Libs,需要指定库文件的路径,库文件路径有两种形式: $OUT_ROOT开头的相对路径 CONFIGS标签指定的库,要使用"$OUT_ROOT/库文件产出路径"的形式, 其中"$OUT_ROOT" 表示产出根目录 绝对路径 对安装在操作系统本地的静态库文件,使用绝对路径,例如:"/opt/protobuf/lib/libprotobuf.a"

举例如下

  • 对BROC文件中CONFIGS指定的依赖库文件 LIBS("$OUT_ROOT/a/b/c/output/lib/libfoo.a") Libs("$OUT_ROOT/a/b/c/output/lib/libfoo.a")

  • 对安装在服务器本地的静态库文件 LIBS("/opt/protobuf/lib/libprotobuf.a") Libs("/opt/protobuf/lib/libprotobuf.a")

  • 对动态库而言,作为链接参数进行指定 LDFLAGS("-lpthread -lcrypto") LDFlags("-lpthread -lcrypto")

场景13 开源代码是否需要改造成broc的构建方式

broc可以通过以下三种方式使用开源代码: ###本地安装(不改造) 将开源代码在本地编译并安装好,在模块的BROC文件中使用标签Include, INCLUDE引入头文件搜索路径,使用Libs, LIBS标签引入依赖库文件即可。

以protobuf为例 将protobuf在本地编译并安装好,本地安装目录结构为: /opt/protobuf |-- bin |-- include `-- lib

在生成可执行文件hello时,链接库文件/opt/protobuf/lib/libprotobuf.a,同时添加头文件搜索路径"/opt/protobuf/include"

APPLICATION("hello", Source("hello.cpp",Include("/opt/protobuf/include")), Libs("/opt/protobuf/lib/libprotobuf.a"))

###本地编译后上传到SVN/GIT(半改造) 将开源代码在本地编译好,提取出头文件和库文件形成一个独立的依赖库,并编写好BROC文件一并保存到代码仓库中,供其他模块使用。

以protobuf 为例本地安装的基础上,编写对应的BROC文件,与安装目录下的所有文件一并纳入SVN或GIT。假设protobuf 的代码仓库的url为 https://git.baidu.com/et/tools/protobuf protobuf的目录结构如下: https://git.baidu.com/et/tools/protobuf |-- bin |-- include `-- lib

BROC文件

#edit-mode: -*- python -*-
#coding:utf-8
STATIC_LIBRARY('protobuf')
STATIC_LIBRARY('protobuf-lite')

将/opt/protobuf下所有文件,以及BROC文件保存到SVN/Git中 编写protobuf的BROC文件时需要注意:

  1. 库文件必须在代码仓库的lib目录下
  2. BROC文件中STATIC_LIBRARY标签只指定库文件的名称,不需要指定其他内容,并且库文件名称中不包含'lib'和'.a'字符串
  3. 在编译时,broc会将protobuf的库文件拷贝到protobuf的产出目录下,供其他模块使用

源代码完全该造成BROC方式(全改造)

此方式需要梳理protobuf的编译脚本,然后将其构建规则转换为BROC的方式,比较繁杂,此处不详细描述。

备注 该方式中使用依赖模块编译后的执行文件生成源文件,然后再进行编译,broc目前还不支持这种情况。例如:先编译protobuf生成执行文件protoc,然后使用protoc处理proto文件生成pb.cc文件,然后再编译。