Libraries in bpt
¶
In bpt
, all code belongs to a library, and every library belongs by either a
bpt
project or a package.
By default, a bpt
project only has a single default library. A project can
be subdivided into more libraries by using the libraries
property. A library can depend on other libraries in external
packages.
bpt
uses the filesystem layout of the library to derive information about it.
Every library has a library root directory. A library root contains either
one or two source roots (src/
and/or include/
).
Libraries within projects are defined using bpt.yaml
. There may be
an implicit default library, or some number of
explicitly declared libraries using the
libs
key of bpt.yaml
.
The project’s bpt.yaml
defines the library roots of every
library in the project:
If relying on the default library, the library root is the same as the project root of the project that contains it. This is the recommended usage mode for most projects.
If using explicitly declared libraries, the library root is defined by a relative path specified using the
path
property of the library declaration inbpt.yaml
(Refer: Library Properties in bpt.yaml).
Every library root must contain a src/
directory, or an include/
directory, or both. bpt
will treat both directories as
source roots, but behaves differently between the two.
A single library root will always correspond to exactly one library. If the
library has any compilable source files then bpt
will use those sources to
generate a static library file that is linked into runtime binaries. If a
library contains only headers (a header-only library) then bpt
will not
generate an archive to be included in downstream binaries, but it will still
generate link rules for the dependencies of a header-only library.
The Default Library¶
In bpt
, all code belongs to a library. If a bpt.yaml
file omits the
libraries
property, bpt
will assume that the project’s
default library lives in the same directory as bpt.yaml
and has a library
name
equivalent to the project name
:
name: acme.widgets
version: 1.2.3
# ┌ Implied: ──────────────┐
│ libraries: │
│ - name: acme.widgets │
│ path: . │
# └────────────────────────┘
The above project definition implies a single default library with the same name
as the project itself: “acme.widgets
”. The library root of the default
library is always the same as the project root, and cannot be changed.
See also
The Project.libraries
property allows one to specify any number
of libraries within the project. The libraries
property is discussed
below: Multiple Libraries in a Project
Note
If your project only defines a single library, you are likely to not need to
use Project.libraries
and can just rely on the implicit default
library.
Note
If the Project.libraries
property is specified then bpt
will
not generate a default library.
Multiple Libraries in a Project¶
Multiple libraries can be specified for a single project by using the
Project.libraries
property in bpt.yaml
. libraries
must be
an array, and each element must be a map, and each map element must have both a
name
and a path
property:
name: acme.widgets
version: 1.2.3
libraries:
- name: gadgets # Required
path: libs/gadgets # Required
See also
For more information on using the libraries
array, refer to:
Library Properties in bpt.yaml.
Library Layout¶
The layout expected by bpt
is based on the Pitchfork layout (PFL). bpt
does not make use of every provision of the layout document, but the features it
does have are based on PFL.
In particular, the src/
and include/
directories are used are used as
source roots.
The smallest subdivision of code that bpt
recognizes is the source file,
which is exactly as it sounds: A single file containing some amount of
source code.
Source files can be grouped on a few axes, the most fundamental of which is “Is this compiled?”
bpt
uses source file extensions to determine whether a
source file should be fed to A compiler. All of the common C and C++ file
extensions are supported:
Compiled as C |
|
Compiled as C++ |
|
Checked but not compiled (header files) |
|
Not checked or compiled |
|
If a file’s extension is not listed in the table above, bpt
will ignore it.
File extensions are case-insensitive for the purpose of this lookup.
Note
Although headers are not compiled, this does not mean they are ignored. For
regular header files, bpt
performs a “compilability check”
on them to ensure that they can be used in isolation. Un-checked
“include-files” such as .ipp
, .inc
, and .inl
are not checked, but
they are collected together as part of the package for distribution.
Source Roots¶
Source files are collected as descendents of some source root directory. A source root is a single directory that contains some portable bundle of source files. The word “portable” is important: It is what distinguishes the source root from its child directories.
Portability¶
By saying that a source root is “portable”, we mean that the directory itself
can be moved, renamed, or copied without breaking the #include
directives of
its children or of the directory’s referrers.
As a practical example, let’s examine such a directory, which we’ll call
src/
for the purposes of this example. Suppose we have a such a directory
with the following structure:
<path>/src/
animals/
mammal/
mammal.hpp
cat/
cat.hpp
sound.hpp
sound.cpp
dog/
dog.hpp
sound.hpp
sound.cpp
In this example, src/
is a source root, but src/animals/
,
src/animals/cat/
, and src/animals/dog/
are not source roots. While
they may be directories that contain source files, they are not
“roots.”
Suppose now that dog.hpp
contains an #include
directive:
#include <animals/mammal/mammal.hpp>
or even a third-party user that wants to use this library:
#include <animals/dog/dog.hpp>
#include <animals/dog/sound.hpp>
In order for any code to compile and resolve these #include
directives, the
src/
directory must be added as a header search path.
Because the #include
directives are based on the source root, and the source
root is portable, the exact filepath location of the source root directory
is not important to the content of the consuming source code, and can thus be
relocated and renamed as necessary. Consumers only need to update the content of
their header search paths in a single location rather than modifying their
source code.
Note
bpt
manages header search paths automatically.
Source Root Kinds¶
The naming or source roots determines how bpt
will treat the
source files in that directory. A source root can be compiled
or header-only, and public or private.
bpt
distinguishes between a library’s public source root, and a private
source root. The headers within the private source root are
private headers of the library, and the headers within the public source
root are the public headers of the library.
When bpt
is compiling a library, both its private and its public source
roots will be added to the compiler’s list of header search paths. This
allows that library to freely refer to both its public and private headers.
On the other hande, when a downstream user of a library \(L\) is being compiled, only the public source root of that library \(L\) will be added as a header search path. This restricts downstream libraries to only have access to the public headers of the libraries that it uses.
bpt
supports either one or two source roots in a library. Their naming
determines which directories are treated as public or private.
The Compiled Source Root: src/
If the library root contains a src/
directory, then bpt
will treat
that directory as the compiled source root, and will compile any files
within that directory that have an appropriate file extension. While compiling
those files, the src/
directory will be given as a header search path for
resolving #include
directives.
Note
One can always safely place header files in src/
.
The Header-Only Source Root: include/
If the library root contains an include/
directory, then that directory
will be treated as the header-only source root. No files within this
directory will be compiled, but bpt
will still validate that every file that
has an appropriate header file extension could be passed to the compiler on
its own.
Public vs. Private Source Roots
If both src/
and include/
are present in a library root, then
src/
will be treated as the private source root and include/
will be
treated as the public source root. Users of the library will be able to
resolve the headers in the include/
directory (they are public headers),
but not headers in the src/
directory (which are private headers).
Additionally: Header files in the include/
directory will not be able to
reference any of the private headers in src/
, but private headers in
src/
will always be able to reference public headers in include/
.
Warning
Because only the public headers are available when compiling library consumers, it is essential that no headers within the public source root attempt to use private headers as they will not be visible.
If only one of src/
or include/
is present in the library root,
that directory will be treated as the public header root for the library, and
users will be able to #include
all headers in the library. There will be no
private header root.
If only include/
(and not src/
) is present in the
library root, then bpt
will treat it as a header-only library (No
source files in include/
will be compiled).
When bpt
exports a library to a package, the header files
from the public source root will be collected together and distributed as that
library’s header tree. The path to the individual header files relative to their
source root will be retained as part of the library distribution.
By default, bpt
will compile all compilable source files
that appears in the src/
directory. bpt
will not compile compilable source
files that appear in the include/
directory and will issue a warning if any
such files are found.
Note
Some source files will be treated differently based on there name. Refer:
Library Properties in bpt.yaml
¶
A bpt
project can declare one or more libraries using the
top-level Project.libraries
property in bpt.yaml
. If the
libraries
property is omitted, bpt
will instead generate a
default library for the project. Most projects will not need to declare
explicit libraries
and can rely on the default library.
The project’s libraries
must be an array, and each element must be a
ProjectLibrary
map. The only required properties are
name
and path
:
name: my-project
version: 1.2.3
libraries:
# Declare one library "my-library"
- name: my-library
path: libs/mylib
# Declare a second library
- name: widgets
path: libs/widgets
Refer to [YAML] for a quick-start on the YAML syntax. If nothing else, you can
use YAML’s flow-syntax as an “enhanced JSON” that supports # comments
and unquoted identifier keys:
{
name: "my-project",
version: "1.2.3",
libraries: [
{name: "my-library", path: "libs/mylib"},
{name: "widgets", path: "libs/widgets"},
]
}
Note
For the default library (if Project.libraries
is omitted), the
name
is the same as the project’s name, and the path
is
“.
” (equivalent to the project root).
Schema
- mapping ProjectLibrary¶
A mapping in
bpt.yaml
that defines a library for the project. These mapping objects appear as elements of thelibraries
array for the project.name: acme-components version: 4.1.2 # Every library will share the common test-dependencies: test-dependencies: [catch2@3.0.1] # This project defines two libraries listed below: libraries: - name: util path: libs/util depends: [fmt@8.1.3, asio@1.12.0] - name: gui path: libs/gui using: [util] # 'gui' uses 'util'
- property name (required)¶
- Type
string
The name of the library. Must follow the valid name conventions. This name must be unique within the project.
- property path (required)¶
- Type
string
The
path
property specifies the relative filepath pointing to the library root for the library. This path must be relative to the project root (the directory that containsbpt.yaml
) and may only use forward-slash “/
” as a directory separator. Thepath
must not “reach outside” of the project root directory. A path of a single ASCII dot “.
” refers to the project root itself.
- property using (optional)¶
- Type
string[]
The internal dependencies of the library. Must be the names of other libraries within the same project.
name: acme-items version: 1.2.3 libraries: - name: widgets path: libs/widgets - name: gadgets path: libs/gadgets using: [widgets] # All entities from widgets will be # available to gadgets, as well as to # any of gadgets' dependents
- property test-using (optional)¶
- Type
string[]
The internal test dependencies of the library. Must be the names of other libraries within the same project. Like
using
, but these dependencies are only used for building tests.
- property dependencies (optional)¶
- Type
string[]
The external dependencies of the library. If provided, the value must be an array of dependency specifiers.
The dependencies here will be merged with the common dependencies of the project (from
Project.dependencies
).
- property test-dependencies (optional)¶
- Type
string[]
The external dependencies of the library. If provided, the value must be an array of dependency specifiers. Like
dependencies
, but these dependencies are only used for building tests.The dependencies here will be merged with the common test dependencies of the project (from
Project.test-dependencies
.