There are 3 phrases in the C/C++ building: preprocessing, compiling, linking. with different flags.
The common CC attributes are:
-
warning
: str = ['yes', 'no'], Whether suppress all warningsExample:
warning='no'
. The default values is 'yes', so can be omitted. -
defs
:str = [], User define macrosExample:
defs=['_MT']
, can also has value, eg.'A=1'
. -
incs
: list(str) = [] header search pathsExample:
incs=['poppy/myinc']
.Usually used for thirdparty library, use full include path in our code is more recommended.
-
optimize
optimize flagsExample:
optimize=['-O3']
There is a separated
optimize
attribute from theextra_cppflags
, because it should be ignored in the debug mode, otherwise it will hinder debugging. but for some kind of libraries, which are mature enough and performance related, we rarely debug trace into them, such as hash, compress, crypto, you can specifyalways_optimize = True
. -
extra_cppflags
:list(str) = [], Extra C/C++ compile flags.Example:
extra_cppflags = ['-Wno-format-literal']
. many useful flags, such as-g
,-fPIC
are builtin. so it should be rarely used. -
extra_linkflags
:list(str) = [], Extra link flagsExample:
extra_linkflags = ['-fopenmp']
. many useful flags such-g
are already built in, so it should be rarely used. -
linkflags
: list = None, Overrides the global linkflags in the configuration.For example:
linkflags = ['-fopenmp']
.Commonly used flags such as
-g
are already built-in, and generally do not need to be specified. Because global options will be overridden, unless you really understand the link related options ofgcc
andld
, do not use this parameter lightly.
Build a C/C++ library
cc_library
can be used to build static and dynamic library. But only the static library will built default, unless it is depended by a
dynamic_linked cc_binary, or run blade build with --generate-dynamic
.
Example:
cc_library(
name='lowercase',
srcs=['lower/plowercase.cpp'],
hdrs=['lower/plowercase.h'],
deps=['#pthread'],
link_all_symbols=False
)
Attributes:
-
hdrs
: listDeclares the public interface header files of the library.
For normal CC libraries,
hdrs
should exist, otherwise the library may not be used. Therefore, this attribute is required, otherwise a diagnostic problem will be reported. If it surely does not exist, you should set it to empty([]) explicitly.The severity of the problem can be controlled by
cc_library_config.hdrs_missing_severity
. For problems that existed before hdrs support, you can usecc_library_config.hdrs_missing_suppress
to suppress them.Rules for generating header files during construction, such as
pb.h
generated byproto_library
orouts
of thegen_rule
target, if these header files are included, these header files will also be automatically included. Including the header file in the dependency management can avoid compilation or linking problems caused by the library that includes the header file but does not add the dependency, especially for dynamically generated header files.A header file can belong to multiple
cc_library
.cc_library
will not automatically exporthdrs
of othercc_library
that it depends on indeps
. Only public header files should be declared inhdrs
. For private header files, even if they are included in public header files, they do not need to be included. Private header files should be declared in itssrcs
.All CC libraries should be described by
cc_library
, especially for headers-only libraries. Because most library inevitably depends on other libraries, if dependency of the ordinary library is shortage, the "symbol not found" error will be reported during linking, it is easier to add missing dependencies based on error information, but for headers-only libraries, even indirect dependencies, errors are reported at the linking time of the final user, making it difficult to locate and fix the problem.Therefore, for header-only libraries, they also needs to be described with
cc_library
, its public header files need to be declared in itshdrs
, and its direct dependencies need to be listed in itsdeps
.If the granularity of the library is too large, some unnecessary dependencies will be included due to the enforcement of the
hdrs
checking. You should proper splitting the library to reduce unnecessary coupling.For example, gtest_prod.h of gtest is often used to declare testing support in product code, but it only contains some declarations and does not depend on the implementation part of the gtest library. It is suitable to declare it in a separate
gtest_prod
library instead of putting it intogtest
library, Otherwise, the gtest library may be linked into the product code. -
link_all_symbols
: bool = FalseIf you depends on the global initialization to register something, but didn't access these global object directly. it works for code in
srcs
of executable. but if the global is in a library, it will be discarded at the link time because linker find it is unsed.This attribute tells the linker don't discard any unused symbols in this library even if it seems to be unused.
You'd better put these self-registering code into a separated small library. otherwise, the whole library will be linked into the executable unconditionally, increase the size of the executable.
Youi also need to know, the
link_all_symbols
is the attribute of the library, not the user of it. Click here to learn more details if you have interesting. -
binary_link_only
: bool = FalseThis library can only be a depenedency of the executable targets (Such as
cc_binary
orcc_test
), but not normalcc_library
s. This attribute is useful for some exclusive libraries such as malloc library.For example,
tcmalloc
andjemalloc
are both malloc library which contains same symbols (such asmalloc
,free
), if acc_library
depends ontcmalloc
, the dependentcc_binary
may not depends onjemalloc
any more, otherwise linker will report multiplemalloc
definition. This problem can be solved by setting this attribute on both 'tcmalloc' and 'jemalloc'.A 'binary_link_only' library can depends on other 'binary_link_only' libraries.
cc_library( name = 'tcmaloc', binary_link_only = True, ... ) cc_library( name = 'jemaloc', binary_link_only = True, ... )
-
always_optimize
: bool = FalseTrue: Always optimize. False: Don't optimize in debug mode。 The default value is False。It only apply to cc_library.
-
prebuilt
: bool = FalseUse prebuilt in cc_library is deprecated. you should use
prebuilt_cc_library
. -
export_incs
: list = []Similar to
incs
, but it is transitive for all targets depends on it, even if indirect depends on it.NOTE:
There is no dependency code in the generated cc_library, both static and dynamic. But for the dynamic library, it contains the paths of dependencies.
These libraries can just be used locally (such as run tests),not fit for the production environment. If you want to build a shared library can be use in the environment, you should use
cc_plugin
, which will include the code from it dependencies.
In large-scale C++ projects, dependency management is very important, and header files have not been included in it for a long time.
Starting with Blade 2.0, header files have also been included in dependency management.
When a cc target includes a header file, it also needs to put the cc_library
it belongs to in its own deps
, otherwise Blade will check and report
the problem.
The lack of a declaration on the library to which the header files belong will cause the following problems:
- The dependencies between libraries cannot be transferred correctly. If the library to which an undeclared header file belongs adds new dependencies in the future, it may cause link errors.
- For the header files generated during the build, the lack of a dependency declaration for the library to which they belong will cause these header files to have not yet been generated at compile time, resulting in compilation errors.
- To make matters worse, if these header files already exist but have not been updated, outdated header files may be used during compilation, which will cause runtime errors that are more difficult to troubleshoot.
The severity of the problem can be controlled by the cc_config.hdr_dep_missing_severity
configuration item.
For problems that existed before hdrs were supported, It can be suppressed by cc_config.hdr_dep_missing_suppress
.
Blade can detect two kind of missing dependencies:
-
Missing dependenvy
The header files are included in
srcs
orhdrs
through the#include
directive, but the library to which they belong is not declared indeps
, or these header files are not declared in anyhdrs
ofcc_library
at all. Problems and solution:- The library to which the header file belongs is not declared in the
deps
of this target, just follow the instruction to fix it - The header file is a private header file of the library to which it belongs, and direct use is prohibited
- The header file should be the public header file of this target, it should be declared in its
hdr
- The header file should be the target's private header file, it should be declared in its
src
- The header file should be a public header file of other libraries, but there is no declaration, just declare it in the corresponding library
hdr
- The library to which the header file belongs is not declared in the
-
Missing indirect dependency
One of the indirect included header file(included in the header file) does not appear in the
deps
of this target and its transitive dependencies.We only do this check for the header files generated during compilation. Because for the rules for generating header files (such as
proto_library
or possiblygen_rule
), if the dependencies are missing, it may cause these header files to be ungenerated or outdated when compiling the current target, resulting in compilation errors. Fixing this error is a little more troublesome. You need to analyze the include stack reported by the error message, starting from the source file, and searching upwards in the library to which each header file belongs, whether it depends on the library to which the included header file belongs.At this time, you may encounter a situation, that is, some libraries with pure header files have no implementation files, so there is no orresponding
cc_library
to describe it. To fix this issue, you need to write a newcc_library
for it, add header files in thehdrs
, add the implementation dependencies in itsdeps
, and add it to thedeps
of which library depends on it.This can solve the root cause, but it does require some effort. The simple and rude solution is to add the reported missing library to the current target
deps
, which is equivalent to relying on the implementation details of some libraries, which is not recommended.
Since Blade fully managed the dependency of header files, for any header files that are not declared in the hdrs
or src
of any library,
an error will be reported after the build. If these header files should belong to the current target,
According to whether it is public or private, add it to hdrs
or srcs
respectively.
If it belongs to other libraries, it should be added to hdrs
of other libraries, and you cannot include undeclared or private header files of other libraries.
For undeclared header files that already existed in the code base before the upgrade, you can use the cc_config.allowed_undeclared_hdrs configuration item to mask the check.
For libraries without source code, library should be put under the lib{32,64} sub dir accordingly.
The attributes deps
, export_incs
, link_all_symbols
is still avialiable, but other attributes,
include compile and link related options, are not not present in prebuilt_cc_library.
Attributes:
libpath_pattern
: str, The subdirectory which contains the library files. It default tocc_library_config.prebuilt_libpath_pattern
config. See cc_library_config.prebuilt_libpath_pattern for more details.
Example:
prebuilt_cc_library(
name = 'mysql',
deps = [':mystring', '#pthread']
)
NOTE: This feature is still experimental.
There are already a large number of existing libraries in the world. They are built with different build systems. If you want to add Blade build support to them, you need to invest a lot of time cost and maintenance. foreign_cc_library is used to describe C/C++ libraries not built directly by Blade but generated by other build systems, such as make or cmake. The main difference between foreign_cc_library and prebuilt_cc_library is that the library it describes is dynamically generated by Blade during the build by calling other build systems, The library described by prebuilt_cc_library is placed in the source tree before the build. So generally foreign_cc_library always needs to be used with gen_rule.
Considering that a large number of C/C++ libraries are built with GNU Autotools,
the default parameters of foreign_cc_library are adapted to the
autotools installation directory layout.
In order to find the library and header files correctly, foreign_cc_library assumes that the package will be installed in a certain directory
after the build (that is, the path specified by the --prefix
parameter of configure
), and the header file is in the include
subdirectory,
The library file is installed in the lib
subdirectory.
Attributes:
name
: str, The name of the libraryinstall_dir
: str, The installation directory after the package is builtlib_dir
: list, The subdirectory name of the library in the installation directoryhas_dynamic
: bool = False, Whether this library has a dynamic linked edition.
zlib can be the simplest example of autotools package. Assuming that zlib-1.2.11.tar.gz
is in the thirparty/zlib
directory, its BUILD file is
thirdparty/zlib/BUILD
:
# Assume that after executing this rule, the built package will be installed under `build64_release/thirdparty/openssl`,
# then the header file is under `include/openssl`, and the library file is under `lib`.
# We have developed general build rules for autotools and cmake, but they are still in the experimental state.
# Here we still use `gen_rule` to examplify.
gen_rule(
name = 'zlib_build',
srcs = ['zlib-1.2.11.tar.gz'],
outs = ['lib/libz.a', 'include/zlib.h', 'include/zconf.h'],
cmd = '...', # tar xf,configure, make, make install...
)
# Describe the installed library of zlib
foreign_cc_library(
name = 'z', # The name of the library is `libz.a`,under the `lib` subdirectory
install_dir = '', # The installation directory is `build64_release/thirdparty/libz`
# lib_dir= 'lib', # The dafault os OK and can be omitted.
deps = [':zlib_build'],
)
Use the above library:
cc_binary(
name = 'use_zlib',
srcs = ['use_zlib.cc'],
deps = ['//thirdparty/openssl:ssl'],
)
use_openssl.cc:
#include "thirdparty/zlib/include/zlib.h"
// or
#include "zlib.h"
// because `thirdparty/zlib/include` has been exported in the `foreign_cc_library` defaultly
Strictly speaking, openssl is not built with autotools, but it is generally compatible with autotools.
Its autotools-like configure file is Config
, and the directory layout after installation is compatible.
However, its header file paths have package name prefix, which is not directly under include
but under the include/openssl
subdirectory.
Suppose openssl-1.1.0.tar.gz is in the thirparty/openssl directory, and its BUILD file is thirdparty/openssl/BUILD:
# Assuming that after executing this rule, the built package will be installed under `build64_release/thirdparty/openssl`,
# then the header file is under `include/openssl` and the library file is under `lib`.
gen_rule(
name = 'openssl_build',
srcs = ['openssl-1.1.0.tar.gz'],
outs = ['lib/libcrypto.a', 'lib/libss.a'],
cmd = '...', # tar xf,Config, make, make install...
export_incs = 'include', # Let the compiler find the `openssl` subdirectory under `include`
)
# Describe the above 2 libraries
foreign_cc_library(
name = 'crypto', # The name of the library is libcrypto.a, under the `lib` subdirectory
install_dir = '', # The installation directory is `build64_release/thirdparty/openssl`
deps = [':openssl_build'],
)
foreign_cc_library(
name = 'ssl', # The name of the library is libssl.a, under the `lib` subdirectory
install_dir = '',
deps = [':openssl_build', ':crypto'],
)
Use the above library:
cc_binary(
name = 'use_openssl',
srcs = ['use_openssl.cc'],
deps = ['//thirdparty/openssl:ssl'],
)
use_openssl.cc:
#include "openssl/ssl.h" // Contains package name
Build executable from source.
Example:
cc_binary(
name='prstr',
srcs=['./src/mystr_main/mystring.cpp'],
deps=['#pthread',':lowercase',':uppercase','#dl'],
)
-
dynamic_link
:bool = Falsecc_binary is static linked defaultly for easy deploying, include libstdc++. For some technology limitation, glibc is not static linked. we can link it statically, but there are still some problems.
If you want to generate a dynamic linked executable, you can set it to True,all dependency libraries will be linked statically, except prebuilt libraries if they has no dynamic version. This may reduce disk usage, but program will startup slower. Usually it can be use for local testing, but it didn't fit for deploy.
-
export_dynamic
: = TrueUsually, dynamic library will only access symbols from its dependencies. but for some special case, shared library need to access symbols defined in the host executable, so come the attribute.
This attribute tells linker to put all symbols into its dynamic symbol table. make them visible for loaded shared libraries. for more details, see
--export-dynamic
in man ld(1).
cc_binary, with gtest gtest_main be linked automatically,
Test will be ran in a test sandbox, it can't access source code directly.
If you want to pass some data files into it, you must need testdata
attribute.
-
testdata
: list = []Copy test data files into the test sandbox, make them visible to the test code. This attribute supports the following forms:
-
'file'
Use the original filename in test code.
-
'//your_proj/path/file'
Use "your_proj/path/file" form to access in test code.
-
('//your_proj/path/file', "new_name")
Use the "new_name" in test code.
-
Example:
cc_test(
name = 'textfile_test',
srcs = 'textfile_test.cpp',
deps = ':io',
testdata = [
'test_dos.txt',
'//your_proj/path/file',
('//your_proj/path/file', 'new_name')
]
)
Link all denendencies into the generated so
file, make it can be easily loaded in other languages.
cc_plugin(
name='mystring',
srcs=['./src/mystr/mystring.cpp'],
deps=['#pthread',':lowercase',':uppercase','#dl'],
warning='no',
defs=['_MT'],
optimize=['O3']
)
Attributes:
prefix
: str = 'lib', the file name prefix of the generated dynamic library, the default islib
suffix
: str = '.so', the file name prefix of the generated dynamic library, the default is.so
allow_undefined
: bool = False, whether undefined symbols are allowed when linking. Because many plug-in libraries depend on the symbol names provided by the host process at runtime, the definition of these symbols does not exist in the link phase.strip
: bool = False, whether to remove the debugging symbol information. If enabled, the size of the generated library can be reduced, but symbolic debugging cannot be performed.linker_scripts
: list(string), uses linker scripts. The linker script is a script used to control the linking process. Its role is mainly to specify how to put the sections in the input file into the output file and to control the layout of the sections in the input file in the program address space. The linker has a default built-in linking script, which can be viewed withld --verbose
. This option will replace the system's default linking script. The linker script files usually have the extension.ld
or.lds
. Linker scripts are usually quite complex, so if you just want to control the version or visibility of the symbols, use theversion_scripts
option below.version_scripts
: list(string), using linker "version" script. The linker version script is used to control the version and visibility of the symbol, if no version id is specified, it only to control the visibility of the symbol. Linker version script files usually have the extensionexp
,sym
,.ver
or.map
.
prefix
and suffix
control the file name of the generated dynamic library, assuming name='file'
, the default generated library is libfile.so
,
set prefix=''
, then it becomes file.so
.
To control the external visibility of symbols in the linking result, you can use GCC extended attributes in the source code or command line options.
With the linker version file, it is possible to control or override the visibility settings in the source code when linking,eg:
{
global: # Globally visible
# Support wildcards
Name1;
_Name2*;
extern "C++" { # C++ symbols
# Quoted strings do not match wildcards
"a()";
"a(int)";
"operator<<(std::ostream&, B const&)";
# Unquoted strings match wildcards
B::*;
typeinfo?for?magic::*;
vtable?for?magic::*;"
};
local: # The rest are local symbols, not visible
*;
};
To see the visibility of symbols in the library, you can use the nm
command.
000000000000010c t _init
U puts@@GLIBC_2.2.5
00000000000000000060 t register_tm_clones
00000000000000f0 T hello
00000000000000000100 t world
The second column is the symbol type. If lowercase, the symbol is usually local; if uppercase, the symbol is global (external).
U
is the undefined external symbols that the library depends on, don't care.
cc_plugin
is mainly used to create various extensions, such as JNI, python extension and other dynamic libraries
that are dynamically loaded by calling certain functions during runtime.
It will be ignored when linking even if it appears in the deps
of other cc targets.
Compile static data file to be resource, which can be accessed in the program directly.
When we deploy a program, it often requires many other files to be deployed together. it's often boring.
Blade support resource_library
, make it quite easy to put resource files into the executable easily.
For example, put there static pages into a library:
resource_library(
name = 'static_resource',
srcs = [
'static/favicon.ico',
'static/forms.html',
'static/forms.js',
'static/jquery-1.4.2.min.js',
'static/jquery.json-2.2.min.js',
'static/methods.html',
'static/poppy.html'
]
)
It will generate a library libstatic_resource.a
,and a header file static_resource.h
(with full include path)。
When you need tp use it, you need to include static_resource.h
(with full path from workspace root)
and also "common/base/static_resource.h",
Use STATIC_RESOURCE macro to simplify access to the data (defined in "common/base/static_resource.h"):
StringPiece data = STATIC_RESOURCE(poppy_static_favicon_ico);
The argument of STATIC_RESOURCE is the full path of the data file, and replace all of the
non-alphanum and hyphen character to unserscore(_
):
The data is readonly static storage. can be accessed in any time.
NOTE: There is a little drawback for static resource, it can;t be updated at the runtime, so consider it before using.
build a C++ library target containing CUDA device code using nvcc.
Rules are similar to cc_library
.
The attributes cuda_path
and extra_cuflags
are added.
cuda_path
is pointed to cuda installed path in the local repository with prefix //
, with that nvcc binary {cuda_path}/bin/nvcc
and include search path {cuda_path}/include
can be found automatically. Environment variable below will be ignored if use cuda_path.
extra_cuflags
support some flags which only work for cuda.
Environment variable NVCC
is pointed to the path of nvcc binary
,such as NVCC=/usr/local/cuda/bin/nvcc blade build
.
Environment variable CUDA_PATH
is pointed to the installed path of cuda, with which {CUDA_PATH}/include
and {CUDA_PATH}/samples/common/inc
will be added to include search path.
Priority: cuda_path
> NVCC
/CUDA_PATH
.
Example:
cu_library(
name = 'template_gpu',
srcs = ['template_gpu.cu'],
hdrs = [],
# cuda_path = '//thirdparty/cuda',
)
build a C++ executable target containing CUDA device code using nvcc.
Rules are similar to cc_binary
. Use environment variables reference to cu_library
.
Example:
cu_binary(
name = 'template',
srcs = ['template.cu'],
deps = [':template_cpu'],
# cuda_path = '//thirdparty/cuda',
)
build a C++ ut target containing CUDA device code using nvcc.
Rules are similar to cc_test
. Use environment variables reference to cu_library
.
Example:
cu_test(
name = 'cu_test',
srcs = ['cu_test.cu'],
deps = [':template_cpu'],
# cuda_path = '//thirdparty/cuda',
)