diff options
Diffstat (limited to 'doc/packaging.cli')
-rw-r--r-- | doc/packaging.cli | 5446 |
1 files changed, 5446 insertions, 0 deletions
diff --git a/doc/packaging.cli b/doc/packaging.cli new file mode 100644 index 0000000..d861457 --- /dev/null +++ b/doc/packaging.cli @@ -0,0 +1,5446 @@ +// file : doc/packaging.cli +// license : MIT; see accompanying LICENSE file + +"\name=build2-packaging-guide" +"\subject=toolchain" +"\title=Packaging Guide" + +// NOTES +// +// - Maximum <pre> line is 70 characters. +// +// - In guideline titles (do/don't) omit a/the. + +// TODO: +// +// @@ Update build2-packaging front page with link to this guide (add +// organization README?) +// +// @@ Enforce continous versioning with pre-commit hook -- maybe add a note? +// +// @@ Add example of propagating config.libfoo.debug to macro on build options? +// +// @@ Add link from metadata HOWTO to "Modifying upstream source code with +// preprocessor". +// +// @@ Mapping to system package manager names/versions. +// +// @@ Are upstream tests well behaved? -- add never exit/take unreasonable time +// + +" +\h0#preface|Preface| + +This document provides guidelines for converting third-party C/C++ projects to +the \c{build2} build system and making them available as packages from +\l{https://cppget.org cppget.org}, the \c{build2} community's central package +repository. For additional information, including documentation for individual +\c{build2} toolchain components, man pages, HOWTOs, etc., refer to the project +\l{https://build2.org/doc.xhtml Documentation} page. + + +\h1#intro|Introduction| + +The aim of this guide is to ease the conversion of third-party C/C++ projects +to the \c{build2} build system and publishing them to the +\l{https://cppget.org cppget.org} package repository by codifying the best +practices and techniques. By following the presented guidelines you will also +make it easier for others to review your work and help with ongoing +maintenance. + +\N|A \c{build2}-based project can only consume packages that use the +\c{build2} build system (with the exception of +\l{https://build2.org/faq.xhtml#why-syspkg system-installed packages}). In +other words, there is no support for \"wrapping\" or otherwise adapting +third-party projects' existing build systems. While replacing the build system +unquestionably requires more work upfront, the \c{build2} project's experience +is that the long-term benefits of this effort are well justified (see +\l{https://build2.org/faq.xhtml#why-package-managers How does \c{build2} +compare to other package managers?} for details).| + +The primary focus of this guide is existing C/C++ projects that use a +different build system and that are maintained by a third-party, which we will +refer to as \i{upstream}. Unless upstream is willing to incorporate support +for \c{build2} directly into their repository, such projects are normally +packaged for \c{build2} in a separate \c{git} repository under the +\l{https://github.com/build2-packaging github.com/build2-packaging} +organization. Note, however, that many of the presented guidelines are also +applicable when converting your own projects (that is, where you are the +upstream) as well as projects that use languages other than C or C++. + +Most C/C++ packages that are published to \l{https://cppget.org cppget.org} +are either libraries or executables (projects that provide both are normally +split into several packages) with libraries being in the strong majority. +Libraries are also generally more difficult to build correctly. As a result, +this guide uses libraries as a baseline. In most cases, a library-specific +step is easily distinguished as such and can be skipped when dealing with +executables. And in cases where a more nuanced change is required, a note will +be provided. + +At the high-level, packaging a third-party project involves the following +steps: + +\ol| + +\li|Create the \c{git} repository and import upstream source code.| + +\li|Generate \c{buildfile} templates that match upstream layout.| + +\li|Tweak the generated \c{buildfiles} to match upstream build.| + +\li|Test locally and using the \l{https://ci.cppget.org \c{build2} CI service}.| + +\li|Release and publish the package to \l{https://cppget.org cppget.org}.| + +| + +Once this process is completed and the package is published, new releases +normally require a small amount of work provided there are no drastic changes +in the upstream layout or build. The sequence of steps for a new release would +typical look like this: + +\ol| + +\li|Add new and/or remove old upstream source code, if any.| + +\li|Tweak \c{buildfiles} to match changes to upstream build, if any.| + +\li|Test locally and using the \l{https://ci.cppget.org \c{build2} CI service}.| + +\li|Release and publish the package to \l{https://cppget.org cppget.org}.| + +| + +While packaging a simple library or executable is relatively straightforward, +the C and C++ languages and their ecosystems are infamous for a large amount +of variability in the platforms, compilers, source code layouts, and build +systems used. This leads to what looks like an endless list of special +considerations that are only applicable in certain, more complex cases. + +As result, the presented guidelines are divided into four chapters: \l{#core +Common Guidelines} cover steps that are applicable to most packaging +efforts. As mentioned earlier, these steps will assume packaging a library but +they should be easy to adapt to executables. This chapter is followed by +\l{#dont-do What Not to Do} which covers the common packaging mistakes and +omissions. These are unfortunately relatively common because experience with +other build systems often does not translate directly to \c{build2} and some +techniques (such as header-only libraries) are discouraged. The last two +chapters are \l{#howto HOWTO} and \l{#faq FAQ}. The former covers the +above-mentioned long list of special considerations that are only applicable +in certain cases while the latter answer frequent packaging-related questions. + +Besides the presented guidelines, you may also find the existing packages +found in \l{https://github.com/build2-packaging github.com/build2-packaging} a +good source of example material. The repositories pinned to the front page are +the recommended starting point. + +This guide assumes familiarity with the \c{build2} toolchain. At the minimum +you should have read through \l{intro The \c{build2} Toolchain Introduction} +and the \l{b#intro Introduction} chapter in the build system manual. Ideally, +you should also have some experience using \c{build2} in your own projects. + +In this guide we will only show the UNIX version of the commands. In most +cases making a Windows version is a simple matter of adjusting paths and, if +used, line continuations. And where this is not the case a note will be +provided showing the equivalent Windows command. + + +\h#intro-term|Terminology| + +We use the term \i{upstream} to refer collectively to the third-party project +as well as to its authors. For example, we may say, \"upstream does not use +semver\" meaning that the upstream project does not use semver for +versioning. Or we may say, \"upstream released a new version\" meaning that +the upstream project's authors released a new version. + +We will often use \i{upstream} as a qualifier to refer to a specific part of +the upstream project. Commonly used qualified terms like this include: + +\dl| + +\li|\i{upstream repository} + +The version control (normally \c{git}) repository of the third-party project.| + +\li|\i{upstream source code} + +The C/C++ source code that constitutes the third-party project. + +| + +\li|\i{upstream layout} + +The directory structure and location of source code in the third-party +project. + +| + +\li|\i{upstream build system} + +The equivalents of \c{buildfiles} that are used by the third-party project to +build its source code, run tests, etc. For example, if upstream uses CMake, +then all the \c{CMakeLists.txt}, \c{*.cmake}, etc., files will constitute its +build system.|| + +To avoid confusion, in this guide we will always use the term \i{project} to +refer to upstream and \i{package} to refer to its \c{build2} conversion, even +though we would normally call our own \c{build2}-based work a project, not a +package (see \l{b#intro-proj-struct Project Structure} for details on the +\c{build2} terminology in this area). Some commonly used \c{build2}-side terms +in this guide include: + + +\dl| + +\li|\i{package \c{git} repository} + +The \c{git} repository that hosts the package of the upstream project.| + + +\li|\i{multi-package repository} + +Sometimes it makes sense to split the upstream project into multiple +\c{build2} packages (for example, a library and an executable). In this case +the package repository structure must become multi-package.|| + + +\h1#core|Common Guidelines| + +This chapter describes the recommended sequence of steps for packaging a +third-party project for \c{build2} with the end-goal of publishing it to the +\l{https://cppget.org cppget.org} package repository. + + +\h#core-repo|Setup the package repository| + +This section covers the creation of the package \c{git} repository and +the importation of the upstream source code. + +\h2#core-repo-exists|Check if package repository already exists| + +Before deciding to package a third-party project you have presumably checked +on \l{https://cppget.org cppget.org} if someone has already packaged it. There +are several other places that make sense to check as well: + +\ul| + +\li|\l{https://queue.cppget.org queue.cppget.org} contains packages that +have been submitted but not yet published.| + +\li|\l{https://queue.stage.build2.org queue.stage.build2.org} contains +packages that have been submitted but can only be published after the next +release of the \c{build2} toolchain (see \l{#faq-publish-stage Where to +publish if package requires staged toolchain?} for background).| + +\li|\l{https://github.com/build2-packaging github.com/build2-packaging} +contains all the third-party package repositories. Someone could already be +working on the package but haven't yet published it.| + +\li|\l{https://github.com/build2-packaging/WISHLIST/issues +github.com/build2-packaging/WISHLIST} contains as issues projects that people +wish were packaged. These may contain offers to collaborate or announcements +of ongoing work.|| + + +In all these cases you should be able to locate the package \c{git} repository +and/or connect with others in order to collaborate on the packaging work. If +the existing effort looks abandoned (for example, there hasn't been any +progress for a while and the existing maintainer doesn't respond) and you +would like to take over the package, +\l{https://build2.org/community.xhtml#help get in touch}. + + +\h2#core-repo-name|Use upstream repository name as package repository name| + +It is almost always best to use the upstream repository name as the package +repository name. If there is no upstream repository (for example, because the +project doesn't use a version control system), the name used in the source +archive distribution would be the natural fallback choice. + +\N|See \l{#core-package-name Decide on the package name} for the complete +picture on choosing names.| + +\h2#core-repo-create|Create package repository in personal workspace| + +For a third-party project, the end result that we are aiming for is a package +repository under the \l{https://github.com/build2-packaging +github.com/build2-packaging} organization. + +\N|We require all the third-party projects that are published to +\l{https://cppget.org cppget.org} to be under the +\l{https://github.com/build2-packaging github.com/build2-packaging} +organization in order to ensure some continuity in case the original +maintainer loses interest, etc. You will still be the owner of the repository +and by hosting your packaging efforts under this organization (as opposed to, +say, your personal workspace) you make it easier for others to discover your +work and to contribute to the package maintenance. + +Note that this requirement does not apply to your own projects (that is, where +you are the upstream) and where the \c{build2} support is normally part of the +upstream repository. + +Finally, a note on the use of \c{git} and GitHub: if for some reason you are +unable to use either, \l{https://build2.org/community.xhtml#help get in touch} +to discuss alternatives.| + +However, the recommended approach is to start with a repository in your +personal workspace and then, when it is ready or in a reasonably complete +state, transfer it to \l{https://github.com/build2-packaging +github.com/build2-packaging}. This gives you the freedom to make destructive +changes to the repository (including deleting it and starting over) during the +initial packaging work. It also removes the pressure to perform: you can give +it a try and if things turn out more difficult than you expected, you can just +drop the repository. + +\N|For repositories under \l{https://github.com/build2-packaging +github.com/build2-packaging} the \c{master}/\c{main} branch is protected: it +cannot be deleted and its commit history cannot be overwritten with a forced +push.| + +\N|While you can use any name for a repository under the personal workspace, +under \l{https://github.com/build2-packaging github.com/build2-packaging} it +should follow the \l{#core-repo-name Use upstream repository name as package +repository name} guideline. In particular, there should be no prefixes like +\c{build2-} or suffixes like \c{-package}. If the repository under your +personal workspace does not follow this guideline, you will need to rename it +before transferring it to the \l{https://github.com/build2-packaging +github.com/build2-packaging} organization.| + +There is one potential problem with this approach: it is possible that +several people will start working on the same third-party project without +being aware of each other's efforts. If the project you are packaging is +relatively small and you don't expect it to take more than a day or two, then +this is probably not worth worrying about. For bigger projects, however, it +makes sense to announce your work by creating (or updating) the corresponding +issue in \l{https://github.com/build2-packaging/WISHLIST +github.com/build2-packaging/WISHLIST}. + +To put it all together, the recommended sequence of actions for this step: + +\ol| + +\li|Create a new empty repository under your personal workspace from the + GitHub UI.| + +\li|Set the repository description to \c{build2 package for <name>}, where +\c{<name>} is the third-party project name.| + +\li|Make the repository public (otherwise you won't be able to CI it).| + +\li|Don't automatically add any files (\c{README}, \c{LICENSE}, etc).| + +\li|Clone the empty repository to your machine (using the SSH protocol).|| + +\N|Since this is your personal repository, you can do the initial work +directly in \c{master}/\c{main} or in a separate branch, it's up to you.| + +As a running example, let's assume we want to package a library called \c{foo} +whose upstream repository is at \c{https://github.com/<upstream>/foo.git}. We +have created its package repository at +\c{https://github.com/<personal>/foo.git} (with the \c{build2 package for foo} +description) and can now clone it: + +\ +$ git clone git@github.com:<personal>/foo.git +\ + + +\h2#core-repo-init|Initialize package repository with \c{bdep new}| + +Change to the root directory of the package repository that you have cloned +in the previous step and run (continuing with our \c{foo} example): + +\ +$ cd foo/ # Change to the package repository root. +$ bdep new --type empty,third-party +$ tree -a . +./ +├── .bdep/ +│ └── ... +├── .git/ +│ └── ... +├── .gitattributes +├── .gitignore +├── README.md +└── repositories.manifest +\ + +\N|We use the special \c{third-party} sub-option which is meant for converting +third-party projects to \c{build2}. See \l{bdep-new(1)} for details.| + +This command creates a number of files in the root of the repository: + +\dl| + +\li|\n\c{README.md}\n + +This is the repository \c{README.md}. We will discuss the recommended content +for this file later.| + +\li|\n\c{repositories.manifest}\n + +This file specifies the repositories from which this project will obtain its +dependencies (see \l{intro#guide-add-remove-deps Adding and Removing +Dependencies}). If the project you are packaging has no dependencies, then you +can safely remove this file (it's easy to add later if this changes). And for +projects that do have dependencies we will discuss the appropriate changes to +this file later.| + + +\li|\n\c{.gitattributes} and \c{.gitignore}\n + +These are the \c{git} infrastructure files for the repository. You shouldn't +normally need to change anything in them at this stage (see the comments +inside for details).|| + +Next add and commit these files: + +\ +$ cd foo/ # Change to the package repository root. +$ git add . +$ git status +$ git commit -m \"Initialize package repository\" +\ + +\N|In these guidelines we will be using the package repository setup that is +capable of having multiple packages (referred to as \i{multi-package +repository}). This is recommended even for upstream projects that only +provides a single package because it gives us the flexibility of adding new +packages at a later stage without having to perform a major restructuring of +our repository. + +Note also that upstream providing multiple packages is not the only reason we +may end up having multiple \c{build2} packages. Another common reason is +factoring tests into a separate package due to a dependency on a testing +framework +(see \l{https://github.com/build2/HOWTO/blob/master/entries/handle-tests-with-extra-dependencies.md +How do I handle tests that have extra dependencies?} for background and +details). While upstream adding new packages may not be very common, upstream +deciding to use a testing framework is a lot more plausible. + +The only notable drawback of using a multi-package setup with a single package +is the extra subdirectory for the package and a few extra files (such as +\c{packages.manifest} that lists the packages) in the root of the repository. +If you are certain that the project that you are converting is unlikely to +have multiple packages (for example, because you are the upstream) and won't +need extra dependencies for its tests (a reasonable assumption for a C +project), then you could instead go with the single-package repository where +the repository root is the package root. See \l{bdep-new(1)} for details on +how to initialize such a repository. In this guide, however, we will continue +to assume a multi-package repository setup.| + + +\h2#core-repo-submodule|Add upstream repository as \c{git} submodule| + +If the third-party project is available from a \c{git} repository, then the +recommended approach is to use the \c{git} submodule mechanism to make the +upstream source code available inside the package repository, customarily in a +subdirectory called \c{upstream/}. + +\N|While \c{git} submodules receive much criticism, in our case we use them +exactly as intended: to select and track specific (release) commits of an +external project. As a result, there is nothing tricky about their use for our +purpose and all the relevant commands will be provided and explained, in case +you are not familiar with this \c{git} mechanism.| + +Given the upstream repository URL, to add it as a submodule, run the following +command from the package repository root (continuing with our \c{foo} example): + +\ +$ cd foo/ # Change to the package repository root. +$ git submodule add https://github.com/<upstream>/foo.git upstream +\ + +\N|You should prefer \c{https://} over \c{git://} for the upstream repository +URL since the \c{git://} protocol may not be accessible from all networks. +Naturally, never use a URL that requires authentication, for example, SSH +(SSH URLs start with \c{git@github.com} for GitHub).| + +Besides the repository URL, you also need the commit of the upstream release +which you will be packaging. It is common practice to tag releases so the +upstream tags would be the first place to check. Failing that, you can always +use the commit id. + +Assuming the upstream release tag you are interested in is called \c{vX.Y.Z}, +to update the \c{upstream} submodule to point to this release commit, run the +following commands: + +\ +$ cd upstream/ +$ git checkout vX.Y.Z +$ cd ../ +\ + +Then add and commit these changes: + +\ +$ git add . +$ git status +$ git commit -m \"Add upstream submodule, vX.Y.Z\" +\ + +Now we have all the upstream source code for the version that we are +packaging available in the \c{upstream/} subdirectory of our repository. + +The plan is to then use symbolic links (symlinks) to non-invasively overlay +the \c{build2}-related files (\c{buildfile}, \c{manifest}, etc) with the +upstream source code, if necessary adjusting upstream structure to split it +into multiple packages and/or to better align with the source/output layouts +recommended by \c{build2} (see \l{https://build2.org/article/symlinks.xhtml +Using Symlinks in \c{build2} Projects} for background and rationale). But +before we can start adding symlinks to the upstream source (and other files +like \c{README}, \c{LICENSE}, etc), we need to generate the \c{buildfile} +templates that match the upstream source code layout. This is the subject of +the next section. + +\N|While on UNIX-like operating systems symlinks are in widespread use, on +Windows it's a niche feature that unfortunately could be cumbersome to use +(see \l{https://build2.org/article/symlinks.xhtml#windows Symlinks and +Windows} for details). However, the flexibility afforded by symlinks when +packaging third-party projects is unmatched by any other mechanism and we +therefore use them despite potentially sub-optimal packaging experience +on Windows.| + + +\h#core-package|Create package and generate \c{buildfile} templates| + +This section covers the addition of the package to the repository we have +prepared in the previous steps and the generation of the \c{buildfile} +templates that match the upstream source code layout. + + +\h2#core-package-name|Decide on the package name| + +While choosing the package repository name was pretty straightforward, things +get less clear cut when it comes to the package name. + +\N|If you need a refresher on the distinction between projects and packages, +see \l{#intro-term Terminology}.| + +Picking a name for a package that provides an executable is still relatively +straightforward: you should use the upstream name (which is usually the same +as the upstream project name) unless there is a good reason to deviate. One +recommended place to check before deciding on a name is the +\l{https://packages.debian.org Debian package repository}. If their package +name differs from upstream, then there is likely a good reason for that and +it is worth trying to understand what it is. + +\N|Tip: when trying to find the corresponding Debian package, search for the +executable file name in the package contents if you cannot fine the package by +its upstream name. Also consider searching in the \c{unstable} distribution in +addition to \c{stable} for newer packages.| + +Picking a name for a package that provides a library is where things can get +more complicated. While all the recommendations that have been listed for +executables apply equally to libraries, there are additional considerations. + +In \c{build2} we recommend (but not require) that new library projects use a +name that starts with \c{lib} in order to easily distinguish them from +executables and avoid any clashes, potential in the future (see +\l{intro#proj-struct Canonical Project Structure} for details). To illustrate +the problem, consider the \c{zstd} project which provides a library and an +executable. In upstream repository both are part of the same codebase that +doesn't try to separate them into packages so that, for example, library could +be used without downloading and building the executable. In \c{build2}, +however, we do need to split them into two separate packages and both packages +cannot be called \c{zstd}. So we call them \c{zstd} and \c{libzstd}. + +\N|If you are familiar with the Debian package naming policy, you will +undoubtedly recognize this approach. In Debian all the library packages (with +very few exceptions) start with the \c{lib} prefix. So when searching for an +upstream name in the \l{https://packages.debian.org Debian package repository} +make sure to prefix it with \c{lib} (unless it already starts with this +prefix, of course).| + +This brings the question of what to do about third-party libraries: should we +add the \c{lib} prefix to the package name if it's not already there? +Unfortunately, there is no clear cut answer and whichever decision you make, +there will be drawbacks. Specifically, if you add the \c{lib} prefix, the main +drawback is that the package name now deviates from the upstream name and if +the project maintainer ever decides to add \c{build2} support to the upstream +repository, there could be substantial friction. On the other hand, if you +don't add the \c{lib} prefix, then you will always run the risk of a future +clash with an executable name. And, as was illustrated with the \c{zstd} +example, a late addition of an executable won't necessarily cause any issues +to upstream. As a result, we don't have a hard requirement for the \c{lib} +prefix unless there is already an executable that would cause the clash (this +applies even if it's not being packaged yet or is provided by an unrelated +project). If you don't have a strong preference, we recommend that you add the +\c{lib} prefix (unless it is already there). In particular, this will free you +from having to check for any potential clashes. See +\l{https://github.com/build2/HOWTO/blob/master/entries/name-packages-in-project.md +How should I name packages when packaging third-party projects?} for +additional background and details. + +To build some intuition for choosing package names, let's consider several +real examples. We start with executables: + +\ + upstream | upstream | Debian | build2 package| build2 +project name|executable name|package name|repository name|package name +------------+---------------+------------+---------------+------------ +byacc byacc byacc byacc byacc +sqlite sqlite3 sqlite3 sqlite sqlite3 +vim xxd xxd xxd xxd +OpenBSD m4 - openbsd-m4 openbsd-m4 +qtbase 5 moc qtbase5-\ Qt5 Qt5Moc + dev-tools +qtbase 6 moc qt6-base-\ Qt6 Qt6Moc + dev-tools +\ + +The examples are arranged from the most straightforward naming to the +least. The last two examples show that sometimes, after carefully considering +upstream naming, you nevertheless have no choice but to ignore it and forge +your own path. + +Next let's look at library examples. Notice that some use the same \c{build2} +package repository name as the executables above. This means they are part of +the same multi-package repository. + +\ + upstream | upstream | Debian | build2 package| build2 +project name|library name |package name|repository name|package name +------------+---------------+------------+---------------+------------ +libevent libevent libevent libevent libevent +brotli brotli libbrotli brotli libbrotli +zlib zlib zlib zlib libz +sqlite libsqlite3 libsqlite3 sqlite libsqlite3 +libsig\ libsigc++ libsigc++ libsig\ libsigc++ +cplusplus cplusplus +qtbase 5 QtCore qtbase5-dev Qt5 libQt5Core +qtbase 6 QtCore qt6-base-dev Qt6 libQt6Core +\ + +If an upstream project is just a single library, then the project name is +normally the same as the library name (but there are exceptions, like +\c{libsigcplusplus} in the above table). However, when looking at the upstream +repository that contains multiple components (libraries and/or executables, +like \c{qtcore} in the above example), it may not be immediately obvious what +the upstream's library names are. In such cases, the corresponding Debian +packages can really help clarify the situation. Failing that, look into the +existing build system. In particular, if it generates the \c{pkg-config} file, +then the name of this file is usually the upstream library name. + +\N|Looking at the names of the library binaries is less helpful because on +UNIX-like systems they must start with the \c{lib} prefix. And on Windows the +names of library binaries often embed extra information (static/import, +debug/release, etc) and may not correspond directly to the library name.| + +And, speaking of multiple components, if you realize the upstream project +provides multiple libraries and/or executables, then you need to decide +whether to split them into separate \c{build2} packages and if so, how. Here, +again, the corresponding Debian packages can be a good reference point. Note, +however, that we often deviate from Debian's splits, especially when it comes +to libraries. Such differences are usually due to Debian focusing on binary +packages while in \c{build2} we are focusing on source packages. + +To give a few example, \c{libevent} shown in the above table provides several +libraries (\c{libevent-core}, \c{libevent-extra}, etc) and in Debian it is +actually split into several binary packages along these lines. In \c{build2}, +however, there is a single source package that provides all these libraries +with everything except \c{libevent-core} being optional. An example which +shows the decision made in a different direction would be the Boost libraries: +in Debian all the header-only Boost libraries are bundled into a single +package while in \c{build2} they are all separate packages. + +The overall criteria here can be stated as follows: if a small family of +libraries provide complimentary functionality (like \c{libevent}), then we put +them all into a single package, usually making the additional functionality +optional. However, if the libraries are independent (like Boost) or provide +alternative rather than complimentary functionality (for example, like +different backends in \c{imgui}), then we make them separate packages. Note +that we never bundle an executable and a (public) library in a single package +(because, when consumed, they usually require different +\l{bpkg#manifest-package-depends dependency types}: build-time vs run-time). + +Note also that while it's a good idea to decide on the package split and all +the package names upfront to avoid surprises later, you don't have to actually +provide all the packages right away. For example, if upstream provides a +library and an executable (like \c{zstd}), you can start with the library and +the executable package can be added later (potentially by someone else). + +\N|In the \"library and executable\" case, if you plan to package both, the +sensible strategy is to first completely package the library stopping short of +releasing and publishing, then repeat the same process to package the +executable, and finally release and publish both.| + +Admittedly, the recommendations in this section are all a bit fuzzy and one +can choose different names or different package splits that could all seem +reasonable. If you are unsure how to split the upstream project or what names +to use, \l{https://build2.org/community.xhtml#help get in touch} to discuss +the alternatives. It can be quite painful to change these things after you +have completed the remaining packaging steps. + +Continuing with our \c{foo} example, we will follow the above recommendation +and call the library package \c{libfoo}. + + +\h2#core-package-struct|Decide on the package source code layout| + +Another aspect we need to decide on is the source code layout inside the +package. Here we want to stay as close to the upstream layout as possible +unless there are valid reasons to deviate. Staying close has the best chance +of giving us a build without any compile errors since the header inclusion in +the project can be sensitive to this layout. This also makes it easier for +upstream to adopt the \c{build2} build. + +Sometimes, however, there are good reasons for deviating from upstream, +especially in cases where upstream is clearly following bad practices, for +example including generically-named public headers without the library name as +a subdirectory prefix. If you do decide to change the layout, it's usually +less disruptive (to the build) to rearrange things at the outer levels than at +the inner. For example, it should normally be possible to move/rename the +top-level \c{tests/} directory or to place the library source files into a +subdirectory. + +Our overall plan is to create the initial layout and \c{buildfile} templates +automatically using \l{bdep-new(1)} in the \c{--package} mode, then \"fill\" +the package with upstream source code using symlinks, and finish off with +tweaking the generated \c{buildfiles} to match the upstream build. + +The main rationale for using \l{bdep-new(1)} instead of doing everything by +hand is that there are many nuances in getting the build right and +auto-generated \c{buildfiles} had years of refinement and fine-tuning. The +familiar structure also makes it easier for others to understand your build, +for example while reviewing your package submission or helping out with +maintenance. + +The \l{bdep-new(1)} command supports a wide variety of +\l{bdep-new.xhtml#src-layout source layouts}. While it may take a bit of time +to understand the customization points necessary to achieve the desired layout +for your first package, this experience will pay off in spades when you work +on converting subsequent packages. + +And so the focus of the following several steps is to iteratively discover the +\l{bdep-new(1)} command line that best approximates the upstream layout. The +recommended procedure is as follows: + +\ol| + +\li|\nStudy the upstream source layout and existing build system.| + +\li|\nCraft and execute the \l{bdep-new(1)} command line necessary to achieve +the upstream layout.| + +\li|\nStudy the auto-generated \c{buildfiles} for things that don't fit and +need to change. But don't rush to start manually editing the result. First get +an overview of the required changes and then check if it's possible to achieve +these changes automatically using one of \l{bdep-new(1)} sub-options. If +that's the case, delete the package, and restart from step 2.|| + +This and the following two sections discuss each of these steps in more detail +and also look at some examples. + +The first step above is to study the upstream project in order to understand +where the various parts are (headers, sources, etc) and how they are built. +Things that can help here include: + +\ul| + +\li|Read through the existing build system definitions.| + +\li|Try to build the project using the existing build system.| + +\li|Try to install the project using the existing build system.| + +\li|Look into the Debian package contents to see if there are any differences + with regards to the installation locations.|| + +\N|If while studying the upstream build system you notice other requirements, +for example, the need to compile source files other than C/C++ (such as +Objective-C/C++, assembler, etc) or the need to generate files from \c{.in} +templates (or their \c{.cmake}/\c{.meson} equivalents), and are wondering how +they would be handled in the \c{build2} build, see the +\l{#core-adjust-build-src-source-ext Adjust source \c{buildfile}: extra +requirements} step for a collection of pointers.| + +For libraries, the first key pieces of information we need to find is how the +public headers are included and where they are installed. The two common +\i{good} practices is to either include the public headers with a library name +as a subdirectory, for example, \c{#include\ <foo/util.h>}, or to include the +library name into each public header name, for example, \c{#include\ +<foo-util.h>} or \c{#include\ <foo.h>} (in the last example the header name is +the library name itself, which is also fairly common). Unfortunately, there is +also a fairly common \i{bad} practice: having generically named headers (such +as \c{util.h}) included without the library name as a subdirectory. + +\N|The reason this is a bad practice is that libraries that have such headers +cannot coexist, neither in the same build nor when installed. See +\l{#howto-bad-inclusion-practice How do I deal with bad header inclusion +practice?} if you encounter such a case. See \l{intro#proj-struct Canonical +Project Structure} for additional background and details.| + +Where should we look to get this information? While the library source files +sound like a natural place, oftentimes they include own headers with the +\c{\"\"} style inclusion, either because the headers are in the same directory +or because the library build arranges for them to be found this way with +additional header search paths. As a result, a better place to look could be +the library's examples and/or tests. Some libraries also describe which +headers they provide and how to include them in their documentation. + +The way public headers are included normally determines where they are +installed. If they are included with a subdirectory, then they are normally +installed into the same subdirectory in, say, \c{/usr/include/}. Continuing +with the above example, a header that is included as \c{<foo/util.h>} would +normally be installed as \c{/usr/include/foo/util.h}. On the other hand, if +the library name is part of the header name, then the headers are usually (but +not always) installed directly into, say, \c{/usr/include/}, for example as +\c{/usr/include/foo-util.h}. + +\N|While these are the commonly used installation schemes, there are +deviations. In particular, in both cases upstream may choose to add an +additional subdirectory when installing (so the above examples will instead +end up with, say, \c{/usr/include/foo-v1/foo/util.h} and +\c{/usr/include/foo-v1/sub/foo-util.h}). See +\l{#howto-extra-header-install-subdir How do I handle extra header +installation subdirectory?} if you encounter such a case.| + +The inclusion scheme would normally also be recreated in the upstream source +code layout. In particular, if upstream includes public headers with a +subdirectory prefix, then this subdirectory would normally also be present in +the upstream layout so that such a header can be included from the upstream +codebase directly. As an example, let's say we determined that public headers +of \c{libfoo} are included with the \c{foo/} subdirectory, such as +\c{<foo/util.hpp>}. One of the typical upstream layouts for such a library +would look like this: + +\ +$ tree upstream/ +upstream/ +├── include/ +│ └── foo/ +│ └── util.hpp +└── src/ + ├── priv.hpp + └── util.cpp +\ + +Notice how the \c{util.hpp} header is in the \c{foo/} subdirectory rather +than in \c{include/} directly. + +The second key piece of information we need to find is whether and, if so, +how the public headers and sources are split. For instance, in the above +example, we can see that public headers go into \c{include/} while sources and +private headers go into \c{src/}. But they could also be combined in the same +directory, for example, as in the following layout: + +\ +upstream/ +└── foo/ + ├── priv.hpp + ├── util.cpp + └── util.hpp +\ + +\N|In multi-package projects, for example, those that provide both a library +and an executable, you would also want to understand how the sources are split +between the packages.| + +If the headers and sources are split into different directories, then the +source directory may or may not have the inclusion subdirectory, similar to +the header directory. In the above split layout the \c{src/} directory doesn't +contain the inclusion subdirectory (\c{foo/}) while the following layout does: + +\ +upstream/ +├── include/ +│ └── foo/ +│ └── util.hpp +└── src/ + └── foo/ + ├── priv.hpp + └── util.cpp +\ + +With the understanding of these key properties of upstream layout you should +be in a good position to start crafting the \l{bdep-new(1)} command line that +recreates it. + +\N|The \c{bdep-new} documentation uses slightly more general terminology +compared to what we used in the previous section in order to also be +applicable to projects that use modules instead of headers. + +Specifically, the inclusion subdirectory (\c{foo/}) is called \i{source +subdirectory} while the header directory (\c{include/}) and source directory +(\c{src/}) are called \i{header prefix} and \i{source prefix}, respectively.| + + +\h2#core-package-craft-cmd|Craft \c{bdep new} command line to create package| + +The recommended procedure for this step is to read through the \c{bdep-new}'s +\l{bdep-new.xhtml#src-layout SOURCE LAYOUT} section (which contains a large +number of examples) while experimenting with various options in an attempt to +create the desired layout. If the layout you've got isn't quite right yet, +simply remove the package directory along with the \c{packages.manifest} file +and try again. + +\N|Next to \c{packages.manifest}, \c{bdep-new} will also create the \"glue\" +\c{buildfile} that allows building all the packages from the repository +root. You don't need to remove it when re-creating the package.| + +Let's illustrate this approach on the first split layout from the previous +section: + +\ +upstream/ +├── include/ +│ └── foo/ +│ └── util.hpp +└── src/ + ├── priv.hpp + └── util.cpp +\ + +We know it's split, so let's start with that and see what we get. Remember, +our \c{foo} package repository that we have cloned and initialized earlier +looks like this: + +\ +$ tree foo/ +foo/ +├── upstream/ +├── .gitattributes +├── .gitignore +├── README.md +└── repositories.manifest +\ + +Now we create the \c{libfoo} package inside: + +\ +$ cd foo/ +$ bdep new --package --lang c++ --type lib,split libfoo +$ tree libfoo/ +libfoo/ +├── include/ +│ └── libfoo/ +│ └── foo.hxx +└── src/ + └── libfoo/ + └── foo.cxx +\ + +The outer structure looks right, but inside \c{include/} and \c{src/} things +are a bit off. Specifically, the source subdirectory should be \c{foo/}, not +\c{libfoo/}, there shouldn't be one inside \c{src/}, and the file extensions +don't match upstream. All this can be easily tweaked, however: + +\ +$ rm -r libfoo/ packages.manifest +$ bdep new --package \ + --lang c++,cpp \ + --type lib,split,subdir=foo,no-subdir-source \ + libfoo +$ tree libfoo/ +libfoo/ +├── include/ +│ └── foo/ +│ └── foo.hpp +└── src/ + └── foo.cpp +\ + +The other \c{bdep-new} sub-options (see the \l{bdep-new(1)} man page for the +complete list) that you will likely want to use when packaging a third-party +project include: + +\dl| + +\li|\n\cb{no-version} + +Omit the auto-generated version header. Usually upstream will provide its own +equivalent of this functionality. + +\N|Note that even if upstream doesn't provide any version information, it's +not a good idea to try to rectify this by providing your own version header +since upstream may add it in a future version and you may end up with a +conflict. Instead, work with the project authors to rectify this upstream.|| + +\li|\n\cb{no-symexport}\n\cb{auto-symexport} + +The \c{no-symexport} sub-option suppresses the generation of the DLL symbol +exporting header. This is an appropriate option if upstream provides its +own symbol exporting arrangements. + +The \c{auto-symexport} sub-option enables automatic DLL symbol exporting +support (see \l{b#cc-auto-symexport Automatic DLL Symbol Exporting} for +background). This is an appropriate option if upstream relies on similar +support in the existing build system. It is also recommended that you give +this functionality a try even if upstream does not support building +shared libraries on Windows.| + +\li|\n\cb{binless} + +Create a header-only library. See \l{#dont-header-only Don't make +library header-only if it can be compiled} and +\l{https://github.com/build2/HOWTO/blob/master/entries/make-header-only-library.md +How do I make a header-only C/C++ library?}| + +\li|\n\cb{buildfile-in-prefix} + +Place header/source \c{buildfiles} into the header/source prefix directory +instead of source subdirectory. To illustrate the difference, compare these +two auto-generated layouts paying attention to the location of \c{buildfiles}: + +\ +$ bdep new ... --type lib,split,subdir=foo libfoo +$ tree libfoo/ +libfoo/ +├── include/ +│ └── foo/ +│ ├── buildfile +│ └── foo.hpp +└── src/ + └── foo/ + ├── buildfile + └── foo.cpp +\ + +\ +$ bdep new ... --type lib,split,subdir=foo,buildfile-in-prefix libfoo +$ tree libfoo/ +libfoo/ +├── include/ +│ ├── foo/ +│ │ └── foo.hpp +│ └── buildfile +└── src/ + ├── foo/ + │ └── foo.cpp + └── buildfile +\ + +Note that this sub-option only makes sense if we have the header and/or source +prefixes (\c{include/} and \c{src/} in our case) as well as the source +subdirectory (\c{foo/} in our case). + +Why would we want to do this? The main reason is to be able to symlink the +entire upstream directories rather than individual files. In the first +listing, the generated \c{buildfiles} are inside the \c{foo/} subdirectories +which mean we cannot just symlink \c{foo/} from upstream. + +With a large number of files to symlink, this can be such a strong motivation +that it may make sense to invent a source subdirectory in the source prefix +even if upstream doesn't have one. See \l{#dont-main-target-root-buildfile +Don't build your main targets in the root \c{buildfile}} for details on this +technique. + +Another reason we may want to move \c{buildfiles} to prefix is to be able to +handle upstream projects that have multiple source subdirectories. While this +situation is not very common in the header prefix, it can be encountered in the +source prefix of more complex projects, where upstream wishes to organize the +source files into components.|| + +\N|If upstream uses a mixture of C and C++, then it's recommended to set this +up using the \c{--lang} sub-option of \c{bdep-new}. For example: + +\ +$ bdep new --lang c++,c ... +\ + +| + +Continuing with our \c{libfoo} example, assuming upstream provides its own +symbol exporting, the final \c{bdep-new} command line would be: + +\ +$ bdep new --package \ + --lang c++,cpp \ + --type lib,split,subdir=foo,no-subdir-source,no-version,no-symexport \ + libfoo +\ + +When packaging an executable, things are usually quite a bit simpler: there is +no version header, symbol exporting, and the layout is normally combined +(since there are no public headers). Typically the only potentially tricky +decision you will need to make is whether to use \i{prefix} or \i{source +subdirectory}. Most likely it will be \i{prefix} since most executable +projects will use the \c{\"\"} style inclusion for own headers. For example: + +\ +$ bdep new --package \ + --lang c++ \ + --type exe,no-subdir,prefix=foo,export-stub \ + foo +\ + +\N|The \c{export-stub} sub-option causes the generation of +\c{build/export.build}, an export stub that facilitates the importation of +targets from our package (see \l{b#intro-import Target Importation} for +details). The generation of this file for a library is the default since it +will normally be used by other projects and thus imported. An executable, +however, will only need an export stub if it can plausibly be used during the +build (see \l{intro#guide-build-time-linked Build-Time Dependencies and Linked +Configurations} for background). Source code generators are an obvious example +of such executables. A less obvious example would be compression utilities +such as \c{gzip} or \c{zstd}. If you are unsure, it's best to provide an +export stub.| + + +\h2#core-package-review|Review and test auto-generated \c{buildfile} templates| + +Let's get a more complete view of what got generated by the final \c{bdep-new} +command line from the previous section: + +\ +$ tree libfoo/ +libfoo/ +├── build/ +│ └── ... +├── include/ +│ └── foo/ +│ ├── buildfile +│ └── foo.hpp +├── src/ +│ ├── buildfile +│ └── foo.cpp +├── tests/ +│ ├── build/ +│ │ └── ... +│ ├── basics/ +│ │ ├── buildfile +│ │ └── driver.cpp +│ └── buildfile +├── buildfile +├── manifest +└── README.md +\ + +Once the overall layout looks right, the next step is to take a closer look at +the generated \c{buildfiles} to make sure that overall they match the upstream +build. Of particular interest are the header and source directory +\c{buildfiles} (\c{libfoo/include/foo/buildfile} and \c{libfoo/src/buildfile} +in the above listing) which define how the library is built and installed. + +Here we are focusing on the macro-level differences that are easier to change +by tweaking the \c{bdep-new} command line rather than manually. For example, +if we look at the generated source directory \c{buildfile} and realize it +builds a \i{binful} library (that is, a library that includes source files and +therefore produces library binaries) while the upstream library is header-only, +it is much easier to fix this by re-running \c{bdep-new} with the \c{binless} +sub-option than by changing the \c{buildfiles} manually. + +\N|Don't be tempted to start making manual changes at this stage even if you +cannot see anything else that can be fixed with a \c{bdep-new} re-run. This +is still a dry-run and we will recreate the package one more time in the +following section before starting manual adjustments.| + +Besides examining the generated \c{buildfiles}, it's also a good idea to +build, test, and install the generated package to make sure everything ends up +where you expected and matches upstream where necessary. In particular, make +sure public headers are installed into the same location as upstream (unless +you have decided to deviate, of course) or at least it's clear how to tweak +the generated \c{buildfiles} to achieve this. + +\N|The \c{bdep-new}-generated library is a simple \"Hello, World!\" example +that can nevertheless be built, tested, and installed. The idea here is to +verify it matches upstream using the generated source files before replacing +them with the upstream source file symlinks. + +If you are using Windows, then you will need to temporarily replace the +\c{no-symexport} sub-option with \c{auto-symexport} in order to make the +generated library buildable. But do not forget to drop this sub-option in +the next step.| + +Note that at this stage it's easiest to build, test, and install in the source +directory, skipping the \c{bdep} initialization of the package (which we would +have to de-initialize before we can re-run \c{bdep-new}). Continue with the +above example, the recommended sequence of commands would be: + +\ +$ cd libfoo/ +$ b update +$ b test +$ rm -rf /tmp/install +$ b install config.install.root=/tmp/install +$ b clean +\ + +\N|One relatively common case where the installation location may not match +upstream are libraries that include their headers without the subdirectory +prefix (for example, \c{<foo_util.h>} instead of \c{<foo/util.h>}). In such +cases, in the \c{bdep-new} command, you want to use \i{prefix} rather that +\i{source subdirectory} (with the latter being the default). For example: + +\ +$ bdep new --lib,no-subdir,prefix=foo ... +\ + +See \l{bdep-new.xhtml#src-layout SOURCE LAYOUT} for details.| + +Let's also briefly discuss other subdirectories and files found in the +\c{bdep-new}-generated \c{libfoo} package. + +The \c{build/} subdirectory is the standard \c{build2} place for project-wide +build system information (see \l{b#intro-proj-struct Project Structure} for +details). We will look closer at its contents in the following sections. + +In the root directory of our package we find the root \c{buildfile} and +package \c{manifest}. We will be tweaking both in the following steps. There +is also \c{README.md} which we will replace with the upstream symlink. + +The \c{tests/} subdirectory is the standard \c{build2} tests subproject (see +\l{b#intro-operations-test Testing} for background and details). While you can +suppress its generation with the \c{no-tests} \c{bdep-new} sub-option, we +recommend that you keep it and use it as a starting point for porting upstream +tests or, if upstream doesn't provide any, for a basic \"smoke test\". + +\N|You can easily add/remove/rename this \c{tests/} subproject. The only place +where it is mentioned explicitly and where you will need to make changes is +the root \c{buildfile}. In particular, if upstream provides examples that you +wish to port, it is recommended that you use a copy of the generated +\c{tests/} subproject as a starting point (not forgetting to add the +corresponding entry in the root \c{buildfile}).| + + +\h2#core-package-create|Create final package| + +If you are satisfied with the \c{bdep-new} command line and there are no more +automatic adjustments you can squeeze out of it, then it's time to re-run +\c{bdep-new} one last time to create the final package. + +\N|While redoing this step later will require more effort, especially if +you've made manual modifications to \c{buildfile} and \c{manifest}, nothing is +set in stone and it can be done again by simply removing the package +directory, removing (or editing, if you have multiple packages and only want +to redo some of them) \c{packages.manifest}, and starting over.| + +This time, however, we will do things a bit differently in order to take +advantage of some additional automation offered by \c{bdep-new}. + +Firstly, we will use the special \c{third-party} sub-option which is meant for +converting third-party projects to \c{build2}. Specifically, this sub-option +automatically enables \c{no-version} and \c{no-symexport} (unless +\c{auto-symexport} is specified). It also adds a number of values to +\c{manifest} that makes sense to specify in a package of a third-party +project. Finally, it generates the \c{PACKAGE-README.md} template which +describes how to use the package from a \c{build2}-based project (see the +\l{bpkg#manifest-package-description \c{package-description}} \c{manifest} +value for background). + +Secondly, if the package directory already exists and contains certain files, +\c{bdep-new} can take this into account when generating the root \c{buildfile} +and package \c{manifest}. In particular, it will try to guess the license from +the \c{LICENSE} file and extract the summary from \c{README.md} and use this +information in \c{manifest} as well as generated \c{PACKAGE-README.md}. + +\N|If the file names or formats used by upstream don't match those recognized +by \c{bdep-new}, then for now simply omit the corresponding files from the +package directory and add them later manually. Similarly, if an attempt to +extract the information is unsuccessful, we will have a chance to adjust it in +\c{manifest} later. + +Specifically, for \c{README}, \c{bdep-new} recognizes \c{README.md}, +\c{README.txt} and \c{README} but will only attempt to extract the summary +from \c{README.md}. + +For license files, \c{bdep-new} recognizes \c{LICENSE}, \c{LICENSE.txt} +\c{LICENSE.md}, \c{COPYING}, and \c{UNLICENSE}. + +For changes-related files, \c{bdep-new} recognizes \c{NEWS}, \c{CHANGES}, and +\c{CHANGELOG} in various cases as well as with the \c{.md}, \c{.txt} +extensions.| + +Continuing with our \c{libfoo} example and assuming upstream provides the +\c{README.md}, \c{LICENSE}, and \c{NEWS} files, we first manually create the +package directory, then add the symlinks, and finally run \c{bdep-new} (notice +that we have replaced \c{no-version} and \c{no-symexport} with \c{third-party} +and omitted the package name from the \c{bdep-new} command line since we are +running from inside the package directory): + +\ +$ cd foo/ # Change to the package repository root. + +$ rm -r libfoo/ packages.manifest +$ mkdir libfoo/ + +$ cd libfoo/ # Change to the package root. +$ ln -s ../upstream/README.md ./ +$ ln -s ../upstream/LICENSE ./ +$ ln -s ../upstream/NEWS ./ + +$ bdep new --package \ + --lang c++,cpp \ + --type lib,split,subdir=foo,no-subdir-source,third-party +\ + +The final contents of our package will look like this (\c{->} denotes a +symlink): + +\ +$ cd ../ +$ tree libfoo/ +libfoo/ +├── build/ +│ └── ... +├── include/ +│ └── foo/ +│ ├── buildfile +│ └── foo.hpp +├── src/ +│ ├── buildfile +│ └── foo.cpp +├── tests/ +│ ├── build/ +│ │ └── ... +│ ├── basics/ +│ │ ├── buildfile +│ │ └── driver.cpp +│ └── buildfile +├── buildfile +├── manifest +├── NEWS -> ../upstream/NEWS +├── LICENSE -> ../upstream/LICENSE +├── README.md -> ../upstream/README.md +└── PACKAGE-README.md +\ + +If auto-detection of \c{README}, \c{LICENSE}, and \c{NEWS} succeeds, then you +should see the \c{summary} and \c{license} values automatically populated in +\c{manifest} and the symlinked files listed in the root \c{buildfile}. + + +\h2#core-package-adjust-version|Adjust package version| + +While adjusting the \c{bdep-new}-generated code is the subject of the +following sections, one tweak that we want to make right away is to change the +package version in the \c{manifest} file. + +In this guide we will assume the upstream package uses semver (semantic +version) or semver-like (that is, has three version components) and will rely +on the \i{continuous versioning} feature of \c{build2} to make sure that each +commit in our package repository has a distinct version (see +\l{intro#guide-versioning-releasing Versioning and Release Management} for +background). + +\N|If upstream does not use semver, then see +\l{https://github.com/build2/HOWTO/blob/master/entries/handle-projects-which-dont-use-semver.md +How do I handle projects that don't use semantic versioning?} and +\l{https://github.com/build2/HOWTO/blob/master/entries/handle-projects-which-dont-use-version.md +How do I handle projects that don't use versions at all?} for available +options. If you decide to use the non-semver upstream version as is, then you +will have to forgo \i{continuous versioning} as well as the use of +\l{bdep-release(1)} for release management. The rest of the guide, however, +will still apply. In particular, you will still be able to use \l{bdep-ci(1)} +and \l{bdep-publish(1)} with a bit of extra effort.| + +The overall plan to implement continuous versioning is to start with a +pre-release snapshot of the upstream version, keep it like that while we are +adjusting the \c{bdep-new}-generated package and committing our changes (at +which point we get distinct snapshot versions), and finally, when the package +is ready to publish, change to the final upstream version with the help of +\l{bdep-release(1)}. Specifically, if the upstream version is +\c{\i{X}.\i{Y}.\i{Z}}, then we start with the \c{\i{X}.\i{Y}.\i{Z}-a.0.z} +pre-release snapshot. + +\N|In continuous versioning \c{\i{X}.\i{Y}.\i{Z}-a.0.z} means a snapshot after +the (non-existent) \c{0}th alpha pre-release of the \c{\i{X}.\i{Y}.\i{Z}} +version. See \l{intro#guide-versioning-releasing Versioning and Release +Management} for a more detailed explanation and examples.| + +Let's see how this works for our \c{libfoo} example. Say, the upstream version +that we are packaging is \c{2.1.0}. This means we start with \c{2.1.0-a.0.z}. + +\N|Naturally, the upstream version that we are using should correspond to the +commit of the \c{upstream} submodule we have added in the +\l{#core-repo-submodule Add upstream repository as \c{git} submodule} step.| + +Next we edit the \c{manifest} file in the \c{libfoo} package and change the +\c{version} value to read: + +\ +version: 2.1.0-a.0.z +\ + +Let's also commit this initial state of the package for easier rollbacks: + +\ +$ cd foo/ # Change to the package repository root. +$ git add . +$ git status +$ git commit -m \"Initialize package\" +\ + + +\h#core-fill|Fill package with source code and add dependencies| + +With the package skeleton ready, the next steps are to fill it with upstream +source code, add dependencies, and make any necessary manual adjustments to +the generated \c{buildfiles}, \c{manifest}, etc. If we do this all at once, +however, it can be hard to pin-point the cause of build failures. For example, +if we convert both the library and its tests right away and something doesn't +work, it can be hard to determine whether the mistake is in the library or in +the tests. As a result, we are going to split this work into a sequence or +smaller steps that incrementally replace the \c{bdep-new}-generated code with +upstream while allowing us to test each change individually. We will also +commit the changes on each step for easy rollbacks. Specifically, the overall +plan is as follows: + +\ol| + +\li|Initialize (\c{bdep-init}) the package in one or more build configurations.| + +\li|Add dependencies, if any.| + +\li|Fill the library with upstream source code.| + +\li|Adjust project-wide and source subdirectory \c{buildfiles}.| + +\li|Make a smoke test for the library.| + +\li|Replace the smoke test with upstream tests.| + +\li|Tweak root \c{buildfile} and \c{manifest}.|| + +The first three steps are the subject of this section with the following +sections covering the rest of the plan. + +\N|As you become more experienced with packaging third-party projects for +\c{build2}, it may make sense to start combining or omitting some steps, +especially for simpler libraries. For example, if you see that a library +comes with a simple test that shouldn't cause any complications, then you +could omit the smoke test.| + + +\h2#core-fill-init|Initialize package in build configurations| + +Before we start making any changes to the \c{bdep-new}-generated files, let's +initialize the package in at least one build configuration so that we are able +to build and test our changes (see \l{intro#guide Getting Started Guide} for +background on \c{bdep}-based development workflow). Continuing with our +\c{libfoo} example from the earlier steps: + +\ +$ cd foo/ # Change to the package repository root. +$ bdep init -C ../foo-gcc @gcc cc config.cxx=g++ +\ + +\N|If you are initializing subsequent packages in the already created +configuration, then the command line will be just: + +\ +$ bdep init @gcc +\ + +| + +Let's build and test the \c{bdep-new}-generated package to make sure +everything is in order: + +\ +$ bdep update +$ bdep test +$ bdep clean +\ + +You can create additional configurations, for example, if you have access to +several compilers. For instance, to create a build configuration for Clang: + +\ +$ bdep init -C ../foo-clang @clang cc config.cxx=clang++ +\ + +If you would like to perform a certain operation on all the build +configurations, pass the \c{-a|--all} flag to \c{bdep}: + +\ +$ bdep update -a +$ bdep test -a +$ bdep clean -a +\ + +Let's also verify that the resulting package repository is clean (doesn't have +any uncommitted or untracked files): + +\ +$ git status +\ + +\h2#core-fill-depend|Add dependencies| + +If the upstream project has any dependencies, now is a good time to specify +them so that when we attempt to build the upstream source code, they are +already present. + +Identifying whether the upstream project has dependencies is not always easy. +The natural first places to check are the documentation and the existing build +system. Sometimes projects also bundle their dependencies with the project +source code (also called vendoring). So it makes sense to look around the +upstream repository for anything that looks like bundled dependencies. +Normally we would need to \"unbundle\" such dependencies when converting to +\c{build2} by instead specifying a dependency on an external package (see +\l{#dont-bundle Don't bundle dependencies} for background). + +\N|While there are several reasons we insist on unbundling of dependencies, +the main one is that bundling can cause multiple, potentially conflicting +copies of the same dependency to exist in the build. This can cause subtle +build failures that are hard to understand and track down.| + +One particularly common case to check for is bundling of the testing +framework, such as \l{https://cppget.org/catch2 \c{catch2}}, by C++ +projects. If you have identified that the upstream tests depend on a testing +framework (whether bundled or not), see +\l{https://github.com/build2/HOWTO/blob/master/entries/handle-tests-with-extra-dependencies.md +How do I handle tests that have extra dependencies?} for the recommended +way to deal with that. + +\N|One special type of dependency which is easy to overlook is between +packages in the same package repository. For example, if we were packaging +both \c{libfoo} as well as the \c{foo} executable that depends on it, then the +\c{foo} package has a dependency on \c{libfoo} and it must be specified. In +this case we don't need to add anything to \c{repositories.manifest} and in +the \c{depends} entry (see below) in \c{foo}'s \c{manifest} we will normally +use the special \c{==\ $} version constraint, meaning \c{libfoo} should have +the same version as \c{foo} (see the \l{bpkg#manifest-package-depends +\c{depends} package \c{manifest} value} for details). For example: + +\ +depends: libfoo == $ +\ + +| + +If you have concluded that the upstream project doesn't have any dependencies, +then you can remove \c{repositories.manifest} from the package repository root +(unless you have already done so), commit this change, and skip the rest of +this section. + +And if you are still reading, then we assume you have a list of dependencies +you need to add, preferably with their minimum required versions. If you could +not identify the minimum required version for a dependency, then you can +fallback to the latest available version, as will be described in a moment. + +With the list of dependencies in hand, the next step is to determine whether +they are already available as \c{build2} packages. For that, head over to +\l{https://cppget.org cppget.org} and search for each dependency. + +If you are unable to find a package for a dependency, then it means it hasn't +been packaged for \c{build2} yet. Check the places mentioned in the +\l{#core-repo-exists Check if package repository already exists} step to see +if perhaps someone is already working on the package. If not and the +dependency is not optional, then the only way forward is to first package +the dependency. + +If you do find a package for a dependency, then note the section of the +repository (\c{stable}, \c{testing}, etc; see \l{intro#guide-repositories +Package Repositories} for background) from which the minimum required version +of the package is available. If you were unable to identify the minimum +required version, then note the latest version available from the \c{stable} +section. + +Given the list of repository sections, edit the \c{repositories.manifest} file +in the package repository root and uncomment the entry for \c{cppget.org}: + +\ +: +role: prerequisite +location: https://pkg.cppget.org/1/stable +#trust: ... +\ + +Next, replace \c{stable} at the end of the \c{location} value with the least +stable section from your list. For example, if your list contains \c{stable}, +\c{testing}, and \c{beta}, then you need \c{beta} (the sections form a +hierarchy and so \c{beta} includes \c{testing} which in turn includes +\c{stable}). + +\N|If you wish, you can also uncomment the \c{trust} value and replace \c{...} +with the \l{https://cppget.org/?about repository fingerprint}. This way you +won't be prompted to confirm the repository authenticity on the first +fetch. See \l{intro#guide-add-remove-deps Adding and Removing Dependencies} +for details.| + +Once this is done, edit \c{manifest} in package root and add the \c{depends} +value for each dependency. See \l{intro#guide-add-remove-deps Adding and +Removing Dependencies} for background. In particular, here you will use the +minimum required version (or the latest available) to form a version +constraint. Which constraint operator to use will depend on the dependency's +versioning policies. If the dependency uses semver, then a \c{^}-based +constraint is a sensible default. + +As an example, let's say our \c{libfoo} depends on \c{libz}, \c{libasio}, and +\c{libsqlite3}. To specify these dependencies we would add the following +entries to its \c{manifest}: + +\ +depends: libz ^1.2.0 +depends: libasio ^1.28.0 +depends: libsqlite3 ^3.39.4 +\ + +With all the dependencies specified, let's now synchronize the state of the +build configurations with our changes by running \l{bdep-sync(1)} from the +package repository root: + +\ +$ bdep sync -a +\ + +This command should first fetch the metadata for the repository we specified +in \c{repositories.manifest} and then fetch, unpack and configure each +dependency that we specified in \c{manifest}. + +\N|If you have any build-time dependencies (see +\l{intro#guide-build-time-linked Build-Time Dependencies and Linked +Configurations} for background), then you will get a warning about the +corresponding \c{config.import.*} variable being unused and therefore +dropped. This is because we haven't yet added the corresponding \c{import} +directives to our \c{buildfiles}. For now you can ignore this warning, which +we will fix later, when we adjust the generated \c{buildfiles}.| + +We can examine the resulting state, including the version of each dependency, +with \l{bdep-status(1)}: + +\ +$ bdep status -ai +\ + +The last step for this section is to commit our changes: + +\ +$ cd foo/ # Change to the package repository root. +$ git add . +$ git status +$ git commit -m \"Add dependencies\" +\ + + +\h2#core-fill-source|Fill with upstream source code| + +Now we are ready to begin replacing the \c{bdep-new}-generated files with +upstream source code symlinks. We start with the library's header and source +files. Continuing with our \c{libfoo} example, this is what we currently have +(notice that \c{LICENSE}, \c{README.md}, and \c{NEWS} are already symlinks to +upstream): + +\ +$ cd foo/ # Change to the package repository root. + +$ tree libfoo/ +libfoo/ +├── build/ +│ └── ... +├── include/ +│ └── foo/ +│ ├── buildfile +│ └── foo.hpp +├── src/ +│ ├── buildfile +│ └── foo.cpp +├── tests/ +│ └── ... +├── buildfile +├── manifest +├── NEWS -> ../upstream/NEWS +├── LICENSE -> ../upstream/LICENSE +├── README.md -> ../upstream/README.md +└── PACKAGE-README.md +\ + +Now we replace generated \c{include/foo/foo.hpp} with the library's real +headers and \c{src/foo.cpp} with its real source files: + +\ +$ cd libfoo/ # Change to the package root. + +$ cd include/foo/ +$ rm foo.hpp +$ ln -s ../../../upstream/include/foo/*.hpp ./ +$ cd - + +$ cd src/ +$ rm foo.cpp +$ ln -s ../../upstream/src/*.hpp ./ +$ ln -s ../../upstream/src/*.cpp ./ +$ cd - + +$ tree libfoo/ +libfoo/ +├── build/ +│ └── ... +├── include/ +│ └── foo/ +│ ├── buildfile +│ ├── core.hpp -> ../../../upstream/include/foo/core.hpp +│ └── util.hpp -> ../../../upstream/include/foo/util.hpp +├── src/ +│ ├── buildfile +│ ├── impl.hpp -> ../../upstream/src/impl.hpp +│ ├── core.cpp -> ../../upstream/src/core.cpp +│ └── util.cpp -> ../../upstream/src/util.cpp +├── tests/ +│ └── ... +└── ... +\ + +Note that the wildcards used above may not be enough in all situations and +it's a good idea to manually examine the relevant upstream directories and +make sure nothing is missing. Specifically, look out for: + +\ul| + +\li|Header/sources with other extensions, for example, C, Objective-C, etc.| + +\li|Other files that may be needed, for example, \c{.def}, \c{config.h.in}, etc.| + +\li|Subdirectories that contain more header/source files.|| + +If upstream contains subdirectories with additional header/source files, then +you can symlink entire subdirectories instead of doing it file by file. For +example, let's say \c{libfoo}'s upstream source directory contains the +\c{impl/} subdirectory with additional source files: + +\ +$ cd src/ +$ ln -s ../../upstream/impl ./ +$ cd - + +$ tree libfoo/ +libfoo/ +├── build/ +│ └── ... +├── include/ +│ └── ... +├── src/ +│ ├── impl/ -> ../../upstream/src/impl/ +│ │ ├── bar.cpp +│ │ └── baz.cpp +│ ├── buildfile +│ ├── impl.hpp -> ../../upstream/src/impl.hpp +│ ├── core.cpp -> ../../upstream/src/core.cpp +│ └── util.cpp -> ../../upstream/src/util.cpp +├── tests/ +│ └── ... +└── ... +\ + +Wouldn't it be nice if we could symlink the entire top-level subdirectories +(\c{include/foo/} and \c{src/} in our case) instead of symlinking individual +files? As discussed in \l{#core-package-craft-cmd Craft \c{bdep new} command +line to create package}, we can, but we will need to change the package +layout. Specifically, we will need to move the \c{buildfiles} out of the +source subdirectories with the help of the \c{buildfile-in-prefix} sub-option +of \c{bdep-new}. In the above case, we will also need to invent a source +subdirectory in \c{src/}. Whether this is a worthwhile change largely depends +on how many files you have to symlink individually. If it's just a handful, +then it's probably not worth the complication, especially if you have to +invent source subdirectories. On the other hand, if you are looking at +symlinking hundreds of files, changing the layout makes perfect sense. + +\N|One minor drawback of symlinking entire directories is that you cannot +easily patch individual upstream files (see \l{#howto-patch-upstream-source +How do I patch upstream source code?}). + +You will also need to explicitly list such directories as symlinks in +\c{.gitattributes} if you want your package to be usable from the \c{git} +repository directly on Windows. See +\l{https://build2.org/article/symlinks.xhtml#windows Symlinks and Windows} for +details.| + +We won't be able to test this change yet because to make things build we will +most likely also need to tweak the generated \c{buildfiles}, which is the +subject of the next section. However, it still makes sense to commit our +changes to make rollbacks easier: + +\ +$ cd foo/ # Change to the package repository root. +$ git add . +$ git status +$ git commit -m \"Add upstream source symlinks\" +\ + + +\h#core-adjust-build|Adjust project-wide and source \c{buildfiles}| + +With source code and dependencies added, the next step is to adjust the +regenerated \c{buildfiles} that build the library. This involves two places: +the project-wide build system files in \c{build/} and the source subdirectory +\c{buildfiles} (in \c{include/} and \c{src/} for our \c{libfoo} example). + + +\h2#core-adjust-build-wide|Adjust project-wide build system files in \c{build/}| + +We start with reviewing and adjusting the files in the \c{build/} subdirectory +of our package, where you will find three files: + +\ +$ cd foo/ # Change to the package repository root. +$ tree libfoo/ +libfoo/ +├── build/ +│ ├── bootstrap.build +│ ├── root.build +│ └── export.build +└── ... +\ + + +To recap, the first two contain the project-wide build system setup (see +\l{b#intro-proj-struct Project Structure} for details) while the last is an +export stub that facilitates the importation of targets from our package (see +\l{b#intro-import Target Importation} for details). + +Normally you don't need to change anything in \c{bootstrap.build} \- all it +does is specify the build system project name and load a standard set of core +build system modules. Likewise, \c{export.build} is ok as generated unless you +need to do something special, like exporting targets from different +subdirectories of your package. + +While \c{root.build} is also often good as is, situations where you may +need to tweak it are not uncommon and include: + +\ul| + +\li|Loading an additional build system module. + + For example, if your package makes use of Objective-C/C++ (see \l{b#c-objc + Objective-C Compilation} and \l{b#cxx-objcxx Objective-C++ Compilation}) + or Assembler (see \l{b#c-as-cpp Assembler with C Preprocessor + Compilation}), then \c{root.build} would be the natural place to load the + corresponding modules. + + \N|If your package uses a mixture of C and C++, then it's recommended to + set this up using the \c{--lang} sub-option of \c{bdep-new} rather + than manually. For example: + + \ + $ bdep new --lang c++,c ... + \ + + || + +\li|Specifying package configuration variables. + + If upstream provides the ability to configure their code, for example to + enable optional features, then you may want to translate this to + \c{build2} configuration variables, which are specified in \c{root.build} + (see \l{b#proj-config Project Configuration} for background and details). + + Note that you don't need to add all the configuration variables right + away. Instead, you could first handle the \"core\" functionality which + doesn't require any configuration and then add the configuration variables + one by one while also making the corresponding changes in \c{buildfiles}. + + \N|One type of configuration that you should normally not expose when + packaging for \c{build2} is support for both header-only and compiled + modes. See \l{b#dont-header-only Don't make library header-only if it + can be compiled} for details.||| + +Also, in C++ projects, if you don't have any inline or template files, then +you can drop the assignment of the file extension for the \c{ixx{\}} and +\c{txx{\}} target types, respectively. + +If you have added any configuration variables and would like to use +non-default values for some of them in your build, then you will need to +reconfigure the package. For example, let's say we have added the +\c{config.libfoo.debug} variable to our \c{libfoo} package which enables +additional debugging facilities in the library. This is how we can reconfigure +all our builds to enable this functionality: + +\ +$ bdep sync -a config.libfoo.debug=true +\ + +If you have made any changes, commit them (similar to the previous step, we +cannot test things just yet): + +\ +$ cd foo/ # Change to the package repository root. +$ git add . +$ git status +$ git commit -m \"Adjust project-wide build system files\" +\ + + +\h2#core-adjust-build-src|Adjust source subdirectory \c{buildfiles}| + +The last step we need to perform before we can try to build our library is to +adjust its \c{buildfiles}. These \c{buildfiles} are found in the source +subdirectory or, if we used the \c{buildfile-in-prefix} \c{bdep-new} +sub-option, in the prefix directory. There will be two \c{buildfiles} if we +use the split layout (\c{split} sub-option) or a single \c{buildfile} in the +combined layout. The single \c{buildfile} in the combined layout contains +essentially the same definitions as the split \c{buildfiles} but combined into +one and with some minor simplifications that this allows. Here we will assume +the split layout and continue with our \c{libfoo} from the previous +sections. To recap, here is the layout we've got with the \c{buildfiles} of +interest found in \c{include/foo/} and in \c{src/}: + +\ +libfoo/ +├── build/ +│ └── ... +├── include/ +│ └── foo/ +│ ├── buildfile +│ ├── core.hpp -> ../../../upstream/include/foo/core.hpp +│ └── util.hpp -> ../../../upstream/include/foo/util.hpp +├── src/ +│ ├── buildfile +│ ├── impl.hpp -> ../../upstream/src/impl.hpp +│ ├── core.cpp -> ../../upstream/src/core.cpp +│ └── util.cpp -> ../../upstream/src/util.cpp +├── tests/ +│ └── ... +└── ... +\ + +\N|If instead of a library you are packaging an executable, you can skip +directly to \l{#core-adjust-build-src-source-exe Adjust source \c{buildfile}: +executables}.| + + +\h2#core-adjust-build-src-header|Adjust header \c{buildfile}| + +The \c{buildfile} in \c{include/foo/} is pretty simple: + +\N|The \c{buildfile} in your package may look slightly different, depending on +the exact \c{bdep-new} sub-options used. However, all the relevant definitions +discussed below should still be easily recognizable.| + +\ +pub_hdrs = {hxx ixx txx}{**} + +./: $pub_hdrs + +# Install into the foo/ subdirectory of, say, /usr/include/ +# recreating subdirectories. +# +{hxx ixx txx}{*}: +{ + install = include/foo/ + install.subdirs = true +} +\ + +Normally, the only change that you would make to this \c{buildfile} is to +adjust the installation location of headers (see \l{b#intro-operations-install +Installing} for background). In particular, if our headers were included +without the \c{<foo/...>} prefix but instead contained the library name in +their names (for example, \c{foo-util.hpp}), then the installation setup would +instead look like this: + +\ +# Install directly into say, /usr/include/ recreating subdirectories. +# +{hxx ixx txx}{*}: +{ + install = include/ + install.subdirs = true +} +\ + +If the library doesn't have any headers in nested subdirectories (for example, +\c{<foo/util/string.hpp>}), you can drop the \c{install.subdirs} variable: + +\ +# Install into the foo/ subdirectory of, say, /usr/include/. +# +{hxx ixx txx}{*}: install = include/foo/ +\ + +\N|In the combined layout, the installation-related definitions are at the end +of the combined \c{buildfile}. + +Compared to the split layout where the public and private headers are +separated physically, in the combined layout you may need to achieve the same +result (that is, avoid installing private headers) at the build system +level. If the library provides only a handful of public headers and this set +is unlikely to change often, then listing them explicitly is the most +straightforward approach. For example (the \c{@./} qualifier tells \c{build2} +they are in the source directory): + +\ +# Only install public headers into, say, /usr/include/. +# +h{foo}@./ h{foo_version}@./: install = include/ +h{*}: install = false +\ + +| + +See also \l{#howto-extra-header-install-subdir How do I handle extra header +installation subdirectory?} + + +\h2#core-adjust-build-src-source|Adjust source \c{buildfile}: overview| + +Next is the \c{buildfile} in \c{src/}: + +\N|Again, the \c{buildfile} in your package may look slightly different, +depending on the exact \c{bdep-new} sub-options used. However, all the +relevant definitions discussed below should still be easily recognizable. + +For a binless (header-only) library, this \c{buildfile} will contain only a +small subset of the definitions shown below. See +\l{https://github.com/build2/HOWTO/blob/master/entries/make-header-only-library.md +How do I make a header-only C/C++ library?} for additional considerations when +packaging header-only libraries.| + +\ +intf_libs = # Interface dependencies. +impl_libs = # Implementation dependencies. +#import xxxx_libs += libhello%lib{hello} + +# Public headers. +# +pub = [dir_path] ../include/foo/ + +include $pub + +pub_hdrs = $($pub/ pub_hdrs) + +lib{foo}: $pub/{$pub_hdrs} + +# Private headers and sources as well as dependencies. +# +lib{foo}: {hxx ixx txx cxx}{**} $impl_libs $intf_libs + +# Build options. +# +out_pfx_inc = [dir_path] $out_root/include/ +src_pfx_inc = [dir_path] $src_root/include/ +out_pfx_src = [dir_path] $out_root/src/ +src_pfx_src = [dir_path] $src_root/src/ + +cxx.poptions =+ \"-I$out_pfx_src\" \"-I$src_pfx_src\" \ + \"-I$out_pfx_inc\" \"-I$src_pfx_inc\" + +#obja{*}: cxx.poptions += -DFOO_STATIC_BUILD +#objs{*}: cxx.poptions += -DFOO_SHARED_BUILD + +# Export options. +# +lib{foo}: +{ + cxx.export.poptions = \"-I$out_pfx_inc\" \"-I$src_pfx_inc\" + cxx.export.libs = $intf_libs +} + +#liba{foo}: cxx.export.poptions += -DFOO_STATIC +#libs{foo}: cxx.export.poptions += -DFOO_SHARED + +# For pre-releases use the complete version to make sure they cannot +# be used in place of another pre-release or the final version. See +# the version module for details on the version.* variable values. +# +if $version.pre_release + lib{foo}: bin.lib.version = \"-$version.project_id\" +else + lib{foo}: bin.lib.version = \"-$version.major.$version.minor\" + +# Don't install private headers. +# +{hxx ixx txx}{*}: install = false +\ + + +\h2#core-adjust-build-src-source-clean|Adjust source \c{buildfile}: cleanup| + +As a first step, let's remove all the definitions that we don't need in our +library. The two common pieces of functionality that are often not needed +are support for auto-generated headers (such as \c{config.h} generated from +\c{config.h.in}) and dependencies on other libraries. + +If you don't have any auto-generated headers, then remove all the assignments +and expansions of the \c{out_pfx_inc} and \c{out_pfx_src} variables. Here +is what the relevant lines in the above \c{buildfile} should look like after +this change: + +\ +# Build options. +# +src_pfx_inc = [dir_path] $src_root/include/ +src_pfx_src = [dir_path] $src_root/src/ + +cxx.poptions =+ \"-I$src_pfx_src\" \"-I$src_pfx_inc\" + +# Export options. +# +lib{foo}: +{ + cxx.export.poptions = \"-I$src_pfx_inc\" +} +\ + +\N|If you do have auto-generated headers, then in the split layout you can +remove \c{out_pfx_inc} if you only have private auto-generated headers and +\c{out_pfx_src} if you only have public ones.| + +\N|In the combined layout the single \c{buildfile} does not set the +\c{*_pfx_*} variables. Instead it uses the \c{src_root} and \c{out_root} +variables directly. For example: + +\ +# Build options. +# +cxx.poptions =+ \"-I$out_root\" \"-I$src_root\" + +# Export options. +# +lib{foo}: +{ + cxx.export.poptions = \"-I$out_root\" \"-I$src_root\" +} +\ + +To remove support for auto-generated headers in the combined \c{buildfile}, +simply remove the corresponding \c{out_root} expansions: + +\ +# Build options. +# +cxx.poptions =+ \"-I$src_root\" + +# Export options. +# +lib{foo}: +{ + cxx.export.poptions = \"-I$src_root\" +} +\ + +If you only have private auto-generated headers, then only remove the +expansion from \c{cxx.export.poptions}.| + +If you don't have any dependencies, then remove all the assignments and +expansions of the \c{intf_libs} and \c{intf_libs} variables. That is, +the following lines in the original \c{buildfile}: + +\ +intf_libs = # Interface dependencies. +impl_libs = # Implementation dependencies. +#import xxxx_libs += libhello%lib{hello} + +# Private headers and sources as well as dependencies. +# +lib{foo}: {hxx ixx txx cxx}{**} $impl_libs $intf_libs + +# Export options. +# +lib{foo}: +{ + cxx.export.poptions = \"-I$out_pfx_inc\" \"-I$src_pfx_inc\" + cxx.export.libs = $intf_libs +} +\ + +Become just these: + +\ +# Private headers and sources as well as dependencies. +# +lib{foo}: {hxx ixx txx cxx}{**} + +# Export options. +# +lib{foo}: +{ + cxx.export.poptions = \"-I$out_pfx_inc\" \"-I$src_pfx_inc\" +} +\ + + +\h2#core-adjust-build-src-source-dep|Adjust source \c{buildfile}: dependencies| + +If you do have dependencies, then let's handle them now. + +\N|Here we will assume dependencies on other libraries, which is the common +case. If you have dependencies on executables, for example, source code +generators, see \l{intro#guide-build-time-linked Build-Time Dependencies and +Linked Configurations} on how to handle that. In this case you will also need +to reconfigure your package after adding the corresponding \c{import} +directives in order to re-acquire the previously dropped \c{config.import.*} +values. Make sure to also pass any configuration variables you specified in +\l{#core-adjust-build-wide Adjust project-wide build system files in +\c{build/}}. For example: + +\ +$ bdep sync -a --disfigure config.libfoo.debug=true +\ + +| + +For each library that your package depends on (and which you have added +to \c{manifest} in the \l{#core-fill-depend Add dependencies} step), +you need to first determine whether it's an interface or implementation +dependency and then import it either into the \c{intf_libs} or \c{impl_libs} +variable, respectively. + +See \l{b#intro-lib Library Exportation and Versioning} for background on the +interface vs implementation distinction. But as a quick rule of thumb, if the +library you are packaging includes a header from the dependency library in one +of its public headers, then it's an interface dependency. Otherwise, it's an +implementation dependency. + +Continuing with our \c{libfoo} example, as we have established in +\l{#core-fill-depend Add dependencies}, it depends on \c{libasio}, \c{libz}, +and \c{libsqlite3} and let's say we've determined that \c{libasio} is an +interface dependency because it's included from \c{include/foo/core.hpp} while +the other two are implementation dependencies because they are only included +from \c{src/}. Here is how we would change our \c{buildfile} to import them: + +\ +intf_libs = # Interface dependencies. +impl_libs = # Implementation dependencies. +import intf_libs += libasio%lib{asio} +import impl_libs += libz%lib{z} +import impl_libs += libsqlite3%lib{sqlite3} +\ + +You can tidy this a bit further if you would like: + +\ +import intf_libs = libasio%lib{asio} +import impl_libs = libz%lib{z} +import impl_libs += libsqlite3%lib{sqlite3} +\ + +\N|If you don't have any implementation or interface dependencies, you can +remove the assignment and all the expansion of the corresponding \c{*_libs} +variable.| + +Note also that system libraries like \c{-lm}, \c{-ldl} on UNIX or +\c{advapi32.lib}, \c{ws2_32.lib} on Windows should not be imported. Instead, +they should be listed in the \c{c.libs} or \c{cxx.libs} variables. See +\l{https://github.com/build2/HOWTO/blob/master/entries/link-system-library.md +How do I link a system library} for details. + + +\h2#core-adjust-build-src-source-pub|Adjust source \c{buildfile}: public headers| + +With the unnecessary parts of the \c{buildfile} cleaned up and dependencies +handled, let's discuss the common changes to the remaining definitions, going +from top to bottom. We start with the public headers block: + +\ +# Public headers. +# +pub = [dir_path] ../include/foo/ + +include $pub + +pub_hdrs = $($pub/ pub_hdrs) + +lib{foo}: $pub/{$pub_hdrs} +\ + +This block gets hold of the list of public headers and makes them +prerequisites of the library. Normally you shouldn't need to make any changes +here. If you need to exclude some headers, it should be done in the +header \c{buildfile} in the \c{include/} directory. + +\N|In the combined layout the single \c{buildfile} does not have such code. +Instead, all the headers are covered by the wildcard pattern in the following +block.| + + +\h2#core-adjust-build-src-source-src|Adjust source \c{buildfile}: sources, private headers| + +The next block deals with sources, private headers, and dependencies, if any: + +\ +# Private headers and sources as well as dependencies. +# +lib{foo}: {hxx ixx txx cxx}{**} $impl_libs $intf_libs +\ + +By default it will list all the relevant files as prerequisites of the +library, starting from the directory of the \c{buildfile} and including all +the subdirectories, recursively (see \l{b#name-patterns Name Patterns} for +background on wildcard patterns). + +If your C++ package doesn't have any inline or template files, then you can +remove the \c{ixx} and \c{txx} target types, respectively (which would be +parallel to the change made in \c{root.build}; see \l{#core-adjust-build-wide +Adjust project-wide build system files in \c{build/}}). For example: + +\ +# Private headers and sources as well as dependencies. +# +lib{foo}: {hxx cxx}{**} $impl_libs $intf_libs +\ + +\N|Source files other than C/C++ (for example, Assembler, Objective-C/C++) are +dealt with in \l{#core-adjust-build-src-source-ext Adjust source +\c{buildfile}: extra requirements} below.| + +The other common change to this block is the exclusion of certain files or +making them conditionally included. As an example, let's say in our \c{libfoo} +the source subdirectory contains a bunch of \c{*-test.cpp} files which are +unit tests and should not be listed as prerequisites of a library. Here is how +we can exclude them: + +\ +# Private headers and sources as well as dependencies. +# +lib{foo}: {hxx cxx}{** -**-test} $impl_libs $intf_libs +\ + +Let's also assume our \c{libfoo} contains \c{impl-win32.cpp} and +\c{impl-posix.cpp} which provide alternative implementations of the same +functionality for Windows and POSIX and therefore should only be included as +prerequisites on the respective platforms. Here is how we can handle that: + +\ +# Private headers and sources as well as dependencies. +# +lib{foo}: {hxx cxx}{** -impl-win32 -impl-posix -**-test} +lib{foo}: cxx{impl-win32}: include = ($cxx.target.class == 'windows') +lib{foo}: cxx{impl-posix}: include = ($cxx.target.class != 'windows') +lib{foo}: $impl_libs $intf_libs +\ + +There are two nuances in the above example worth highlighting. Firstly, we +have to exclude the files from the wildcard pattern before we can +conditionally include them. Secondly, we have to always link libraries +last. In particular, the following is a shorter but an incorrect version of the +above: + +\ +lib{foo}: {hxx cxx}{** -impl-win32 -impl-posix -**-test} \ + $impl_libs $intf_libs +lib{foo}: cxx{impl-win32}: include = ($cxx.target.class == 'windows') +lib{foo}: cxx{impl-posix}: include = ($cxx.target.class != 'windows') +\ + +\N|You may also be tempted to use the \c{if} directive instead of the +\c{include} variable for conditional prerequisites. For example: + +\ +if ($cxx.target.class == 'windows') + lib{foo}: cxx{impl-win32} +else + lib{foo}: cxx{impl-posix} +\ + +This would also be incorrect. For background and details, see +\l{https://github.com/build2/HOWTO/blob/master/entries/keep-build-graph-config-independent.md +How do I keep the build graph configuration-independent?}| + + +\h2#core-adjust-build-src-source-opt|Adjust source \c{buildfile}: build and export options| + +The next two blocks are the build and export options, which we will discuss +together: + +\ +# Build options. +# +out_pfx_inc = [dir_path] $out_root/include/ +src_pfx_inc = [dir_path] $src_root/include/ +out_pfx_src = [dir_path] $out_root/src/ +src_pfx_src = [dir_path] $src_root/src/ + +cxx.poptions =+ \"-I$out_pfx_src\" \"-I$src_pfx_src\" \ + \"-I$out_pfx_inc\" \"-I$src_pfx_inc\" + +#obja{*}: cxx.poptions += -DFOO_STATIC_BUILD +#objs{*}: cxx.poptions += -DFOO_SHARED_BUILD + +# Export options. +# +lib{foo}: +{ + cxx.export.poptions = \"-I$out_pfx_inc\" \"-I$src_pfx_inc\" + cxx.export.libs = $intf_libs +} + +#liba{foo}: cxx.export.poptions += -DFOO_STATIC +#libs{foo}: cxx.export.poptions += -DFOO_SHARED +\ + +The build options are in effect when the library itself is being built and the +exported options are propagated to the library consumers (see \l{b#intro-lib +Library Exportation and Versioning} for background on exported options). For +now we will ignore the commented out lines that add \c{-DFOO_STATIC*} and +\c{-DFOO_SHARED*} macros \- they are for symbol exporting and we will discuss +this topic separately. + +If the library you are packaging only relied on platform-independent APIs, +then chances are you won't need to change anything here. On the other hand, if +it does anything platform-specific, then you will most likely need to add some +options. + +As discussed in the \l{b#intro-dirs-scopes Output Directories and Scopes} +section of the build system introduction, there is a number of variables that +are used to specify compilation and linking options, such as \c{*.poptions} +(\c{cxx.poptions} in the above example), \c{*.coptions}, etc. The below table +shows all of them with their rough \c{make} equivalents in the third column: + +\ +*.poptions preprocess CPPFLAGS +*.coptions compile CFLAGS/CXXFLAGS +*.loptions link LDFLAGS +*.aoptions archive ARFLAGS +*.libs system libraries LIBS/LDLIBS +\ + +The recommended approach here is to study the upstream build system and copy +custom compile/link options to the appropriate \c{build2} variables. Note, +however, that doing it thoughtlessly/faithfully by copying all the options may +not always be a good idea. See +\l{https://github.com/build2/HOWTO/blob/master/entries/compile-options-in-buildfile.md +Which C/C++ compile/link options are OK to specify in a project's buildfile?} +for the guidelines. + +\N|If you are packaging a library that includes a large number of optional +features, it may be unclear which of them would make sense to enable by +default. The notorious example of this situation is +\l{https://github.com/build2-packaging/sqlite \c{libsqlite3}} which provides +hundreds of preprocessor macros to enable or tune various aspects of its +functionality. + +The recommended approach in cases like this is to study the configuration of +such a library in distributions like Debian and Fedora and use the same +defaults. In particular, this will allow us to substitute the \c{build2} +package with the system-installed version.| + +Oftentimes, custom options must only be specified for certain target platforms +or when using a certain compiler. While \c{build2} provides a large amount of +information to identify the build configuration as well as more advanced +\c{buildfile} language mechanisms (such as \l{b#intro-switch Pattern +Matching}) to make sense of it, this is a large topic for which we refer you +to \l{b The \c{build2} Build System} manual. Additionally, +\l{https://github.com/build2-packaging github.com/build2-packaging} now +contains a large number of packages that you can study and search for +examples. + +\N|While exporting preprocessor macros to communicate configuration is a +fairly common technique, it has a number of drawbacks and limitations. +Specifically, a large number of such macros will add a lot of noise to the +consumer's compilation command lines (especially if multiple libraries indulge +in this). Plus, the information conveyed by such macros is limited to simple +values and is not easily accessible in consumer \c{buildfiles}. + +To overcome these drawbacks and limitations, \c{build2} provides a mechanism +for conveying metadata with C/C++ libraries (and executables). See, +\l{https://github.com/build2/HOWTO/blob/master/entries/convey-additional-information-with-exe-lib.md +How do I convey additional information (metadata) with executables and C/C++ +libraries?} for details. + +Note that outright replacing the preprocessor macros with metadata can be done +if this information is only used by the library consumers. In other words, if +the library's public headers rely on the presence of such macros, then we have +no choice but to export them, potentially also providing the metadata so that +this information is easily accessible from \c{buildfiles}.| + +Let's consider a representative example based on our \c{libfoo} to get a sense +of what this normally looks like as well as to highlight a few nuances. We +will assume our \c{libfoo} requires either the \c{FOO_POSIX} or \c{FOO_WIN32} +macro to be defined during the build in order to identify the target +platform. Additionally, extra features can be enabled by defining +\c{FOO_EXTRAS}, which should be done both during the build and for consumption +(so this macro must also be exported). Next, this library requires the +\c{-fno-strict-aliasing} compile option for the GCC-class compilers (GCC, +Clang, etc). Finally, we need to link \c{pthread} on POSIX and \c{ws2_32.lib} +on Windows. This is how we would work all this into the above fragment: + +\ +# Build options. +# +out_pfx_inc = [dir_path] $out_root/include/ +src_pfx_inc = [dir_path] $src_root/include/ +out_pfx_src = [dir_path] $out_root/src/ +src_pfx_src = [dir_path] $src_root/src/ + +cxx.poptions =+ \"-I$out_pfx_src\" \"-I$src_pfx_src\" \ + \"-I$out_pfx_inc\" \"-I$src_pfx_inc\" + +cxx.poptions += -DFOO_EXTRAS + +if ($cxx.target.class == 'windows') + cxx.poptions += -DFOO_WIN32 +else + cxx.poptions += -DFOO_POSIX + +#obja{*}: cxx.poptions += -DFOO_STATIC_BUILD +#objs{*}: cxx.poptions += -DFOO_SHARED_BUILD + +if ($cxx.class == 'gcc') + cxx.coptions += -fno-strict-aliasing + +switch $cxx.target.class, $cxx.target.system +{ + case 'windows', 'mingw32' + cxx.libs += -lws2_32 + case 'windows' + cxx.libs += ws2_32.lib + default + cxx.libs += -pthread +} + +# Export options. +# +lib{foo}: +{ + cxx.export.poptions = \"-I$out_pfx_inc\" \"-I$src_pfx_inc\" -DFOO_EXTRAS + cxx.export.libs = $intf_libs +} + +#liba{foo}: cxx.export.poptions += -DFOO_STATIC +#libs{foo}: cxx.export.poptions += -DFOO_SHARED +\ + +There are a few nuances in the above code worth keeping in mind. Firstly, +notice that we append (rather than assign) to all the non-export variables +(\c{*.poptions}, \c{*.coptions}, \c{*.libs}). This is because they may already +contain some values specified by the user with their \c{config.*.*} +counterparts. On the other hand, the \c{*.export.*} variables are assigned. + +Secondly, the order in which we append to the variables is important for the +value to accumulate correctly. You want to first append all the scope-level +values, then target type/pattern-specific, and finally any target-specific; +that is, from more general to more specific (see \l{b#intro-lang Buildfile +Language} for background). To illustrate this point, let's say in our +\c{libfoo}, the \c{FOO_POSIX} or \c{FOO_WIN32} macro are only necessary when +compiling \c{util.cpp}. Below would be the correct order of assigning to +\c{cxx.poptions}: + +\ +cxx.poptions =+ \"-I$out_pfx_src\" \"-I$src_pfx_src\" \ + \"-I$out_pfx_inc\" \"-I$src_pfx_inc\" + +cxx.poptions += -DFOO_EXTRAS + +#obja{*}: cxx.poptions += -DFOO_STATIC_BUILD +#objs{*}: cxx.poptions += -DFOO_SHARED_BUILD + +if ($cxx.target.class == 'windows') + {obja objs}{util}: cxx.poptions += -DFOO_WIN32 +else + {obja objs}{util}: cxx.poptions += -DFOO_POSIX +\ + +\N|Note that target-specific \c{*.poptions} and \c{*.coptions} must be +specified on the object file targets while \c{*.loptions} and \c{*.libs} \- on +the library or executable targets.| + + +\h2#core-adjust-build-src-source-sym|Adjust source \c{buildfile}: symbol exporting| + +Let's now turn to a special sub-topic of the build and export options that +relates to the shared library symbol exporting. To recap, a shared library on +Windows must explicitly specify the symbols (functions and global data) that +it wishes to make accessible by its consumers (executables and other shared +libraries). This can be achieved in three different ways: The library can +explicitly mark in its source code the names whose symbols should be +exported. Alternatively, the library can provide a \c{.def} file to the linker +that lists the symbols to be exported. Finally, the library can request +the automatic exporting of all symbols, which is the default semantics on +non-Windows platforms. Note that the last two approaches only work for +exporting functions, not data, unless special extra steps are taken by the +library consumers. Let's discuss each of these approaches in the reverse +order, that is, starting with the automatic symbol exporting. + +The automatic symbol exporting is implemented in \c{build2} by generating a +\c{.def} file that exports all the relevant symbols. It requires a few +additional definitions in our \c{buildfile} as described in +\l{b#cc-auto-symexport Automatic DLL Symbol Exporting}. You can automatically +generate the necessary setup with the \c{auto-symexport} \c{bdep-new} +sub-option. + +Using a custom \c{.def} file to export symbols is fairly straightforward: +simply list it as a prerequisite of the library and it will be automatically +passed to the linker when necessary. For example: + +\ +# Private headers and sources as well as dependencies. +# +lib{foo}: {hxx cxx}{**} $impl_libs $intf_libs def{foo} +\ + +\N|Some third-party projects automatically generate their \c{.def} file. In +this case you can try to re-create the same generation in the \c{buildfile} +using an ad hoc recipe (or the \l{b#module-in \c{in}} or +\l{https://github.com/build2/libbuild2-autoconf \c{autoconf}} build system +modules). If that doesn't look possible (for example, if the generation logic +is complex and is implemented in something like Perl or Python), then you can +try your luck with automatic symbol exporting. Failing that, the only +remaining option is to use a pre-generated \c{.def} file in the \c{build2} +build.| + +The last approach is to explicitly specify in the source code which symbols +must be exported by marking the corresponding declarations with +\c{__declspec(dllexport)} during the library build and +\c{__declspec(dllimport)} during the library use. This is commonly achieved +with a macro, customarily called \c{*_EXPORT} or \c{*_API}, which is defined +to one of the above specifiers based on whether static or shared library is +being built or is being consumed, which, in turn, is also normally signaled +with a few more macros, such as \c{*_BUILD_DLL} and \c{*_USE_STATIC}. + +\N|Because this approach requires extensive changes to the source code, you +will normally only use it in your \c{build2} build if it is already used in +the upstream build.| + +In \c{build2} you can explicitly signal any of the four situations +(shared/static, built/consumed) by uncommenting and adjusting the following +four lines in the build and export options blocks: + +\ +# Build options. +# + +... + +#obja{*}: cxx.poptions += -DFOO_STATIC_BUILD +#objs{*}: cxx.poptions += -DFOO_SHARED_BUILD + +# Export options. +# + +... + +#liba{foo}: cxx.export.poptions += -DFOO_STATIC +#libs{foo}: cxx.export.poptions += -DFOO_SHARED +\ + +As an example, let's assume our \c{libfoo} defines in one of its headers the +\c{FOO_EXPORT} macro based on the \c{FOO_BUILD_DLL} (shared library is being +build) and \c{FOO_USE_STATIC} (static library is being used) macros that it +expects to be appropriately defined by the build system. This is how we would +modify the above fragment to handle this setup: + +\ +# Build options. +# + +... + +objs{*}: cxx.poptions += -DFOO_BUILD_DLL + +# Export options. +# + +... + +liba{foo}: cxx.export.poptions += -DFOO_USE_STATIC +\ + + +\h2#core-adjust-build-src-source-ver|Adjust source \c{buildfile}: shared library version| + +The final few lines in the above \c{buildfile} deal with shared library binary +(ABI) versioning: + +\ +# For pre-releases use the complete version to make sure they cannot +# be used in place of another pre-release or the final version. See +# the version module for details on the version.* variable values. +# +if $version.pre_release + lib{foo}: bin.lib.version = \"-$version.project_id\" +else + lib{foo}: bin.lib.version = \"-$version.major.$version.minor\" +\ + +The \c{bdep-new}-generated setup arranges for the platform-independent +versioning where the package's major and minor version components are embedded +into the shared library binary name (and \c{soname}) under the assumption +that only patch versions are ABI-compatible. + +The two situations where you would want to change this are when the above +assumption does not hold and/or when upstream provides platform-specific +shared library versions which you would like to re-create in your \c{build2} +build. See \l{b#intro-lib Library Exportation and Versioning} for background +and details. + + +\h2#core-adjust-build-src-source-exe|Adjust source \c{buildfile}: executables| + +If instead of a library you are packaging an executable, then, as mentioned +earlier, it will most likely be a combined layout with a single \c{buildfile}. +This \c{buildfile} will also be much simpler compared to the library's. For +example, give the following \c{bdep-new} command: + +\ +$ bdep new --package \ + --lang c++ \ + --type exe,no-subdir,prefix=foo,export-stub \ + foo +\ + +The resulting source \c{buildfile} will look like this: + +\ +libs = +#import libs += libhello%lib{hello} + +exe{foo}: {hxx ixx txx cxx}{**} $libs testscript + +out_pfx = [dir_path] $out_root/foo/ +src_pfx = [dir_path] $src_root/foo/ + +cxx.poptions =+ \"-I$out_pfx\" \"-I$src_pfx\" +\ + +If the executable doesn't have any inline/template/header files, then you can +remove the \c{ixx}/\c{txx}/\c{hxx} target types, respectively (which would be +parallel to the change made in \c{root.build}; see \l{#core-adjust-build-wide +Adjust project-wide build system files in \c{build/}}). For example: + +\ +exe{foo}: {hxx cxx}{**} $libs testscript +\ + +If the source code includes its own headers with the \c{\"\"} style inclusion +(or doesn't have any headers), then we can also get rid of \c{out_pfx} and +\c{src_pfx}. For example: + +\ +libs = +#import libs += libhello%lib{hello} + +exe{foo}: {hxx ixx txx cxx}{**} $libs testscript +\ + +\N|Unfortunately, it's not uncommon for projects that provide both a library +and an executable, for the executable source code to include public and/or +private library headers with the relative \c{\"\"} style inclusion. For +example: + +\ +#include \"../../libfoo/include/foo/util.hpp\" +#include \"../../libfoo/src/impl.hpp\" +\ + +This approach won't work in \c{build2} since the two packages may end up in +different directories or the library could even be installed. There are two +techniques that can be used to work around this issue (other than patching the +upstream source code). + +For public headers we can provide, in the appropriate places within the +executable package, \"thunk headers\" with the same names as public headers +that simply include the corresponding public header from the library using the +\c{<>} style inclusion. + +For private headers we can provide, again in the appropriate places within the +executable package, our own symlinks for a subset of private headers. Note +that this will only work if the use of private headers within the executable +does not depend on any symbols that are not exported by the library (failing +that, the executable will have to always link to the static variant of the +library). + +For a real example of both of these techniques, see the +\l{https://github.com/build2-packaging/zstd \c{zstd}} package repository.| + +Dealing with dependencies in executables is similar to libraries except that +here we don't have the interface/implementation distinction; see the +\l{#core-adjust-build-src-source-dep Adjust source \c{buildfile}: +dependencies} step. For example: + +\ +import libs = libfoo%lib{foo} + +exe{foo}: {hxx ixx txx cxx}{**} $libs testscript +\ + +Likewise, dealing with build options in executables is similar to libraries +except that here we have no export options; see the +\l{#core-adjust-build-src-source-opt Adjust source \c{buildfile}: build and +export options} step. + +If the executable can plausibly be used in a build, then it's recommended to +add \c{build2} metadata as describe in +\l{https://github.com/build2/HOWTO/blob/master/entries/convey-additional-information-with-exe-lib.md +How do I convey additional information (metadata) with executables and C/C++ +libraries?} See also \l{#howto-patch-upstream-source-preproc Modifying +upstream source code with C/C++ preprocessor} on how to do it without +physically modifying upstream source code. \N{See the +\l{https://github.com/build2-packaging/zstd \c{zstd}} package repository for a +real example of doing this.} + +\N|We will discuss the \c{testscript} prerequisite in the +\l{#core-test-smoke-exe Make smoke test: executables} step below.| + + +\h2#core-adjust-build-src-source-ext|Adjust source \c{buildfile}: extra requirements| + +The changes discussed so far should be sufficient to handle a typical library +or executable that is written in C and/or C++ and is able to handle platform +differences with the preprocessor and compile/link options. However, sooner or +later you will run into a more complex library that may use additional +languages, require more elaborate platform detection, or use additional +functionality, such as support for source code generators. The below list +provides pointers to resources that cover the more commonly encountered +additional requirements. + +\ul| + +\li|\l{b#module-in The \c{in} build system module} + +Use to process \c{config.h.in} (or other \c{.in} files) that don't require +Autoconf-style platform probing (\c{HAVE_*} options).| + +\li|\l{https://github.com/build2/libbuild2-autoconf The \c{autoconf} build + system module} + +Use to process \c{config.h.in} (or their CMake/Meson variants) that require +Autoconf-style platform probing (\c{HAVE_*} options) or CMake/Meson-specific +substitution syntax (\c{#cmakedefine}, etc).| + +\li|\l{b#c-objc Objective-C Compilation} and + \l{b#cxx-objcxx Objective-C++ Compilation} + +Use to compile Objective-C (\c{.m}) or Objective-C++ (\c{.mm}) source files.| + +\li|\l{b#c-as-cpp Assembler with C Preprocessor Compilation} + +Use to compile Assembler with C Preprocessor (\c{.S}) source files.| + +\li|\l{b#intro-unit-test Implementing Unit Testing} + +Use if upstream has tests (normally unit tests) in the source subdirectory.| + +\li|\l{intro#guide-build-time-linked Build-Time Dependencies and Linked + Configurations} + +Use if upstream relies on source code generators, such as +\l{https://cppget.org/reflex \c{lex}} and \l{https://cppget.org/byacc +\c{yacc}}.| + +\li|\l{https://github.com/build2/HOWTO/ The \c{build2} HOWTO} + +See the \c{build2} HOWTO article collection for more unusual requirements.|| + + +\h2#core-adjust-build-test|Test library build| + +At this point our library should be ready to build, at least in theory. While +we cannot build and test the entire package before adjusting the generated +\c{tests/} subproject (the subject of the next step), we can try to build just +the library and, if it has any unit tests in the source subdirectory, even run +some tests. + +\N|If the library is header only, there won't be anything to build unless +there are unit tests. Still, you may want to continue with this exercise to +detect any syntactic mistakes in the \c{buildfiles}, etc.| + +To build only a specific subdirectory of our package, we use the build system +directly (continuing with our \c{libfoo} example): + +\ +$ cd libfoo/src/ # Change to the source subdirectory. +$ b update +\ + +If there are any issues, try to fix them and then build again. Once the +library builds and if it has unit tests, you can try to run them: + +\ +$ b test +\ + +It also makes sense to test the installation and see if anything is off (such +as private headers being installed): + +\ +$ rm -rf /tmp/install +$ b install config.install.root=/tmp/install +\ + +Once the library builds, it makes sense to commit our changes for easier +rollbacks: + +\ +$ cd foo/ # Change to the package repository root. +$ git add . +$ git status +$ git commit -m \"Adjust source subdirectory buildfiles\" +\ + + +\h#core-test-smoke|Make smoke test| + +With the library build sorted, we need tests to make sure the result is +actually functional. As \l{#core-fill discussed earlier}, it is recommended to +start with a simple \"smoke test\", make sure that works, and then replace it +with upstream tests. However, if upstream tests look simple enough, you can +skip the smoke test. For example, if upstream has all its tests in a single +source file and the way it is built doesn't look too complicated, then you can +just use that source file in place of the smoke test. + +\N|If upstream has no tests, then the smoke test will have to stay. A library +can only be published if it has at least one test. + +It is also recommended to have the smoke test if upstream tests are in a +separate package. See +\l{https://github.com/build2/HOWTO/blob/master/entries/handle-tests-with-extra-dependencies.md +How do I handle tests that have extra dependencies?} for background and +details.| + +\N|If instead of a library you are packaging an executable, you can skip +directly to \l{#core-test-smoke-exe Make smoke test: executables}.| + +To recap, the \c{bdep-new}-generated \c{tests/} subdirectory looks like this +(continuing with our \c{libfoo} example): + +\ +libfoo/ +├── ... +└── tests/ + ├── build/ + │ ├── bootstrap.build + │ └── root.build + ├── basics/ + │ ├── driver.cpp + │ └── buildfile + └── buildfile +\ + +The \c{tests/} subdirectory is a build system subproject, meaning that it can +be built independently, for example, to test the installed version of the +library (see \l{b#intro-operations-test Testing} for background). In +particular, this means it has the \c{build/} subdirectory with project-wide +build system files, the same as the library. The \c{basics/} subdirectory +contains the generated test, which is what we will be turning into a smoke +test. The subproject root \c{buildfile} rarely needs changing. + + +\h2#core-test-smoke-build-wide|Adjust project-wide build system files in \c{tests/build/}| + +Review and adjust the generated \c{bootstrap.build} and \c{root.build} (there +will be no \c{export.build}) similar to the \l{#core-adjust-build-wide Adjust +project-wide build system files in \c{build/}} step. + +Here the only change you would normally make is in \c{root.build} and which is +to drop the assignment of extensions for target types that are not used in +tests. + + +\h2#core-test-smoke-adjust|Convert generated test to library smoke test| + +The \c{basics/} subdirectory contains the \c{driver.cpp} source file that +implements the test and \c{buildfile} that builds it. You can rename both the +test subdirectory (\c{basics/}) and the source file \c{driver.cpp}, for +example, if you are going with the upstream tests directly. You can also add +more tests by simply copying \c{basics/}. + +The purpose of a smoke test is to make sure the library's public headers can +be included (including in the installed case, no pun intended), it can be +linked, and its basic functionality works. + +To achieve this, we modify \c{driver.cpp} to include the library's main +headers and call a few functions. For example, if the library has the +initialize/deinitialize type of functions, those are good candidates to +call. If the library is not header-only, make sure that the smoke test calls +at least one non-inline/template function to test symbol exporting. + +\N|Make sure that your test includes the library's public headers the same way +as would be done by the library consumers.| + +Continuing with our \c{libfoo} example, this is what its smoke test might look +like: + +\ +#include <foo/core.hpp> +#include <foo/util.hpp> + +#undef NDEBUG +#include <cassert> + +int main () +{ + foo::context* c (foo::init (0 /* flags */)); + assert (c != nullptr); + foo::deinit (c); +} +\ + +\N|The C/C++ \c{assert()} macro is often adequate for simple tests and does +not require extra dependencies. But see +\l{https://github.com/build2/HOWTO/blob/master/entries/use-assert-in-tests.md +How do I correctly use C/C++ assert() in tests?}| + +The test \c{buildfile} is pretty simple: + +\ +import libs = libfoo%lib{foo} + +exe{driver}: {hxx ixx txx cxx}{**} $libs testscript{**} +\ + +If you have adjusted the library target name (\c{lib{foo\}}) in the source +subdirectory \c{buildfile}, then you will need to make the corresponding +change in the \c{import} directive here. You may also want to tidy it up by +removing unused prerequisite types. For example: + +\ +import libs = libfoo%lib{foo} + +exe{driver}: {hxx cxx}{**} $libs +\ + + +\h2#core-test-smoke-exe|Make smoke test: executables| + +If instead of a library we are packaging an executable, then instead of the +\c{tests/} subproject we get the \c{testscript} file in the source +subdirectory (see \l{#core-adjust-build-src-source-exe Adjust source +\c{buildfile}: executables} for a refresher). This file can be used to write +one or more tests that exercise our executable (see \l{b#intro-operations-test +Testing} for background). + +How exactly to test any given executable depends on its functionality. For +instance, for a compression utility we could write a roundtrip test that first +compresses some input, then decompresses it, and finally compares the result +to the original. For example (taken from the +\l{https://github.com/build2-packaging/zstd \c{zstd}} package repository): + +\ +: roundtrip +: +echo 'test content' | $* -zc | $* -dc >'test content' +\ + +On the other hand, for an executable that is a source code generator, proper +testing would involve a separate tests package that has a build-time +dependency on the executable and that exercises the generated code (see +\l{https://github.com/build2/HOWTO/blob/master/entries/handle-tests-with-extra-dependencies.md +How do I handle tests that have extra dependencies?} for background and +details). \N{See the \l{https://github.com/build2-packaging/thrift/tree/0.17 +\c{thrift}} package repository for an example of this setup.} + +If the executable provides a way to query its version, one test that you +should always be able to write, and which can serve as a last resort smoke +test, is the version check. For example: + +\ +: version +: +$* --version >>~\"/EOO/\" +/.*$(version.major)\.$(version.minor)\.$(version.patch).*/ +EOO +\ + +See also +\l{https://github.com/build2/HOWTO/blob/master/entries/sanitize-test-execution.md +How do I sanitize the execution of my tests?} + + +\h2#core-test-smoke-locally|Test locally| + +With the smoke test ready, we can finally do some end-to-end testing of our +library build. We will start with doing some local testing to catch basic +mistakes and then do the full CI to detect any platform/compiler-specific +issues. + +First let's run the test in the default build configuration by invoking the +build system directly (see \l{intro#guide Getting Started Guide} for +background on default configurations): + +\ +$ cd libfoo/tests/ # Change to the tests/ subproject. +$ b test +\ + +If there are any issues (compile/link errors, test failures), try to address +them and re-run the test. + +Once the library builds in the default configuration and the result passes the +tests, you can do the same for all the build configurations, in case you have +\l{#core-fill-init initialized} your library in several: + +\ +$ bdep test -a +\ + +\h2#core-test-smoke-locally-install|Test locally: installation| + +Once the development build works, let's also test the installed version of the +library. In particular, this makes sure that the public headers are installed +in a way that is compatible with how they are included by our test (and would +be included by the library consumers). To test this we first install the +library into a temporary directory: + +\ +$ cd libfoo/ # Change to the package root. +$ rm -rf /tmp/install +$ b install config.install.root=/tmp/install +\ + +Next we build just the \c{tests/} subproject out of source and arranging for +it to find the installed library (see \l{b#intro-dirs-scopes Output +Directories and Scopes} for background on the out of source build syntax): + +\ +$ cd libfoo/ # Change to the package root. +$ b test: tests/@/tmp/libfoo-tests-out/ \ + config.cc.loptions=-L/tmp/install/lib \ + config.bin.rpath=/tmp/install/lib +\ + +\N|The equivalent MSVC command line would be: + +\ +> b install config.install.root=c:\tmp\install + +> set \"PATH=c:\tmp\install\bin;%PATH%\" +> b test: tests\@c:\tmp\libfoo-tests-out\^ + config.cc.loptions=/LIBPATH:c:\tmp\install\lib +\ + +| + +It is a good idea to look over the installed files manually and make sure +there is nothing unexpected, for example, missing or extraneous files. + +Once done testing the installed case, let's clean things up: + +\ +$ rm -r /tmp/install /tmp/libfoo-tests-out +\ + +\h2#core-test-smoke-locally-dist|Test locally: distribution| + +Another special case worth testing is the preparation of the source +distribution (see \l{b#intro-operations-dist Distributing} for +background). This, in particular, is how your package will be turned into the +source archive for publishing to \l{https://cppget.org cppget.org}. Here we +are primarily looking for missing files. As a bonus, this will also allow us +to test the in source build. First we distribute our package to a temporary +directory (again using the default configuration and the build system +directly): + +\ +$ cd libfoo/ # Change to the package root. +$ b dist config.dist.root=/tmp/dist config.dist.uncommitted=true +\ + +The result will be in the \c{/tmp/dist/libfoo-<version>/} directory which +should resemble our \c{libfoo/} package but without files like \c{.gitignore}. +Next we build and test the distribution in source: + +\ +$ cd /tmp/dist/libfoo-<version>/ +$ b configure config.cxx=g++ +$ b update +$ b test +\ + +\N|If your package has dependencies that you import in your \c{buildfile}, +then the above \c{configure} operation will most likely fail because such +dependencies cannot be found (it may succeed if they are available as +system-installed). The error message will suggest specifying the location of +each dependency with the \c{config.import.*} variable. You can fix this by +setting each such \c{config.import.*} to the location of the default build +configuration (created in the \l{#core-fill-init Initialize package in build +configurations} step) which should contain all the necessary +dependencies. Simply re-run the \c{configure} operation until you have +discovered and specified all the necessary \c{config.import.*} variables, for +example: + +\ +$ b configure config.cxx=g++ \ + config.import.libz=.../foo-gcc \ + config.import.libasio=.../foo-gcc \ + config.import.libsqlite3=.../foo-gcc +\ + +| + +It is a good idea to look over the distributed files manually and make sure +there is nothing missing or extraneous. + +Once done testing the distribution, let's clean things up: + +\ +$ rm -r /tmp/dist +\ + + +\h2#core-test-smoke-ci|Commit and test with CI| + +With local testing complete, let's commit our changes and submit a remote CI +job to test our library on all the major platforms and with all the major +compilers: + +\ +$ cd foo/ # Change to the package repository root. +$ git add . +$ git status +$ git commit -m \"Add smoke test\" +$ git push -u + +$ bdep ci +\ + +The result of the \l{bdep-ci(1)} command is a link where you can see the +status of the builds. + +\N|Make sure to wait until there are no more unbuilt configurations (that is, +the number of entries with the \c{<unbuilt>} or \c{building} result is +\c{0}).| + +If any builds fail, view the logs to determine the cause, try to fix it, +commit your fix, and CI again. + +\N|It is possible that upstream does not support some platforms or compilers. +For example, it's common for smaller projects not to bother with supporting +\"secondary\" compilers, such as MinGW GCC on Windows or Homebrew GCC on Mac +OS. + +If upstream expressly does not support some platform or compiler, it's probably +not worth spending time and energy trying to support it in the package. Most +likely it will require changes to upstream source code and that is best done +upstream rather than in the package (see \l{#dont-fix-upstream Don't try to +fix upstream issues in the package} for background). In this case you would +want to exclude these platforms/compilers from the CI builds using the +\l{bpkg#manifest-package-builds \c{builds} package \c{manifest} value}. + +The other common cause of a failed build is a newer version of a compiler or +platform that breaks upstream. In this case there are three options: Ideally +you would want to fix this in upstream and have a new version released. +Failing that, you may want to patch the upstream code to fix the issues, +especially if this is one of the major platforms and/or primary compilers (see +\l{#howto-patch-upstream-source How do I patch upstream source code?} for +details). Finally, you can just leave the build failing with the expectation +that it will be fixed in the next upstream version. Note that in this case you +should not exclude the failing build from CI.| + + +\h#core-test-upstream|Replace smoke test with upstream tests| + +With the smoke test working we can now proceed with replacing it with the +upstream tests. + + +\h2#core-test-upstream-understand|Understand how upstream tests work| + +While there are some commonalities in how C/C++ libraries are typically built, +when it comes to tests there is unfortunately little common ground in how they +are arranged, built, and executed. As a result, the first step in dealing with +upstream tests is to study the existing build system and try to understand how +they work. + +\N|If upstream tests prove incomprehensible (which is unfortunately not +uncommon) and the only options you see are to go with just the smoke test or +to give up, then go with just the smoke test. In this case it's a good idea to +create an issue in the package repository mentioning that upstream tests are +still a TODO.| + +\N|If instead of a library you are packaging an executable, then whether the +below steps will apply depends on the functionality of the executable. + +In particular, testing source code generators would normally involve +exercising the generated code, in which case the following will largely apply, +though in this case the tests would need to be placed into a separate tests +package that has a build-time dependency on the executable (see +\l{https://github.com/build2/HOWTO/blob/master/entries/handle-tests-with-extra-dependencies.md +How do I handle tests that have extra dependencies?} for background and +details). In fact, if a source code generator is accompanied by a runtime +library, then the tests will normally exercise them together (though a runtime +library might also have its own tests). See the +\l{https://github.com/build2-packaging/thrift/tree/0.17 \c{thrift}} package +repository for an example of this setup.| + +To get you started with analyzing the upstream tests, below are some of the +questions you would likely need answered before you can proceed with the +conversion: + +\ul| + +\li|\b{Are upstream tests unit tests or integration tests?} + +While the distinction is often fuzzy, for our purposes the key differentiator +between unit and integration tests is which API they use: integration tests +only use the library's public API while unit tests need access to the +implementation details. + +Normally (but not always), unit tests will reside next to the library source +code since they need access to more than just the public headers and the +library binary (private headers, individual object files, utility libraries, +etc). While integration tests are normally (but again not always) placed into +a separate subdirectory, usually called \c{tests} or \c{test}. + +If the library has unit tests, then refer to \l{b#intro-unit-test Implementing +Unit Testing} for background on how to handle them in \c{build2}. + +If the library has integration tests, then use them to replace (or complement) +the smoke test. + +If the library has unit tests but no integration tests, then it is recommended +to keep the smoke test since that's the only way the library will be tested +via its public API.| + + +\li|\b{Do upstream tests use an external testing framework?} + +Oftentimes a C++ library will use an external testing framework to implement +tests. Popular choices include \l{https://cppget.org/catch2 \c{catch2}}, +\l{https://cppget.org/gtest \c{gtest}}, \l{https://cppget.org/doctest +\c{doctest}}, and \l{https://cppget.org/libboost-test \c{libboost-test}}. + +If a library uses such an external testing framework, then it is recommended +to factor tests into a separate package in order to avoid making the library +package depend on the testing framework (which is only required during +testing). See +\l{https://github.com/build2/HOWTO/blob/master/entries/handle-tests-with-extra-dependencies.md +How do I handle tests that have extra dependencies?} for details. + +\N|Sometimes you will find that upstream bundles the source code of the +testing framework with their tests. This is especially common with +\c{catch2}. If that's the case, it is strongly recommended that you +\"unbundle\" it by making it a proper external dependency. See \l{#dont-bundle +Don't bundle dependencies} for background.|| + + +\li|\b{Are upstream tests in a single or multiple executables?} + +It's not unusual for libraries to have a single test executable that runs all +the test cases. This is especially common if a C++ testing framework is used. +In this case it is natural to replace the contents of the smoke test with the +upstream source code, potentially renaming the test subdirectory (\c{basics/}) +to better match upstream naming. + +If upstream has multiple test executables, then they could all be in a single +test subdirectory (potentially reusing some common bits) or spread over +multiple subdirectories. In both cases it's a good idea to follow the upstream +structure unless you have good reasons to deviate. In the former case (all +executables in the same subdirectory), you can re-purpose the smoke test +subdirectory. In the latter case (each executable in a separate subdirectory) +you can make copies of the smoke test subdirectory.| + + +\li|\b{Do upstream tests use an internal utility library?} + +If there are multiple test executables and they need to share some common +functionality, then it's not unusual for upstream to place such functionality +into a static library and then link it to each test executable. In \c{build2} +such an internal library is best represented with a utility library (see +\l{b#intro-unit-test Implementing Unit Testing} for details). See the +following section for an example.| + + +\li|\b{Are upstream tests well behaved?} + +Unfortunately, it's not uncommon for upstream tests not to behave well, such +as to write diagnostics to \c{stdout} instead of \c{stderr}, create temporary +files without cleaning them up, or assume presence of input files in the +current working directory. For details on how to deal with such situations see +\l{https://github.com/build2/HOWTO/blob/master/entries/sanitize-test-execution.md +How do I sanitize the execution of my tests?}|| + + +\h2#core-test-upstream-convert|Convert smoke test to upstream tests| + +Once you have a good grasp of how upstream tests work, convert or replace the +smoke test with the upstream tests. If upstream has multiple test executables, +you may want to deal with one test at a time, making sure that it passes +before moving to the next one. + +It's normally a good idea to use the smoke test \c{buildfile} as a starting +point for upstream tests. To recap, the smoke test \c{buildfile} for our +\c{libfoo} example ended up looking like this: + +\ +import libs = libfoo%lib{foo} + +exe{driver}: {hxx cxx}{**} $libs +\ + +At a minimum you will most likely need to change the name of the executable to +match upstream. If you need to build multiple executables in the same +directory, then it's probably best to get rid of the name pattern for the +source files and specify the prerequisite names explicitly, for example: + +\ +import libs = libfoo%lib{foo} + +./: exe{test1}: cxx{test1} $libs +./: exe{test2}: cxx{test2} $libs +\ + +If you have a large number of such test executables, then a \l{b#intro-for +\c{for}-loop} might be a more scalable option: + +\ +import libs = libfoo%lib{foo} + +for src: cxx{test*} + ./: exe{$name($src)}: $src $libs +\ + +If the upstream tests have some common functionality that is used by all the +test executables, then it is best placed into a utility library. For example: + +\ +import libs = libfoo%lib{foo} + +./: exe{test1}: cxx{test1} libue{common} +./: exe{test2}: cxx{test2} libue{common} + +libue{common}: {hxx cxx}{common} $libs +\ + + +\h2#core-test-upstream-locally|Test locally| + +With the upstream tests ready, we re-do the same end-to-end testing as we did +with the smoke test: + +\l{#core-test-smoke-locally Test locally}\n +\l{#core-test-smoke-locally-install Test locally: installation}\n +\l{#core-test-smoke-locally-dist Test locally: distribution}\n + + +\h2#core-test-upstream-ci|Commit and test with CI| + +With local testing complete, we commit our changes and submit a remote CI +job. This step is similar to what \l{#core-test-smoke-ci we did for the smoke +test} but this time we are using the upstream tests: + +\ +$ cd foo/ # Change to the package repository root. +$ git add . +$ git status +$ git commit -m \"Add upstream tests\" +$ git push + +$ bdep ci +\ + + +\h#core-examples-benchmarks|Add upstream examples, benchmarks, if any| + +If the upstream project provides examples and/or benchmarks and you wish to +add them to the \c{build2} build (which is not strictly necessary for the +\c{build2} package to be usable), then now is a good time to do that. + +As was mentioned in \l{#core-package-review Review and test auto-generated +\c{buildfile} templates}, the recommended approach is to copy the \c{tests/} +subproject (potentially from the commit history before the smoke test was +replaced with the upstream tests) and use that as a starting point for +examples and/or benchmarks. Do not forget to add the corresponding entry in +the root \c{buildfile}. + +Once that is done, follow the same steps as in \l{#core-test-upstream Replace +smoke test with upstream tests} to add upstream examples/benchmarks and test +the result. + + +\h#core-root|Adjust root files (\c{buildfile}, \c{manifest}, etc)| + +The last few files that we need to review and potentially adjust are +the root \c{buildfile}, package \c{manifest}, and \c{PACKAGE-README.md}. + + +\h2#core-root-buildfile|Adjust root \c{buildfile}| + +The main function of the root \c{buildfile} is to pull in all the +subdirectories that need building plus list targets that are usually found in +the root directory of a project, typically \c{README.md}, \c{LICENSE}, +etc. This is what the generated root \c{buildfile} looks like for our +\c{libfoo} project assuming we have symlinked \c{README.md}, \c{LICENSE}, and +\c{NEWS} from upstream in the \l{#core-package-create Create final package} +step: + +\ +./: {*/ -build/} \ + doc{README.md PACKAGE-README.md NEWS} \ + legal{LICENSE} manifest + +# Don't install tests. +# +tests/: install = false +\ + +If the upstream project provides any other documentation (detailed change +logs, contributing guidelines, etc) or legal files (alternative licenses, list +of authors, code of conduct, etc), then you may want to symlink and list them +as the \c{doc{\}} and \c{legal{\}} prerequisites, respectively. + +\N|If you are packaging an executable and it provides a man page, then it can +also be listed in the root \c{buildfile}. For example, if the man page file is +called \c{foo.1}: + +\ +./: ... man1{foo} +\ + +| + +\N|One file you don't need to list is \c{INSTALL} (or equivalent) which +normally contains the installation instructions for the upstream build +system. In the \c{build2} package of a third-party project the +\c{PACKAGE-README.md} file serves this purpose (see +\l{#core-root-package-readme Adjust \c{PACKAGE-README.md}} for details).| + + +\h2#core-root-buildfile-doc|Adjust root \c{buildfile}: other subdirectories| + +If the upstream project has other subdirectories that makes sense to include +into the \c{build2} package, then now is a good time to take care of that. +The most common such case will be extra documentation (besides the root +\c{README}), typically in a subdirectory called \c{doc/}, \c{docs/}, or +\c{documentation/}. + +The standard procedure for handling such subdirectories will be to symlink the +relevant files (or the entire subdirectory) and then list the files as +prerequisites. For this last step, there are two options: we can list the +files directly in the root \c{buildfile} or we can create a separate +\c{buildfile} in the subdirectory. + +\N|If symlinking entire subdirectories, don't forget to also list them in +\c{.gitattributes} if you want your package to be usable from the \c{git} +repository directly on Windows. See +\l{https://build2.org/article/symlinks.xhtml#windows Symlinks and Windows} for +details.| + +Let's examine each approach using our \c{libfoo} as an example. We will +assume that the upstream project contains the \c{docs/} subdirectory with +additional \c{*.md} files that document the library's API. It would make sense +to include them into the \c{build2} package. + +Listing the subdirectory files directly in the root \c{buildfile} works best +for simple cases, where you have a bunch of static files that don't require +any special provisions, such as customizations to their installation +locations. In this case we can symlink the entire \c{docs/} subdirectory: + +\ +$ cd libfoo/ # Change to the package root. +$ ln -s ../upstream/docs ./ +\ + +The adjustments to the root \c{buildfile} are pretty straightforward: we +exclude the \c{docs/} subdirectory (since it has no \c{buildfile}) and list +the \c{*.md} files as prerequisites using the \c{doc{\}} target type (which, +in particular, makes sure they are installed into the appropriate location): + +\ +./: {*/ -build/ -docs/} \ + doc{README.md PACKAGE-README.md NEWS} \ + docs/doc{*.md} \ + legal{LICENSE} manifest +\ + +The alternative approach (create a separate \c{buildfile}) is a good choice if +things are more complicated than that. Let's say we need to adjust the +installation location of the files in \c{docs/} because there is another +\c{README.md} inside and that would conflict with the root one when installed +into the same location. This time we cannot symlink the top-level \c{docs/} +subdirectory (because we need to place a \c{buildfile} there). The two options +here are to either symlink the individual files or introduce another +subdirectory level inside \c{docs/} (which is the same approach as discussed +in \l{#dont-main-target-root-buildfile Don't build your main targets in the +root \c{buildfile}}). Let's illustrate both sub-cases. + +Symlinking individual files works best when you don't expect the set of files +to change often. For example, if \c{docs/} contains a man page and its HTML +rendering, then it's unlikely this set will change. On the other hand, if +\c{docs/} contains a manual split into an \c{.md} file per chapter, then there +is a good chance this set of files will fluctuate between releases. + +Continuing with our \c{libfoo} example, this is how we symlink the individual +\c{*.md} files in \c{docs/}: + +\ +$ cd libfoo/ # Change to the package root. +$ mkdir docs +$ cd docs/ +$ ln -s ../../upstream/docs/*.md ./ +\ + +Then write a new \c{buildfile} in \c{docs/}: + +\ +./: doc{*.md} + +# Install the documentation in docs/ into the manual/ subdirectory of, +# say, /usr/share/doc/libfoo/ since we cannot install both its and root +# README.md into the same location. +# +doc{*.md}: install = doc/manual/ +\ + +Note that we don't need to make any changes to the root \c{buildfile} since +this subdirectory will automatically get picked up by the \c{{*/\ -build/\}} +name pattern that we have there. + +Let's now look at the alternative arrangement with another subdirectory level +inside \c{docs/}. Here we achieve the same result but in a slightly different +way. Specifically, we call the subdirectory \c{manual/} and install recreating +subdirectories (see \l{b#intro-operations-install Installing} for background): + +\ +$ cd libfoo/ # Change to the package root. +$ mkdir -p docs/manual +$ cd docs/manual/ +$ ln -s ../../../upstream/docs/*.md ./ +\ + +And the corresponding \c{buildfile} in \c{docs/}: + +\ +./: doc{**.md} + +# Install the documentation in docs/ into, say, /usr/share/doc/libfoo/ +# recreating subdirectories. +# +doc{*}: +{ + install = doc/ + install.subdirs = true +} +\ + +\N|Yet another option would be to open a scope for the \c{docs/} subdirectory +directly in the root \c{buildfile} (see \l{b#intro-dirs-scopes Output +Directories and Scopes} for background). For example: + +\ +$ cd libfoo/ # Change to the package root. +$ ln -s ../upstream/docs ./ +\ + +And then add the following to the root \c{buildfile}: + +\ +docs/ +{ + ./: doc{*.md} + + # Install the documentation in docs/ into the manual/ subdirectory + # of, say, /usr/share/doc/libfoo/ since we cannot install both its + # and root README.md into the same location. + # + doc{*.md}: install = doc/manual/ +} +\ + +However, this approach should be used sparingly since it can quickly make the +root \c{buildfile} hard to comprehend. Note also that it cannot be used for +main targets since an export stub requires a \c{buildfile} to load (see +\l{#dont-main-target-root-buildfile Don't build your main targets in the +root \c{buildfile}} for details).| + + +\h2#core-root-buildfile-commit|Adjust root \c{buildfile}: commit and test| + +Once all the adjustments to the root \c{buildfile} are made, it makes sense to +test it locally (this time from the root of the package), commit our changes, +and test with CI: + +\ +$ cd libfoo/ # Change to the package root. +$ b test +$ bdep test -a +\ + +If you had to add any extra files to the root \c{buildfile} (or add +\c{buildfiles} in extra subdirectories), then it also makes sense to test the +installation (\l{#core-test-smoke-locally-install Test locally: installation}) +and the preparation of the source distribution +(\l{#core-test-smoke-locally-dist Test locally: distribution}) to make sure +the extra files end up in the right places. + +Then commit our changes and CI: + +\ +$ cd foo/ # Change to the package repository root. +$ git add . +$ git status +$ git commit -m \"Adjust root buildfile\" +$ git push + +$ bdep ci +\ + + +\h2#core-root-manifest|Adjust \c{manifest}| + +The next file we need to look over is the package's \c{manifest}. Here is what +it will look like, using our \c{libfoo} as an example: + +\ +: 1 +name: libfoo +version: 2.1.0-a.0.z +language: c++ +project: foo +summary: C++ library implementing secure Foo protocol +license: MIT ; MIT License. +description-file: README.md +package-description-file: PACKAGE-README.md +changes-file: NEWS +url: https://example.org/upstream +email: upstream@example.org +package-url: https://github.com/build2-packaging/foo +package-email: packaging@build2.org ; Mailing list. +depends: * build2 >= 0.16.0 +depends: * bpkg >= 0.16.0 +\ + +You can find the description of these and other package \c{manifest} values in +\l{bpkg#manifest-package Package Manifest} (the manifest format is described +in \l{bpkg#manifest-format Manifest Format}). + +In the above listing the values that we likely need to adjust are \c{summary} +and \c{license}, unless correctly auto-detected by \c{bdep-new} in the +\l{#core-package-create Create final package} step. See +\l{#core-root-manifest-summary Adjust \c{manifest}: \c{summary}} and +\l{#core-root-manifest-license Adjust \c{manifest}: \c{license}} below +for guidelines on changing these values. + +\N|It is not uncommon for projects to be licensed under multiple licenses. +Note, however, that \c{bdep-new} will only detect one license and you will +need to specify any additional licenses manually.| + +We will also need to change \c{url} and \c{email} with the upstream project's +homepage URL and e-mail, respectively. If upstream doesn't have a dedicated +website for the project, then use its repository URL on GitHub or equivalent. +For e-mail you would normally use a mailing list address. If upstream doesn't +have any e-mail contacts, then you can drop this value from the +\c{manifest}. The \c{package-url} and \c{package-email} values normally do not +need to be changed. + +\N|\l{https://lists.build2.org packaging@build2.org} is a mailing list for +discussions related to the packaging efforts of third-party projects.| + +Note also that while you may be tempted to adjust the \c{version} value, +resist this temptation since this will be done automatically by +\l{bdep-release(1)} later. + +You may also want to add the following value in certain cases: + +\dl| + +\li|\l{bpkg#manifest-package-changes \cb{changes-file}} + +If you have added any extra news of changelog files to the root \c{buildfile} +(see \l{#core-root-buildfile Adjust root buildfile}), then it may also make +sense to list them in the \c{manifest}. For example: + +\ +changes-file: ChangeLog.txt +\ + +| + +\li|\l{bpkg#manifest-package-topics \cb{topics}} + +Package topics. For example: + +\ +topics: network protocol, network security +\ + +\N|If the upstream project is hosted on GitHub or similar, then you can +usually copy the topics from the upstream repository description.|| + +\li|\l{bpkg#manifest-package-doc-url \cb{doc-url}}\n + \l{bpkg#manifest-package-src-url \cb{src-url}} + +Documentation and source code URLs. For example: + +\ +doc-url: https://example.org/foo/doc/ +src-url: https://github.com/.../foo +\ + +|| + + +\h2#core-root-manifest-summary|Adjust \c{manifest}: \c{summary}| + +For \c{summary} use a brief description of the functionality provided by the +library or executable. Less than 70 characters is a good target to aim +for. Don't capitalize subsequent words unless proper nouns and omit the +trailing dot. For example: + +\ +summary: Vim xxd hexdump utility +\ + +Omit weasel words such as \"modern\", \"simple\", \"fast\", \"small\", etc., +since they don't convey anything specific. Omit \"header-only\" or +\"single-header\" for C/C++ libraries since, at least in the context of +\c{build2}, it does not imply any benefit. + +If upstream does not offer a sensible summary, the following template is +recommended for libraries: + +\ +summary: <functionality> C library +summary: <functionality> C++ library +\ + +For example: + +\ +summary: Event notification C library +summary: Validating XML parsing and serialization C++ library +\ + +If the project consists of multiple packages, it may be tempting to name each +package in terms of the overall project name, for example: + +\ +name: libigl-core +summary: libigl core module +\ + +This doesn't give the user any clue about what functionality is provided +unless they find out what \c{libigl} is about. Better: + +\ +summary: Geometry processing C++ library, core module +\ + +If you follow the above pattern, then to produce a summary for external tests +or examples packages simply add \"tests\" or \"examples\" at the end, +for example: + +\ +summary: Event notification C library tests +summary: Geometry processing C++ library, core module examples +\ + +\h2#core-root-manifest-license|Adjust \c{manifest}: \c{license}| + +For \c{license}, use the \l{https://spdx.org/licenses/ SPDX license ID} if at +all possible. If multiple licenses are involved, use the SPDX License +expression. See the +\l{https://build2.org/bpkg/doc/build2-package-manager-manual.xhtml#manifest-package-license +\c{license} manifest value} documentation for details, including the list of +the SPDX IDs for the commonly used licenses. + +\h2#core-root-manifest-commit|Adjust \c{manifest}: commit and test| + +Once all the adjustments to the \c{manifest} are made, it makes sense to test +it locally, commit our changes, and test with CI: + +\ +$ cd libfoo/ # Change to the package root. +$ b test +$ bdep test -a +\ + +Then commit our changes and CI: + +\ +$ cd foo/ # Change to the package repository root. +$ git add . +$ git status +$ git commit -m \"Adjust manifest\" +$ git push + +$ bdep ci +\ + +\h2#core-root-package-readme|Adjust \c{PACKAGE-README.md}| + +The last package file we need to adjust is \c{PACKAGE-README.md} which +describes how to use the package from a \c{build2}-based project. The template +generated by \c{bdep-new} establishes the recommended structure and includes a +number of placeholders enclosed in \c{<\ >}, such as \c{<UPSTREAM-NAME>} and +\c{<SUMMARY-OF-FUNCTIONALITY>}, that need to be replaced with the +package-specific content. While all the placeholders should be +self-explanatory, below are a couple of guidelines. + +For \c{<SUMMARY-OF-FUNCTIONALITY>} it's best to copy a paragraph or two from +the upstream documentation, usually from \c{README.md} or the project's web +page. + +\N|If the \c{bdep new} command was able to extract the summary from upstream +\c{README}, then the summary in the heading (first line) will contain that +information. Otherwise, you would need to adjust it manually, similar to +\c{manifest} above. In this case use the \c{summary} value form the +\c{manifest}, perhaps slightly shortened.| + +If the package contains a single importable target, as is typical with +libraries, then it makes sense to drop the \"Importable targets\" section +since it won't add anything that hasn't already been said in the \"Usage\" +section. + +Similarly, if the package has no configuration variables, then it makes sense +to drop the \"Configuration variables\" section. + +For inspiration, see + +\l{https://github.com/build2-packaging/zstd/blob/master/libzstd/PACKAGE-README.md +\c{PACKAGE-README.md}} in \l{https://github.com/build2-packaging/zstd +\c{zstd}} and +\l{https://github.com/build2-packaging/libevent/blob/main/PACKAGE-README.md +\c{PACKAGE-README.md}} in \l{https://github.com/build2-packaging/libevent +\c{libevent}} (libraries) as well as +\l{https://github.com/build2-packaging/zstd/blob/master/zstd/PACKAGE-README.md +\c{PACKAGE-README.md}} in \l{https://github.com/build2-packaging/zstd +\c{zstd}} and +\l{https://github.com/build2-packaging/xxd/blob/master/xxd/README.md +\c{README.md}} in \l{https://github.com/build2-packaging/xxd \c{xxd}} +(executables). + +\N|If upstream does not provide a \c{README} file, then it makes sense to +rename \c{PACKAGE-README.md} to just \c{README.md} in the \c{build2} package, +as was done in the \c{xxd} package mentioned above.| + +Once \c{PACKAGE-README.md} is ready, commit and push the changes. You may also +want to view the result on GitHub to make sure everything is rendered +correctly. + +\ +$ cd foo/ # Change to the package repository root. +$ git add . +$ git status +$ git commit -m \"Adjust PACKAGE-README.md\" +$ git push +\ + + +\h#core-repo-readme|Adjust package repository \c{README.md}| + +With all the package files taken care of, the last file we need to adjust is +\c{README.md} in the root of our package repository (it was created in the +\l{#core-repo-init Initialize package repository with \c{bdep new}} step). + +\N|If you need to add additional packages and are doing this one package at a +time (for example, first library then executable in the \"library and +executable\" project), then this is the point where you would want to restart +from \l{#core-package Create package and generate \c{buildfile} templates} for +another iteration. Only once all the packages are added does it make sense +to continue with updating this \c{README.md}.| + +The primary purpose of the package repository \c{README.md} is to provide +setup instructions as well as any other relevant information for the +development of the packages as opposed to their consumption. However, it's also +a good idea to give a brief summary of what this repository is about and to +point users interested in consumption to the \c{PACKAGE-README.md} files. + +The template generated by \c{bdep new} establishes the recommended structure +to achieve these objectives. It includes a number of placeholders enclosed +in \c{<\ >}, such as \c{<UPSTREAM-URL>} and \c{<SUMMARY-OF-FUNCTIONALITY>}, +that need to be replaced with the repository-specific content. While all the +placeholders should be self-explanatory, below are a couple of guidelines. + +If there is a single package, then \c{<SUMMARY>} in the heading can be the +same as in \c{PACKAGE-README.md}. If there are multiple packages, then use +an overall summary of the upstream project. + +For \c{<SUMMARY-OF-FUNCTIONALITY>} it's best to copy a paragraph or two from +the upstream documentation, usually from \c{README.md} or the project's web +page. Again, for a single package, this can be copied directly from +\c{PACKAGE-README.md}. + +If there are multiple packages in the repository, then it's recommended to +replace a single link to \c{PACKAGE-README.md} with a list of links (this +also shows the available packages). For example: + +\ +... If you want to use `foo` in your `build2`-based project, then +instead see the accompanying `PACKAGE-README.md` files: + +* [`libfoo/PACKAGE-README.md`](libfoo/PACKAGE-README.md) +* [`foo/PACKAGE-README.md`](foo/PACKAGE-README.md) +\ + +The remainder of the generated \c{README.md} file are the standard \c{bdep} +initialization instructions. Adjust them if your package repository requires +anything special (for example, a host configuration). This is also the place +to mention anything unusual, such as that upstream does not use semver (and +thus only a subset of \c{bdep} functionality is usable). + +For inspiration, see +\l{https://github.com/build2-packaging/zstd/blob/master/README.md +\c{README.md}} in the \l{https://github.com/build2-packaging/zstd \c{zstd}} +package repository. + +Once the repository \c{README.md} is ready, commit and push the changes. You +may also want to view the result on GitHub to make sure everything is rendered +correctly. + +\ +$ cd foo/ # Change to the package repository root. +$ git add . +$ git status +$ git commit -m \"Adjust package repository README.md\" +$ git push +\ + + +\h#core-release-publish|Release and publish| + +Once all the adjustments are in and everything is tested, we can release the +final version of the package and then publish it to \l{https://cppget.org +cppget.org}. Both of these steps are automated with the corresponding \c{bdep} +commands. But before performing these steps we need to transfer the package +repository to \l{https://github.com/build2-packaging +github.com/build2-packaging}. + + +\h2#core-release-publish-transfer|Transfer package repository| + +If you have been doing your work in a repository in your personal workspace, +then now is the time to transfer it to the +\l{https://github.com/build2-packaging github.com/build2-packaging} +organization. + +\N|It is important to transfer the repository before publishing the first +version of the package since the repository is used as a proxy for package +name ownership (see \l{bdep-publish(1)} for details). If you publish the +package from your personal workspace and then transfer the repository, the +ownership information will have to be adjusted manually, which we would +prefer to avoid.| + +The first step is to become a member of this organization (unless you already +are). This will give you permissions to create new repositories, which is +required to perform a transfer (you will also have full read/write access to +the repository once transferred). To get an invite, +\l{https://build2.org/community.xhtml#help get in touch} not forgetting to +mention your GitHub user name. + +If your repository has any prefixes, such as \c{build2-}, or suffixes +such as \c{-package}, then the next step is to rename it to follow the +\l{#core-repo-name Use upstream repository name as package repository name} +guideline. Go to the repository's Settings on GitHub where you should see the +Rename button. + +Finally, to perform the transfer, go to the repository's Settings, Danger Zone +section, where you should see the Transfer button. Select \c{build2-packaging} +as the organization to transfer to, and complete the transfer. + +Once transferred, you will be considered the maintainer of this package going +forward. If other members of the \c{build2-packaging} organization wish to +participate in the package maintenance, the correct etiquette is to do this +via pull requests. However, if you lose interest in maintaining a package or +otherwise become unresponsive, we may allow a new maintainer to take over +this role. + +\N|In extraordinary circumstances the \c{build2-packaging} administrators may +make direct changes to the package, for example, to release a new revision in +order to address a critical issue. They will still try to coordinate the +changes with the maintainer but may not always be able to wait for a response +in time-sensitive cases.| + +\h2#core-release-publish-release|Release final version| + +As you may recall, our package currently has a pre-release snapshot version of +the upstream version (see \l{#core-package-adjust-version Adjust package +version}). Once all the changes are in, we can change to the final upstream +version, in a sense signaling that this package version is ready. + +\N|If you are working in a branch, then now is also the time to merge it into +\c{master} (or equivalent).| + +The recommended way to do this is with the \l{bdep-release(1)} command (see +\l{intro#guide-versioning-releasing Versioning and Release Management} for +background). Besides replacing the \c{version} value in the package +\c{manifest} file, it also commits this change, tags it with the +\c{v\i{X}.\i{Y}.\i{Z}} tag, and can be instructed to push the changes (or show +the \c{git} command to do so). This command also by default \"opens\" the next +development version, which is something that we normally want for our own +projects but not when we package a third-party one (since we cannot predict +which version upstream will release next). So we disable this functionality. +For example: + +\ +$ cd foo/ # Change to the package repository root. +$ bdep release --no-open --show-push +\ + +Then review the commit made by \c{bdep-release} and, if everything looks good, +push the changes by copying the command that it printed: + +\ +$ git diff HEAD~1 +$ git push ... +\ + +\N|If something is wrong and you need to undo this commit, don't forget to +also remove the tag. Note also that once you have pushed your changes, you +cannot undo the commit. Instead, you will need to make a revision. See +\l{#core-version-management Version management} for background and +details.| + + +\h2#core-release-publish-publish|Publish released version| + +Once the version is released we can publish the package to +\l{https://cppget.org cppget.org} with the \l{bdep-publish(1)} command (see +\l{intro#guide-versioning-releasing Versioning and Release Management} for +background): + +\ +$ cd foo/ # Change to the package repository root. +$ bdep publish +\ + +The \c{bdep-publish} command prepares the source distribution of your package, +uploads the resulting archive to the package repository, and prints a link to +the package submission in the queue. Open this link in the browser and check +that there are no surprises in the build results (they should match the +earlier CI results) or in the displayed package information +(\c{PACKAGE-README.md}, etc). + +\N|While there should normally be no discrepancies in the build results +compared to our earlier CI submissions, the way the packages are built on CI +and in the package repository are not exactly the same. Specifically, CI +builds them from \c{git} while the package repository \- from the submitted +package archives. If there are differences, it's almost always due to issues +in the source distribution preparation (see \l{#core-test-smoke-locally-dist +Test locally: distribution}).| + +If everything looks good, then you are done: the package submission will be +reviewed and, if there are no problems, moved to \l{https://cppget.org +cppget.org}. If there are problems, then an issue will be created in the +package repository with the review feedback. In this case you will need to +\l{#core-version-management-new-revision release and publish a version +revision} to address any problems. However, in both cases, you should first +read through the following \l{#core-version-management Package version +management} section to understand the recommended \"version lifecycle\" of a +third-party package. + +Also, if there is an issue for this package in +\l{https://github.com/build2-packaging/WISHLIST +github.com/build2-packaging/WISHLIST}, then you would want to add a comment +and close it once the package has been moved to \l{https://cppget.org +cppget.org}. + + +\h#core-version-management|Package version management| + +Once we have pushed the release commit, in order to preserve continuous +versioning (see \l{#core-package-adjust-version Adjust package version} for +background), no further changes should be made to the package without also +changing its version. + +\N|More precisely, you can make and commit changes without changing the +version provided they don't affect the package. For example, you may keep a +\c{TODO} file in the root of your repository which is not part of any +package. Updating such a file without changing the version is ok since the +package remains unchanged.| + +While in our own projects we can change the versions as we see fit, with +third-party projects the versions are dictated by upstream and as a result +we are limited to what we can use to fix issues in our packaging work +itself. It may be tempting (and perhaps even conceptually correct) to release +a patch version for our own fixes, however, we will be in trouble if later +upstream releases the same patch version but with a different set of changes +(plus the users of our package may wonder where did this version come +from). As a result, we should only change the major, minor, or patch +components of the package version in response to the corresponding upstream +releases. For fixes to the packaging work itself we should instead use version +revisions. + +\N|Because a revision replaces the existing version, we should try to limit +revision changes to bug fixes and preferably only in the package +\"infrastructure\" (\c{buildfiles}, \c{manifest}, etc). Fixes to upstream +source code should be limited to critical bugs and be preferably backported +from upstream. To put it another way, changes in a revision should have an +even more limited scope than a patch release.| + +Based on this, the recommended \"version lifecycle\" for a third-party +package is as follows: + +\ol| + +\li|After a release (the \l{#core-release-publish-release Release final +version} step above), for example, version \c{2.1.0}, the package enters a +\"revision phase\" where we can release revisions (\c{2.1.0+1}, \c{2.1.0+2}, +etc) to address any issues in the packaging work. See +\l{#core-version-management-new-revision New revision} for the detailed +procedure.| + +\li|When a new upstream version is released, for example version \c{2.2.0}, +and we wish to upgrade our package to this version, we switch to its +pre-release snapshot version (\c{2.2.0-a.0.z}) the same way as we did in the +\l{#core-package-adjust-version Adjust package version} step initially. See +\l{#core-version-management-new-version New version} for the detailed +procedure.| + +\li|Once we are done upgrading to the new upstream version, we release the +final version just like in the \l{#core-release-publish-release Release final +version} step initially. At this point the package enters another revision +phase.|| + +Note also that in the above example, once we have switched to \c{2.2.0-a.0.z}, +we cannot go back and release another revision or patch version for \c{2.1.0} +on the current branch. Instead, we will need to create a separate branch for +the \c{2.1.Z} release series and make a revision or patch version there. See +\l{#core-version-management-old-series New version/revision in old release +series} for the detailed procedure. + + +\h2#core-version-management-new-revision|New revision| + +As discussed in \l{#core-version-management Package version management}, we +release revisions to fix issues in the package \"infrastructure\" +(\c{buildfiles}, \c{manifest}, etc) as well as critical bugs in upstream +source code. + +\N|Releasing a new revision is also a good opportunity to review and fix any +accumulated issues that didn't warrant a revision on their own. See +\l{#core-version-management-new-version-issues New version: review/fix +accumulated issues} for background.| + +In the revision phase of the package version lifecycle (i.e., when the version +does not end with \c{-a.0.z}), every commit must be accompanied by the +revision increment to maintain continuous versioning. As a result, each +revision release commit necessarily also contains the changes in this +revision. Below is a typical workflow for releasing and publishing the +revision: + +\ +$ # make changes +$ # test locally +$ git add . +$ bdep release --revision --show-push +$ # review commit +$ git push ... +$ # test with CI +$ bdep publish +\ + +Customarily, the revision commit message has the \c{\"Release version +X.Y.Z+R\"} summary as generated by \c{bdep-release} followed by the +description of changes, organized in a list if there are several. For example: + +\ +Release version 2.1.0+1 + +- Don't compile port/strlcpy.c on Linux if GNU libc is 2.38 or newer + since it now provides the strl*() functions. + +- Switch to using -pthread instead of -D_REENTRANT/-lpthread. +\ + +\N|The fact that all the changes must be in a single commit is another reason +to avoid substantial changes in revisions.| + +Note also that you can make multiple commits while developing and testing the +changes for a revision in a separate branch. However, once they are ready for +a release, they need to be squashed into a single commit. The +\l{bdep-release(1)} command provides the \c{--amend} and \c{--squash} options +to automate this. For example, here is what a workflow with a separate branch +might look like: + +\ +$ git checkout -b wip-2.1.0+1 + +$ # make strl*() changes +$ # test locally +$ git commit -a -m \"Omit port/strlcpy.c if glibc 2.38 or newer\" +$ git push -u +$ # test with CI + +$ # make pthread changes +$ # test locally +$ git commit -a -m \"Switch to using -pthread\" +$ git push +$ # test with CI + +$ git checkout master +$ git merge --ff-only wip-2.1.0+1 +$ bdep release --revision --show-push --amend --squash 2 +$ # review commit +$ # test locally +$ git push ... +$ # test with CI +$ bdep publish +\ + + +\h2#core-version-management-new-version|New version| + +As discussed in \l{#core-version-management Package version management}, we +release new versions strictly in response to the corresponding upstream +releases. + +The amount or work required to upgrade a package to a new upstream version +depends on the extend of changes in the new version. + +On one extreme you may have a patch release which fixes a couple of bugs in +the upstream source code without any changes to the set of source files, +upstream build system, etc. In such cases, upgrading a package is a simple +matter of creating a new work branch, pointing the \c{upstream} \c{git} +submodule to the new release, running tests, and then merging, releasing, and +publishing a new package version. + +On the other extreme you may have a new major upstream release which is +essentially a from-scratch rewrite with new source code layout, different +upstream build system, etc. In such cases it may be easier to likewise start +from scratch. Specifically, create a new work branch, point the \c{upstream} +\c{git} submodule to the new release, delete the existing package, and +continue from \l{#core-package Create package and generate \c{buildfile} +templates}. + +Most of the time, however, it will be something in between where you may need +to tweak a few things here and there, such as adding symlinks to new source +files (or removing old ones), tweaking the \c{buildfiles} to reflect changes +in the upstream build system, etc. + +The following sections provide a checklist-like sequence of steps that can be +used to review upstream changes with links to the relevant earlier sections in +case adjustments are required. + + +\h2#core-version-management-new-version-branch|New version: create new work branch| + +When upgrading a package to a new upstream version it's recommended to do this +in a new work branch which, upon completion, is merged into \c{master} (or +equivalent). For example, if the new upstream version is \c{2.2.0}: + +\ +$ git checkout -b wip-2.2.0 +\ + +\N|If you are not the maintainer of the package and would like to help with +preparing the new version, then, when everything is ready, use this branch +to create a pull request instead of merging it directly.| + + +\h2#core-version-management-new-version-open|New version: open new version| + +This step corresponds to \l{#core-package-adjust-version Adjust package +version} during the initial packaging. Here we can make use of the +\c{bdep-release} command to automatically open the new version and make +the corresponding commit. For example, if the new upstream version is +\c{2.2.0}: + +\ +$ bdep release --open --no-push --open-base 2.2.0 +\ + + +\h2#core-version-management-new-version-submodule|New version: update \c{upstream} submodule| + +This step corresponds to \l{#core-repo-submodule Add upstream repository as +\c{git} submodule} during the initial packaging. Here we need to update +the submodule to point to the upstream commit that corresponds to the +new version. + +For example, if the upstream release tag we are interested in is called +\c{v2.2.0}, to update the \c{upstream} submodule to point to this release +commit, run the following commands: + +\ +$ cd upstream/ +$ git checkout v2.2.0 +$ cd ../ + +$ git add . +$ git status +$ git commit -m \"Update upstream submodule to 2.2.0\" +\ + + +\h2#core-version-management-new-version-review|New version: review upstream changes| + +At this point it's a good idea to get an overview of the upstream changes +between the two releases in order to determine which adjustments are likely to +be required in the \c{build2} package. We can use the \c{upstream} submodule +for that, which contains the change history we need. + +One way to get an overview of changes between the releases is to use a +graphical repository browser such as \c{gitk} and view a cumulative \c{diff} +of changes between the two versions. For example, assuming the latest packaged +version is tagged \c{v2.1.0} and the new version is tagged \c{v2.2.0}: + +\ +$ cd upstream/ +$ gitk v2.1.0..v2.2.0 & +\ + +Then click on the commit tagged \c{v2.2.0}, scroll down and right-click on the +commit tagged \c{v2.1.0}, and select the \"Diff this -> selected\" menu item. +This will display the cumulative set of changes between these two upstream +versions. Review them looking for the following types of changes in +particular (discussed in the following sections): + +\ul| + +\li|Changes to the source code layout.| + +\li|New dependencies being added or old removed.| + +\li|New source files being added or old removed (including in tests, etc).| + +\li|Changes to the upstream build system.| + +\li|Other new files/subdirectories being added or old removed.|| + + +\h2#core-version-management-new-version-layout|New version: layout changes| + +As mentioned earlier, for drastic layout changes it may make sense to start +from scratch and re-generate the package with the \c{bdep-new} command (use +\l{#core-package-struct Decide on the package source code layout} as a +starting point). On the other hand, if the changes are minor, then you can try +to adjust things manually. An in-between strategy is to generate the new +layout using \c{bdep-new} on the side and then retrofit the relevant changes +in \c{buildfiles} to the existing package. In a sense, this approach uses +\c{bdep-new} as a guide to figure out how to implement the new layout. + + +\h2#core-version-management-new-version-dependencies|New version: new/old dependencies| + +If upstream added new or removed old dependencies, then you will need to +replicate these changes in your package as in the \l{#core-fill-depend Add +dependencies} and \l{#core-adjust-build-src-source-dep Adjust source +\c{buildfile}: dependencies} initial packaging steps. + + +\h2#core-version-management-new-version-sources|New version: new/old source files| + +If upstream added new or removed old source files, then you will need to +replicate these changes in your package as in the \l{#core-fill-source Fill +with upstream source code} and possibly \l{#core-adjust-build-src-header +Adjust header \c{buildfile}} and \l{#core-adjust-build-src-source-src Adjust +source \c{buildfile}: sources, private headers} initial packaging steps. + +Also don't forget about tests, examples, etc., which may also add new or +remove old source files (typically new tests). See +\l{#core-test-upstream-convert Convert smoke test to upstream tests}. + +If there are any manual modifications to the upstream source code, then you +will also need to re-apply them to the new version as discussed in +\l{#howto-patch-upstream-source-manual Modifying upstream source code +manually}. + + +\h2#core-version-management-new-version-build|New version: changes to build system| + +If upstream changed anything in the build system, then you may need to +replicate these changes in your package's \c{buildfiles}. The relevant initial +packaging steps are: \l{#core-adjust-build-wide Adjust project-wide build system files in +\c{build/}} and \l{#core-adjust-build-src-source-opt Adjust source +\c{buildfile}: build and export options}. + +The corresponding steps for tests are: \l{#core-test-smoke-build-wide Adjust +project-wide build system files in \c{tests/build/}} and +\l{#core-test-upstream-convert Convert smoke test to upstream tests}. + + +\h2#core-version-management-new-version-other|New version: other new/old files/subdirectories| + +If upstream added or removed any other files or subdirectories that are +relevant to our package (such as documentation), then adjust the package +similar to the \l{#core-root-buildfile Adjust root \c{buildfile}} and +\l{#core-root-buildfile-doc Adjust root \c{buildfile}: other subdirectories} +initial packaging steps. + + +\h2#core-version-management-new-version-root|New version: review \c{manifest} and \c{PACKAGE-README.md}| + +It makes sense to review the package \c{manifest} (\l{#core-root-manifest +Adjust \c{manifest}}) and \c{PACKAGE-README.md} (\l{#core-root-package-readme +Adjust \c{PACKAGE-README.md}}) for any updates. + + +\h2#core-version-management-new-version-repo|New version: review repository \c{README.md}| + +If any new packages were added in this version or if there are any changes to +the development workflow, then it makes sense to review and if necessary +update package repository \c{README.md} (\l{#core-repo-readme Adjust package +repository \c{README.md}}). + + +\h2#core-version-management-new-version-issues|New version: review/fix accumulated issues| + +When a bug is identified in an already released package version, we may not +always be able to fix it immediately (for example, by +\l{#core-version-management-new-revision releasing a revision}). This could be +because the change is too extensive/risky for a revision or simply not +critical enough to warrant a release. In such cases it's recommended to file +an issue in the package repository with the view to fix it when the next +opportunity arises. Releasing a new upstream version is one such opportunity +and it makes sense to review any accumulated package issues and see if any +of them could be addressed. + + +\h2#core-version-management-new-version-test|New version: test locally and with CI| + +Once all the adjustments are in, test the package both locally and with CI +similar to how we did it during the initial packaging after completing the +smoke test: + +\l{#core-test-smoke-locally Test locally}\n +\l{#core-test-smoke-locally-install Test locally: installation}\n +\l{#core-test-smoke-locally-dist Test locally: distribution}\n +\l{#core-test-smoke-ci Commit and test with CI} + + +\h2#core-version-management-new-version-release|New version: merge, release, and publish| + +When the new version of the package is ready to be released, merge the +work branch to \c{master} (or equivalent): + +\ +$ git checkout master +$ git merge --ff-only wip-2.2.0 +\ + +Then release and publish using the same steps as after the initial packaging: +\l{#core-release-publish Release and publish}. + + +\h2#core-version-management-old-series|New version/revision in old release series| + +As discussed in \l{#core-version-management Package version management}, if we +have already switched to the next upstream version in the \c{master} (or +equivalent) branch, we cannot go back and release a new version or revision +for an older release series on the same branch. Instead, we need to create a +separate, long-lived branch for this work. + +As an example, let's say we need to release another revision or a patch +version for an already released \c{2.1.0} while our \c{master} branch has +already moved on to \c{2.2.0}. In this case we create a new branch, called +\c{2.1}, to continue with the \c{2.1.Z} release series. The starting point of +this branch should be the latest released version/revision in the \c{2.1} +series. Let's say in our case it is \c{2.1.0+2}, meaning we have released two +revisions for \c{2.1.0} on the \c{master} branch before upgrading to +\c{2.2.0}. Therefore we use the \c{v2.1.0+2} release tag to start the +\c{2.1} branch: + +\ +$ git checkout -b 2.1 v2.1.0+2 +\ + +Once this is done, we continue with the same steps as in +\l{#core-version-management-new-revision New revision} or +\l{#core-version-management-new-version New version} except that we never +merge this branch to \c{master}. If we ever need to release another revision +or version in this release series, then we continue using this branch. In a +sense, this branch becomes the equivalent of the \c{master} branch for this +release series and you should treat it as such (once published, never delete, +rewrite its history, etc). + +\N|It is less likely but possible that you may need to release a new minor +version in an old release series. For example, the master branch may have +moved on to \c{3.0.0} and you want to release \c{2.2.0} after the already +released \c{2.1.0}. In this case it makes sense to call the branch \c{2} since +it corresponds to the \c{2.Y.Z} release series. If you already have the +\c{2.1} branch, then it makes sense to rename it to \c{2}.| + + +\h1#dont-do|What Not to Do| + +This chapter describes the common anti-patterns along with the recommended +alternative approaches. + + +\h#dont-from-scratch|Don't write \c{buildfiles} from scratch, use \c{bdep-new}| + +Unless you have good reasons not to, create the initial project layout +automatically using \l{bdep-new(1)}, then tweak it if necessary and fill with +upstream source code. + +The main rationale here is that there are many nuances in getting the build +right and auto-generated \c{buildfiles} had years of refinement and +fine-tuning. The familiar structure also makes it easier for others to +understand your build, for example while reviewing your package submission +or helping out with the package maintenance. + +The \l{bdep-new(1)} command supports a wide variety of +\l{bdep-new.xhtml#src-layout source layouts}. While it may take a bit of time +to understand the customization points necessary to achieve the desired layout +for your first package, this will pay off in spades when you work on +converting subsequent packages. + +See \l{#core-package-craft-cmd Craft \c{bdep\ new} command line to create +package} for details. + + +\h#dont-fix-upstream|Avoid fixing upstream issues in the \c{build2} package| + +Any deviations from upstream makes the \c{build2} package more difficult to +maintain. In particular, if you make a large number of changes to the upstream +source code, releasing a new version will require a lot of work. As a result, +it is recommended to avoid fixing upstream issues in the \c{build2} package. +Instead, try to have the issues fixed upstream and wait for them to be +released as a new version. + +Sometimes, however, you may have no choice. For example, upstream is inactive +or has no plans to release a new version with your fixes any time soon. Or you +may want to add support for a platform/compiler that upstream is not willing +or capable of supporting. + +Note that even if you do fix some issues in the \c{build2} package directly, +try hard to also incorporate them upstream so that you don't need to maintain +the patches forever. + +See also \l{#dont-change-upstream Avoid changing upstream source code layout} +and \l{#howto-patch-upstream-source How do I patch upstream source code?} + + +\h#dont-change-upstream|Avoid changing upstream source code layout| + +It's a good idea to stay as close to the upstream's source code layout as +possible. For background and rationale, see \l{#core-package-struct Decide on +the package source code layout}. + + +\h#dont-header-only|Don't make library header-only if it can be compiled| + +Some libraries offer two alternative modes: header-only and compiled. Unless +there are good reasons not to, a \c{build2} build of such a library should use +the compiled mode. + +\N|Some libraries use the \i{precompiled} term to describe the non-header-only +mode. We don't recommend using this term in the \c{build2} package since it +has a strong association with precompiled headers and can therefore be +confusing. Instead, use the \i{compiled} term.| + +The main rationale here is that a library would not be offering a compiled +mode if there were no benefits (usually faster compile times of library +consumers) and there is no reason not to take advantage of it in the +\c{build2} package. + +There are, however, valid reasons why a compiled mode cannot be used, the most +common of which are: + +\ul| + +\li|The compiled mode is not well maintained/tested by upstream and therefore +offers inferior user experience.| + +\li|The compiled mode does not work on some platforms, usually Windows due to +the lack of symbol export support (but see \l{b#cc-auto-symexport Automatic +DLL Symbol Exporting}).| + +\li|Uses of the compiled version of the library requires changes to the +library consumers, for example, inclusion of different headers.| + +| + +If a compiled mode cannot always be used, then it may be tempting to support +both modes by making the mode user-configurable. Unless there are strong +reasons to, you should resist this temptation and, if the compiled mode is not +universally usable, then use the header-only mode everywhere. + +The main rationale here is that variability adds complexity which makes the +result more prone to bugs, more difficult to use, and harder to review and +maintain. If you really want to have the compiled mode, then the right way to +achieve it is to work with upstream to fix any issues that prevent its use +in \c{build2}. + +There are, however, valid reasons why supporting both modes may be needed, the +most common of which are: + +\ul| + +\li|The library is widely used in both modes but switching from one mode to +the other requires changes to the library consumers (for example, inclusion of +different headers). In this case only supporting one mode would mean not +supporting a large number of library consumers.| + +\li|The library consists of a large number of independent components while its +common for applications to only use a small subset of them. And compiling all +of them in the compiled mode takes a substantial amount of time. Note that +this can alternatively be addressed by making the presence of optional +components user-configurable.| + +| + + +\h#dont-bundle|Don't bundle dependencies| + +Sometimes third-party projects bundle their dependencies with their source +code (also called vendoring). For example, a C++ library may bundle a testing +framework. This is especially common with \l{https://cppget.org/catch2 +\c{catch2}} where one often encounters a comical situation with only a few +kilobytes of library source code and over 600KB of \c{catch2.hpp}. + +The extra size, while wasteful, is not the main issue, however. The bigger +problem is that if a bug is fixed in the bundled dependency, then to propagate +the fix we will need to release a new version (or revision) of each package +that bundles it. Needless to say this is not scalable. + +While this doesn't apply to testing frameworks, an even bigger issue with +bundling of dependencies in general is that two libraries that bundle the same +dependency (potentially of different versions) may not be able to coexist in +the same build with the symptoms ranging from compile errors to subtle runtime +issues that are hard to diagnose. + +As a result, it is strongly recommended that you unbundle any dependencies +that upstream may have bundled. In case of testing frameworks, see +\l{https://github.com/build2/HOWTO/blob/master/entries/handle-tests-with-extra-dependencies.md +How do I handle tests that have extra dependencies?} for the recommended way +to deal with such cases. + +\N|One special case where a bundled dependency may be warranted is a small +utility that is completely inline/private to the implementation and where +making it an external dependency may lead to a less performant result (due to +the inability to inline without resorting to LTO). The \c{xxhash} +implementation in \c{libzstd} is a representative example of this situation.| + + +\h#dont-main-target-root-buildfile|Don't build your main targets in the root \c{buildfile}| + +It may be tempting to have your main targets (libraries, executables) in the +root \c{buildfile}, especially if it allows you to symlink entire directories +from \c{upstream/} (which is not possible if you have to have a \c{buildfile} +inside). However, this is not recommended except for the simplest of projects. + +Firstly, this quickly gets messy since you have to combine managing +\c{README}, \c{LICENSE}, etc., and subdirectories with your main target +builds. More importantly, this also means that when your main target is +imported (and thus the \c{buildfile} that defines this target must be loaded), +your entire project will be loaded, including any \c{tests/} and \c{examples/} +subprojects, and that is wasteful. + + +If you want to continue symlinking entire directories from \c{upstream/} but +without moving everything to the root \c{buildfile}, the recommended approach +is to simply add another subdirectory level. Let's look at a few concrete +example to illustrate the technique (see \l{#core-package-struct Decide on the +package source code layout} for background on the terminology used). + +Here is the directory structure of a package which uses a combined layout (no +header/source split) and where the library target is in the root +\c{buildfile}: + +\ +libigl-core/ +├── igl/ -> ../upstream/igl/ +├── tests/ +└── buildfile # Defines lib{igl-core}. +\ + +And here is the alternative structure where we have added the extra +\c{libigl-core} subdirectory with its own \c{buildfile}: + +\ +libigl-core/ +├── libigl-core/ +│ ├── igl/ -> ../../upstream/igl/ +│ └── buildfile # Defines lib{igl-core}. +├── tests/ +└── buildfile +\ + +Below is the \c{bdep-new} invocation that can be used to automatically +create this alternative structure (see \l{#core-package-craft-cmd Craft +\c{bdep\ new} command line to create package} for background and +\l{bdep-new(1)} for details): + +\ +$ bdep new \ + --type lib,prefix=libigl-core,subdir=igl,buildfile-in-prefix \ + libigl-core +\ + +Let's also look at an example of the split layout, which may require a +slightly different \c{bdep-new} sub-options to achieve the same result. Here +is the layout which matched upstream exactly: + +\ +$ bdep new --type lib,split,subdir=foo,no-subdir-source libfoo +$ tree libfoo +libfoo/ +├── include/ +│ └── foo/ +│ ├── buildfile +│ └── ... +└── src/ + ├── buildfile + └── ... +\ + +However, with this layout we will not be able to symlink the entire +\c{include/foo/} and \c{src/} subdirectories because there are \c{buildfiles} +inside (and which may tempt you to just move everything to the root +\c{buildfile}). To fix this we can move the \c{buildfiles} out of source +subdirectory \c{foo/} and into prefixes (\c{include/} and \c{src/}) using the +\c{buildfile-in-prefix} sub-option. And since \c{src/} doesn't have a source +subdirectory, we have to invent one: + +\ +$ bdep new --type lib,split,subdir=foo,buildfile-in-prefix libfoo +$ tree libfoo +libfoo/ +├── include/ +│ ├── foo/ -> ../../upstream/include/foo/ +│ └── buildfile +└── src/ + ├── foo/ -> ../../upstream/src/ + └── buildfile +\ + + +\h#dont-big-changes-revision|Don't make extensive changes in a revision| + +Unlike a new version, a revision replaces the previous revision of the same +version and as a result must be strictly backwards-compatible in all aspects +with what it replaces. If you make extensive changes in a revision, it becomes +difficult to guarantee that this requirement is satisfied. As a result, you +should refrain from making major changes, like substantially altering the +build, in a revision, instead delaying such changes until the next version. + + +\h1#howto|Packaging HOWTO| + +This chapter provides advice on how to handle less common packaging +tasks and requirements. + + +\h#howto-patch-upstream-source|How do I patch upstream source code?| + +If you need to change something in the upstream source code, there are several +options: You can make a copy of the upstream source file and make the +modifications there. While straightforward, this approach has one major +drawback: you will have to keep re-applying the changes for every subsequent +version unless and until upstream incorporates your changes. The other two +options try to work around this drawback. + +The first alternative option is to modify the upstream source code +automatically during the build, typically using an ad hoc recipe. This +approach works best when the changes are regular and can be applied +mechanically with something like the \l{testscript#builtins-sed \c{sed} +builtin}. + +The second alternative option is to use the C/C++ preprocessor to make the +necessary changes to the upstream source code during compilation. Unlike the +first alternative, this approach doesn't have a prescribed way to apply it in +every situation and often requires some imagination. Note that it also has the +tendency to quickly get out of hand, at which point it's wise to keep it +simple and use the first option (manual modification). + +The following sections examine each approach in detail. + + +\h2#howto-patch-upstream-source-manual|Modifying upstream source code manually| + +As an illustration of this approach, let's say we need to patch +\c{src/foo.cpp} in our \c{libfoo} example from the previous sections (see the +\l{#core-fill-source Fill with upstream source code} step for a refresher). +The recommended sequence of steps is as follows: + +\ol| + +\li|Rename the upstream symlink to \c{.orig}: + +\ +$ cd libfoo/src/ +$ mv foo.cpp foo.cpp.orig +\ + +| + +\li|Make a deep copy of \c{.orig}: + +\ +$ cp -H foo.cpp.orig foo.cpp +\ + +| + +\li|Make any necessary modifications in the deep copy: + +\ +$ edit foo.cpp +\ + +| + +\li|Create a patch for the modifications: + +\ +$ diff -u foo.cpp.orig foo.cpp >foo.cpp.patch +\ + +|| + +The presence of the \c{.orig} and \c{.patch} files makes it clear that the +upstream code was modified. They are also useful when re-applying the changes +to the new version of the upstream source code. The recommended sequence of +steps for this task is as follows: + +\ol| + +\li|After the \c{upstream} submodule update (see the +\l{#core-version-management-new-version-submodule New version: update +\c{upstream} submodule} step), the \c{.orig} symlink points to the new version +of the upstream source file.| + +\li|Overwrite old modified version with a deep copy of new \c{.orig}: + +\ +$ cp -H foo.cpp.orig foo.cpp +\ + +| + +\li|Apply old modifications to the new deep copy: + +\ +$ patch <foo.cpp.patch +\ + +If some hunks of the patch could not be applied, manually resolve any +conflicts.| + +\li|If in the previous step the patch did not apply cleanly, regenerate it: + +\ +$ diff -u foo.cpp.orig foo.cpp >foo.cpp.patch +\ + +|| + + +\h2#howto-patch-upstream-source-build|Modifying upstream source code during build| + +As an illustration of this approach, let's say upstream is using the +\c{${VAR\}} style variable substitutions in their \c{config.h.cmake} instead +of the more traditional \c{@VAR@} style: + +\ +/* config.h.cmake */ + +#define FOO_VERSION \"${PROJECT_VERSION}\" +\ + +The \c{${VAR\}} style is not supported by the \c{build2} +\l{https://github.com/build2/libbuild2-autoconf \c{autoconf}} module which +means we cannot use the upstream \c{config.h.cmake} as is. While we could +patch this file manually to use \c{@VAR@} instead, this is a pretty mechanical +change that can be easily made with a simple ad hoc recipe during the build, +freeing us from manually applying the same changes in subsequent versions. For +example: + +\ +using autoconf + +h{config}: in{config.h.in} +{ + autoconf.flavor = cmake + PROJECT_VERSION = $version +} + +in{config.h.in}: file{config.h.cmake} +{{ + sed -e 's/\$\{(.+)\}/@\1@/g' $path($<) >$path($>) +}} +\ + + +\h2#howto-patch-upstream-source-preproc|Modifying upstream source code with C/C++ preprocessor| + +A good illustration of this approach is adding the \c{build2} metadata to an +executable (see +\l{https://github.com/build2/HOWTO/blob/master/entries/convey-additional-information-with-exe-lib.md +How do I convey additional information (metadata) with executables and C/C++ +libraries?} for background). Let's say we have a symlink to upstream's +\c{main.c} that implements the executable's \c{main()} function and we need to +add a snipped of code at the beginning of this function that handles the +\c{--build2-metadata} option. While manually modifying \c{main.c} is not a +wrong approach, we can try to be clever and do it automatically with the +preprocessor. + +Specifically, we can create another file next to the \c{main.c} symlink, +calling it, for example, \c{main-build2.c}, with the following contents: + +\ +/* Handle --build2-metadata in main() (see also buildfile). */ + +#define main xmain +#include \"main.c\" +#undef main + +#include <stdio.h> +#include <string.h> + +int main (int argc, const char** argv) +{ + if (argc == 2 && strncmp (argv[1], \"--build2-metadata=\", 18) == 0) + { + printf (\"# build2 buildfile foo\n\"); + printf (\"export.metadata = 1 foo\n\"); + printf (\"foo.name = [string] foo\n\"); + ... + return 0; + } + + return xmain (argc, argv); +} +\ + +The idea here is to rename the original \c{main()} with the help of the C +preprocessor and provide our own \c{main()} which, after handling +\c{--build2-metadata} calls the original. One notable deal-breaker for this +approach would be a C++ implementation of \c{main()} that doesn't have the +explicit \c{return}. There is also a better chance in C++ for the \c{main} +macro to replace something unintended. + +To complete this we also need to modify our \c{buildfile} to exclude +\c{main.c} from compilation (since it is compiled as part of \c{main-build2.c} +via the preprocessor inclusion). For example: + +\ +exe{foo}: {h c}{** -main} +exe{foo}: c{main}: include = adhoc # Included in main-build2.c. +\ + + +\h#howto-bad-inclusion-practice|How do I deal with bad header inclusion practice?| + +This section explains how to deal with libraries that include their public, +generically-named headers without the library name as a subdirectory +prefix. Such libraries cannot coexist, neither in the same build nor when +installed. + +Specifically, as an illustration of the problem, consider the \c{libfoo} +library with a public header named \c{util.h} that is included as \c{<util.h>} +(instead of, say, \c{<libfoo/util.h>} or \c{<foo/util.h>}). If this library's +headers are installed directly into, say, \c{/usr/include}, then if two such +libraries happened to be installed at the same time, then one will overwrite +the other's header. There are also problems in the non-installed case: if two +such libraries are used by the same project, then which \c{<util.h>} header +gets included depends on which library's header search path ends up being +specified first on the command line (with the \c{-I} option). + +These issues are severe enough that libraries with such inclusion issues +cannot be published to \l{https://cppget.org cppget.org} without them being +addressed in the \c{build2} package. Thankfully, most library authors these +days use the library name as an inclusion prefix (or sometimes they have +headers that are decorated with the library name). However, libraries that do +not follow these guidelines do exist and this section describes how to change +their inclusion scheme if you are attempting to package one of them. + +\N|One notable consequence of changing the inclusion scheme is that it will no +longer be possible to use a system-installed version of the package (because +it presumably still uses the unqualified inclusion scheme). Note, however, +that distributions like Debian and Fedora have the same co-existence issue as +we do and are generally strict about potential header clashes. In +particular, it is not uncommon to find Debian packages installing library +headers into subdirectories of \c{/usr/include} to avoid such clashes. And if +you find this to be the case for the library you are packaging, then it may +make sense to use the same prefix as used by the main distributions for +compatibility. + +It is also possible that distributions disregard these considerations for some +libraries. This usually happens for older, well-known libraries that happened +to be installed this way in the early days and changing things now will be too +disruptive. In a sense, it is understood that such libraries effectively +\"own\" the unqualified header names that they happen to be using. If you +think you are packaging such a library, +\l{https://build2.org/community.xhtml#help get in touch} to discuss this +further since it may make sense to also disregard this rule in +\l{https://cppget.org cppget.org}.| + +As a concrete example of the approach, let's continue with \c{libfoo} that has +\c{util.h} and which upstream expects the users to include as \c{<util.h>}. +The is what the upstream source code layout may look like: + +\ +libfoo/ +├── include/ +│ └── util.h +└── src/ + └── ... +\ + +Our plan is to change the inclusion scheme in the \c{build2} package from +\c{<util.h>} to \c{<libfoo/util.h>}. To this effect, we use a slightly +modified layout for our package (see \l{#core-package-craft-cmd Craft \c{bdep +new} command line to create package} on how to achieve it): + +\ +libfoo/ +├── include/ +│ └── libfoo/ +│ └── util.h -> ../../../upstream/include/util.h +└── src/ + └── ... -> ../../upstream/src/... +\ + +The installation-related section in our \l{#core-adjust-build-src-header +header \c{buildfile}} will look like this: + +\ +# Install into the libfoo/ subdirectory of, say, /usr/include/ +# recreating subdirectories. +# +{hxx ixx txx}{*}: +{ + install = include/libfoo/ + install.subdirs = true +} +\ + +In the \l{#core-adjust-build-src-source source \c{buildfile}} we will most +likely need to add the \c{include/libfoo} header search path since the +upstream source files continue to include public headers without the library +prefix (there should be no harm in that and it's not worth modifying them): + +\ +# Build options. +# +out_pfx_inc = [dir_path] $out_root/include/ +src_pfx_inc = [dir_path] $src_root/include/ +out_pfx_src = [dir_path] $out_root/src/ +src_pfx_src = [dir_path] $src_root/src/ + +# Unqualified (without <libfoo/...>) header search paths. +# +out_pfx_inc_unq = [dir_path] $out_root/include/libfoo +src_pfx_inc_unq = [dir_path] $src_root/include/libfoo + +cxx.poptions =+ \"-I$out_pfx_src\" \"-I$src_pfx_src\" \\ + \"-I$out_pfx_inc\" \"-I$src_pfx_inc\" \\ + \"-I$out_pfx_inc_unq\" \"-I$src_pfx_inc_unq\" +\ + +It is also possible that public headers include each other as \c{<util.h>} +rather than the more common \c{\"util.h\"}. If that's the case, then we need +to fix that and there are two ways to do it. The first approach is to patch +the public headers to include each other with the library prefix (that is, +\c{<libfoo/util.h>}, etc). See \l{#howto-patch-upstream-source How do I patch +upstream source code?} for details. + +The second approach is to support including public headers both ways, that is, +as \c{<libfoo/util.h>} and as \c{<util.h>}. This will not only solve the above +problem (public headers including each other), but also support any existing +code that uses this library and most likely includes its headers the old way, +without the prefix. + +There is, however, a major drawback to doing that: while the installation of +the library can now co-exist with other libraries (because we install its +public headers into, say, \c{/usr/include/libfoo}), it may still not be usable +in combination with other libraries from the same build (because we still add +the unqualified header search path). + +If you still want to provide this dual inclusion support, the way to achieve +it is by exporting the unqualified header search path and also adding it to +the \c{pkg-config} files (see \l{#howto-extra-header-install-subdir How do I +handle extra header installation subdirectory?} for background on the +latter). For example: + +\ +# Export options. +# +lib{foo}: +{ + cxx.export.poptions = \"-I$out_pfx_inc\" \"-I$src_pfx_inc\" \\ + \"-I$out_pfx_inc_unq\" \"-I$src_pfx_inc_unq\" + cxx.export.libs = $intf_libs +} + +# Make sure headers installed into, say, /usr/include/libfoo/ +# can also be included without the directory prefix for backwards +# compatibility. +# +lib{foo}: cxx.pkgconfig.include = include/ include/libfoo/ +\ + + +\h#howto-extra-header-install-subdir|How do I handle extra header installation subdirectory?| + +This section explains how to handle an additional header installation +subdirectory. As an illustration of the problem, consider the \c{libfoo} +example from the previous sections (see the \l{#core-fill-source Fill with +upstream source code} step for a refresher). In that example the library +headers are included as \c{<foo/util.hpp>} and installed as, say, +\c{/usr/include/foo/util.hpp}. In this scheme the installed header inclusion +works without requiring any extra steps from our side because the compiler +searches for header in \c{/usr/include} by default. + +However, some libraries choose to install their headers into a subdirectory +of, say, \c{/usr/include} but without having this subdirectory as part of the +inclusion path (\c{foo/} in \c{<foo/util.hpp>}). The two typical reasons for +this are support for installing multiple versions of the same library +side-by-side (for example, \c{/usr/include/foo-v1/foo/util.hpp}) as well as +not using the library name as the inclusion subdirectory prefix and then +having to hide the headers in a subdirectory due to potential clashes with +other headers (if installed directly into, say, \c{/usr/include}; see +\l{#howto-bad-inclusion-practice How do I deal with bad header inclusion +practice?} for background). + +In such cases the installed header inclusion does not work out of the box and +we have to arrange for an additional header search path to be added via +\c{pkg-config}. Let's use the versioned library case to illustrate this +technique. The relevant part from the \l{#core-adjust-build-src-header header +\c{buildfile}} will look like this: + +\ +# Install into the foo-vN/foo/ subdirectory of, say, /usr/include/ +# recreating subdirectories. +# +{hxx ixx txx}{*}: +{ + install = include/\"foo-v$version.major\"/foo/ + install.subdirs = true +} +\ + +The part that we need to add, this time to the +\l{#core-adjust-build-src-source source \c{buildfile}}, looks like this: + +\ +# Make sure headers installed into, say, /usr/include/foo-vN/foo/ +# can be included as <foo/*.hpp> by overriding the header search +# path in the generated pkg-config files. +# +lib{foo}: cxx.pkgconfig.include = include/\"foo-v$version.major\"/ +\ + +\N|The variable will be \c{c.pkgconfig.include} for a C library.| + + +\h#howto-no-extension-header|How do I handle headers without an extension?| + +If all the headers in a project have no extension, then you can simply +specify the empty \c{extension} value for the \c{hxx{\}} target type +in \c{build/root.build}: + +\ +hxx{*}: extension = +cxx{*}: extension = cpp +\ + +Note, however, that using wildcard patterns for such headers in your +\c{buildfile} is a bad idea since such a wildcard will most likely pick up +other files that also have no extension (such as \c{buildfile}, executables on +UNIX-like systems, etc). Instead, it's best to spell the names of such headers +explicitly. For example, instead of: + +\ +lib{hello}: {hxx cxx}{*} +\ + +Write: + +\ +lib{hello}: cxx{*} hxx{hello} +\ + +If only some headers in a project have no extension, then it's best to specify +the non-empty extension for the \c{extension} variable in \c{build/root.build} +(so that you can still use wildcards for headers with extensions) and spell +out the headers with no extension explicitly. Continuing with the above +example, if we have both the \c{hello.hpp} and \c{hello} headers, then we can +handle them like this: + +\ +hxx{*}: extension = hpp +cxx{*}: extension = cpp +\ + +\ +lib{hello}: {hxx cxx}{*} hxx{hello.} +\ + +Notice the trailing dot in \c{hxx{hello.\}} \- this is the explicit \"no +extension\" specification. See \l{b#targets Targets and Target Types} +for details. + + +\h#howto-debug-macro|How do I expose extra debug macros of a library?| + +Sometimes libraries provide extra debugging facilities that are usually +enabled or disabled with a macro. For example, \c{libfoo} may provide the +\c{LIBFOO_DEBUG} macro that enables additional sanity checks, tracing, etc. +Normally, such facilities are disabled by default. + +While it may seem like a good idea to detect a debug build and enable this +automatically, it is not: such facilities usually impose substantial overheads +and the presence of debug information does not mean that performance is not +important (people routinely make optimized builds with debug information). + +As a result, the recommended approach is to expose this as a configuration +variable that the consumers of the library can use (see \l{b#proj-config +Project Configuration} for background). Continue with the \c{libfoo} example, +we can add \c{config.libfoo.debug} to its \c{build/root.build}: + +\ +# build/root.build + +config [bool] config.libfoo.debug ?= false +\ + +And then define the \c{LIBFOO_DEBUG} macro based on that in the \c{buildfile}: + +\ +# src/buildfile + +if $config.libfoo.debug + cxx.poptions += -DLIBFOO_DEBUG +\ + +If the macro is also used in the library's interface (for example, in inline +or template functions), then we will also need to export it (see +\l{#core-adjust-build-src-source-opt Adjust source \c{buildfile}: build and +export options} for details): + +\ +# src/buildfile + +if $config.libfoo.debug +{ + cxx.poptions += -DLIBFOO_DEBUG + lib{foo}: cxx.export.poptions += -DLIBFOO_DEBUG +} +\ + +\N|If the debug facility in question should be enabled by default even in the +optimized builds (in which case the macro usually has the \c{NO_DEBUG} +semantics), the other option is to hook it up to the standard \c{NDEBUG} +macro, for example, in the library's configuration header file.| + +Note that such \c{.debug} configuration variables should primarily be meant +for the user to selectively enable extra debugging support in certain +libraries of their build. However, if your project depends on a number of +libraries with such extra debugging support and it generally makes sense to +also enable this support in dependencies if it is enabled in your project, +then you may want to propagate your \c{.debug} configuration value to the +dependencies (see the \l{bpkg#manifest-package-depends \c{depends} package +\c{manifest} value} for details on dependency configuration). You, however, +should still allow the user to override this decision on the per-dependency +basis. + +Continuing with the above example, let's say we have \c{libbar} with +\c{config.libbar.debug} that depends on \c{libfoo} and wishes by default to +enable debugging in \c{libfoo} if it is enabled in \c{libbar}. This is how we +can correctly arrange for this in \c{libbar}'s \c{manifest}: + +\ +depends: +\\ +libfoo ^1.2.3 +{ + # We prefer to enable debug in libfoo if enabled in libbar + # but accept if it's disabled (for example, by the user). + # + prefer + { + if $config.libbar.debug + config.libfoo.debug = true + } + + accept (true) +} +\\ +\ + + +\h1#faq|Packaging FAQ| + +\h#faq-publish|Publishing FAQ| + +\h2#faq-publish-alpha|Why is my package in \c{alpha} rather than \c{stable}?| + +If your package uses a semver version (or semver-like, that is, has three +version components) and the first component is zero (for example, \c{0.1.0}), +then, according to the semver specification, this is an alpha version and +\l{bdep-publish(1)} automatically published such a version to the \c{alpha} +section of the repository. + +Sometimes, however, in a third-party package, while the version may look like +semver, upstream may not assign the zero first component any special +meaning. In such cases you can override the \c{bdep-publish} behavior with the +\c{--section} option, for example: + +\ +$ bdep publish --section=stable +\ + +Note that you should only do this if you are satisfied that by having the zero +first component upstream does not imply alpha quality. Getting an explicit +statement to this effect from upstream is recommended. + + +\h2#faq-publish-stage|Where to publish if package requires staged toolchain?| + +If your package requires the \l{https://build2.org/community.xhtml#stage staged +toolchain}, for example, because it needs a feature or bugfix that is not yet +available in the released toolchain, then you won't be able to publish it to +\c{cppget.org}. Specifically, if your package has the accurate \c{build2} +version constraint and you attempt to publish it, you will get an error like +this: + +\ +error: package archive is not valid + info: unable to satisfy constraint (build2 >= 0.17.0-) for package foo + info: available build2 version is 0.16.0 +\ + +There are three alternative ways to proceed in this situation: + +\ol| + +\li|Wait until the next release and then publish the package to +\c{cppget.org}.| + +\li|If the requirement for the staged toolchain is \"minor\", that is, it +doesn't affect the common functionality of the package or only affects a small +subset of platforms/compilers, then you can lower the toolchain version +requirement and publish the package to \c{cppget.org}. For example, if you +require the staged toolchain because of a bugfix that only affects one +platform, it doesn't make sense to delay publishing the package since it is +perfectly usable on all the other platforms in the meantime.| + +\li|Publish it to \l{https://queue.stage.build2.org queue.stage.build2.org}, +the staging package repository. This repository contain new packages that +require the staged toolchain to work and which will be automatically +moved to \c{cppget.org} once the staged version is released. The other +advantage of publishing to this repository (besides not having to remember +to manually publish the package once the staged version is released) is +that your package becomes available from an archive repository, which is +substantially faster than a \c{git} repository. + +To publish to this repository, use the following \c{bdep-publish} command +line: + +\ +$ bdep publish --repository=https://stage.build2.org ... +\ + +|| + +\h2#faq-publish-owner|Why \"project owner authentication failed\" while publishing?| + +If you are getting the following error while attempting to publish a new +version of a package: + +\ +$ bdep publish +... +error: project owner authentication failed +\ + +Then this means the remote \c{git} repository you are using does not match the +one from which you (or someone else) has published the initial version of the +package. + +In \c{build2} we use the ownership of the package \c{git} repository as a +proxy for the ownership of the package name on \l{https://cppget.org +cppget.org}. Specifically, when you publish the package for the first time, +we record the \c{git} URL for its package repository. And any further versions +of this package can only be submitted by someone who has write access to this +repository. See \l{bdep-publish(1)} for details. + +Based on this background, the first step you need to take when getting the +above owner authentication error is to understand its cause. For that, first +use the \c{git-config} command to see the URL you are using locally: + +\ +$ git config --get remote.origin.url +\ + +Then look in the \l{https://github.com/cppget/ \c{git} repositories} that back +\l{https://cppget.org cppget.org} and \l{https://queue.cppget.org +queue.cppget.org} and find the URL that is recorded in the \c{owners/} +subdirectory in the corresponding \c{package-owner.manifest} file. + +Note that your local URL will normally be SSH while the recorded URL will +always be HTTPS. Provided that the host names match, the part to look in for +differences is the path component. One common cause of a mismatch is the +missing \c{.git} extension. For example (local first, recorded second): + +\ +git@github.com:build2-packaging/zstd +https://github.com/build2-packaging/zstd.git +\ + +In this case adding the missing extension to the local URL should fix the +error. + +If, however, the discrepancy is expected, for example, because you have +renamed the package repository or moved it to a new location, the ownership +information will need to be updated manually. In this case feel free to submit +a pull request with the necessary changes or +\l{https://build2.org/community.xhtml#help get in touch}. + +" |