// file : doc/packaging.cli // license : MIT; see accompanying LICENSE file "\name=build2-packaging-guide" "\subject=toolchain" "\title=Packaging Guide" // NOTES // // - Maximum
line is 70 characters. // // - In guideline titles (do/don't) omit a/the. // // @@ Close the issue in WISHLIST. " \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. \N|This document is a work in progress and is incomplete.| \h1#intro|Introduction| @@ Assume read through toolchain introduction and build system introduction. Also, ideally, have some experience using \c{build2} in your own projects. The aim of this guide is to ease the convertion 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 also make it easier for others to review your work and help with ongoing maintenance. The primary focus of this guide are 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 using the \l{https://ci.cppget.org \c{build2} CI service}.| \li|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 using the \l{https://ci.cppget.org \c{build2} CI service}.| \li|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 ecosystem is famous for a large amount varience in the platforms, compilers, and build systems used. This leads to what appears to be an endless list of special considerations that are applicable in certain, more complex cases. As result, the presented guidelines are divided into four chapters: The \l{#core Core Guidelines} cover steps that are applicable to all or 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} which cover the above-mentioned long list of special considerations that are only applicable in certain cases as well as answer frequent packaging-related questions, respectively. @@ Purpose of notes to provide rationale. 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. \h#intro-term|Terminology| upstream upstream repository project package (third-party project) package \c{git} repository multi-package repository \h1#core|Core Guidelines| \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 they finished 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. \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 loose 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 stable shape, 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 strating 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 should rename it before transferring it to the \l{https://github.com/build2-packaging github.com/build2-packaging} organization.| There is one potenential problem with this approach: it is possible that several people 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 GitHub UI. Don't automatically add any files (\c{README}, \c{LICENSE}, etc).| \li|Set the repository description in GitHub UI to the \c{build2 package for} line, where \c{ } is the project name.| \li|Clone the repository to your machine.|| \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/ /foo.git}. We have created its package repository at \c{https://github.com/ /foo.git} (with the \c{build2 package for foo} description) and can now clone it: \ $ git clone https://github.com/ /foo.git \ \h2#core-repo-init|Initialize package repository with \c{bdep new --type empty}| Change to the root directory of the package repository that you have clonned on the previous step and run (continuing with our \c{foo} example): \ $ cd foo $ bdep new --type empty $ tree . ./ ├── .gitattributes ├── .gitignore ├── README.md └── repositories.manifest \ This command creates a number of files in the root of the repository: \dl| \li|\n\c{README.md}\n This is the project \c{README}. 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 dependecies we will discuss the appropriate changes to this file later.| \li|\n\c{.gitattributes} and \c{.gitignore}\n These are the \c{git} infrastrucutre 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: \ $ git add . $ git status $ git commit -m \"Initialize repository\" \ \N|In these guidelines we will be using the package repository setup that is capable of having multiple packages. 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 package 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) or 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 indended: 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): \ $ git submodule add https://github.com/ /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.| 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. Failed 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 command: \ $ cd upstream $ git checkout vX.Y.Z $ cd .. \ Then add and commit these changes: \ $ git add . $ git status $ git commit -m \"Add upstream submodule\" \ Now we have all the upstream source code for the release that we are interested in 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} 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 want 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 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 Debain 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{testing} for newer packages.| Picking a name for a package that provides a library is where things can get more complicated. While all the recommendation 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 the 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 upstream name and if the project maintainer ever decides to add \c{build2} support the upstream repository, there could be substantial friction. On the other handle, 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. That 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 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. Failed 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 seperate \c{build2} packages and if so, how. Here, again, the corresponding Debian packages can be a good strating point. Note, however, that in this case we often deviate from their split, especially when it comes to libraries. For 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 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 seperate 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. Note also that while it's a good idea to decide on the package split and all the package names upfront to avoid suprises 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). Admittedly, the recommendation 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 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 upstream layout as possible unless there are valid reasons to deviate. This 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 subdirectory. 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 directory into a subdirectory. Our overall plan for the package is to create the initial layout and \c{buildfile} templates automatically using \l{bdep-new(1)} in the \c{--package} mode, then tweak \c{buildfile}s if necessary, and finally \"fill\" the package with upstream source code using symlinks. 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{buildfile}s 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. 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. And so the focus of the following 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|Study the upstream source layout and existing build system.| \li|Craft and execute the \l{bdep-new(1)} command line necessary to achieve the upstream layout.| \li|Study the auto-generated \c{buildfile}s 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 subdirectory, 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.|| 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\ }, or to include the library name into each public header name, for example, \c{#include\ } or \c{#include\ } (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 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{intro#proj-struct Canonical Project Structure} for background and details. See \l{#howto-bad-inclusion-practice How do I deal with bad header inclusion practice} if you encounter such a case.| 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 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{ } 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 we instead end up with, say, \c{/usr/include/sub/foo/util.h} and \c{/usr/include/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 be recreated in the upstream source code layout. In particular, if upstream includes public headers with a subdirectory, then this subdirectory would normally also be present in the upstream layout so that such a header can be included form 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{ }. 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 pieces 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 include subdirectory, similar to the header directory. In the above split layout the \c{src/} directory doesn't contain the include 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. \h2#core-package-craft-cmd|Craft \c{bdep new} command line to create package| The recommened 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. Let's illustrate this approach on the original example of the split layout: \ 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 clonned and initialized earlier looks like this: \ $ tree foo/ foo/ ├── .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 include 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 provided its own equivalent to 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 maintainer to rectify this in 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 template. 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{buildfile}s into the header/source prefix directory instead of include subdirectory. To illustrate the difference, compare these two auto-generated layouts paying attention to the location of \c{buildfile}s: \ $ bdep new ... --type lib,split libfoo $ tree libfoo/ libfoo/ ├── include/ │ └── foo/ │ ├── buildfile │ └── foo.hpp └── src/ └── foo/ ├── buildfile └── foo.cpp \ \ $ bdep new ... --type lib,split,buildfile-in-prefix libfoo $ tree libfoo/ libfoo/ ├── include/ │ ├── foo/ │ │ └── foo.hpp │ └── buildfile └── src/ ├── foo/ │ └── foo.cpp └── buildfile \ Note that this sub-option can only be used if both the header and source directories (\c{include/} and \c{src/} in our case) have an include 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{buildfile}s are inside the \c{foo/} subdirectories which mean we cannot just symlink \c{foo/} from upstream. This can be such a strong motivation that it may make sense to invent an include subdirectory in the source directory even if upstream doesn't have one. See \l{#dont-main-target-root-buildfile Don't build your main targets in root \c{buldfile}} for details on this technique. Another reason we may want to move \c{buildfile}s to prefix is to be able to handle upstream projects that may have multiple such include subdirectories. While this situation not very common in the header directory, it can be enountered in the source directory of more complex projects where upstream wishes to seperate the source code into components.|| @@ Terminology does not match bdep-new 'include subdirectory'. Maybe change in bdep-new? 'source prefix directory' and 'source subdirectory' are quite close. But then include subdirectory inside src sounds like an oxymoron. I believe this is also consistent with Canonical Project Structure. @@ 'Don't build your main targets in root buldfile' needs work. Continuing with our \c{libfoo} example, assuming upstream provides 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 \ Let's also get a more complete view of what it generates: \ $ tree libfoo/ libfoo/ ├── build/ │ └── ... ├── include/ │ └── foo/ │ ├── buildfile │ └── foo.hpp ├── src/ │ ├── buildfile │ └── foo.cpp ├── tests/ │ ├── build/ │ │ └── ... │ ├── basics/ │ │ ├── buildfile │ │ └── driver.cpp │ └── buildfile ├── buildfile ├── manifest └── README.md \ \h2#core-package-review|Review and test auto-genetated \c{buildfile} templates| Once the overall layout looks right, the next step is to take a closer look at the generated \c{buildfile}s to make sure that overall they match the upstrem build. Of particular interest are the header and source directory \c{buildfile}s (\c{libfoo/include/buildifle} and \c{libfoo/src/buildifle} 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 \"binful\" library (that is, a library that includes source files and therefore produces library binaries) while the upsteam 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{buildfile} 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{buildfile}s, 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. \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.| Note that at this stage its easiest to build, test, and install in source directly sidestepping the \c{bdep} initialization of the package (which you would have to de-initalize before you can re-run \c{bdep-new}). Continue with the above example, the recommended sequence of commands would be: \ $ cd libfoo $ b update $ b test $ b install config.install.root=/tmp/install $ b clean \ 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 close at its contents in the following steps. 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 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\" (@@ ref HOWTO). \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 pacticular, 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 forgeting to add the corresponding entry in the root \c{buildfile}).| @@ We can actually do the final creation here (with symlinking, etc). @@ Adding additional subdirectories to avoid symlinking individual files. Ideally would want bdep-new mode. @@ Pre-symlink README, LICENSE, PACKAGE-README.md? @@ Auto-detect the license? @@ Where do we overlay the source code? @@ The Don't write buildfiles by hand entry is now duplicate/redundant. ====================================================================== \h1#dont-do|What Not to Do| \h#dont-from-scratch|Don't write \c{buildfile}s 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{buildfile}s 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. 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. The recommended sequence of steps is as follows: \ol| \li|Study the upstream source layout. We want to stay as close to upstream as possible since this has the best chance of producing an issues-free result (see \l{#dont-change-upstream Don't change upstream source code layout} for details).| \li|Craft and execute the \l{bdep-new(1)} command line necessary to achieve the upstream layout.| \li|Study the auto-generated \c{buildfile}s 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. For example, if you see that the generated project assumes the wrong C++ file extensions, these can be changed with \c{--lang|-l} sub-options.| \li|Once you have squeezed as much as possible out of \l{bdep-new(1)}, it's time for manual customizations. These would normally include: \ul| \li|Replace generated source code with upstream, normally as symlinks from the \c{upstream/} \c{git} submodule.| \li|Tweak source subdirectory \c{buildfile} that builds the main target (library, executable).| \li|Add tests and, if necessary, examples.| \li|Tweak \c{manifest} (in particular the \c{version}, \c{summary}, and \c{license} values).| \li|Fill in \c{README.md}.||| | \h#dont-change-upstream|Don't change 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-forget-update-manifest|Don't forget to update \c{manifest} values| After \l{#dont-from-scratch generating the project template with \c{bdep-new}}, don't forget to update at least the key values in the generated \c{manifest}: \l{#dont-forget-update-manifest-version \c{version}}, \l{#dont-forget-update-manifest-license \c{license}}, and \l{#dont-forget-update-manifest-summary \c{summary}}. \h2#dont-forget-update-manifest-version|Don't forget to update \c{manifest} value \c{version}| For \c{version}, use the upstream version directly if it is semver (or semver-like, that is, has three version components). Otherwise, 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?} \h2#dont-forget-update-manifest-license|Don't forget to update \c{manifest} value \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 and the list of the most commonly used SPDX license IDs. \h2#dont-forget-update-manifest-summary|Don't forget to update \c{manifest} value \c{summary}| For \c{summary} use a brief description of the functionality provided by the package. 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 advantage. If upstream does not offer a sensible summary, the following template is recommended for libraries: \ summary: C library summary: 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: \ summary: libigl's 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 \ \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} build 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} build. There are, however, 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 be always used, then it may be tempting to support both modes potentially 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 do it is to work with upstream to fix any issues that prevent its use in \c{build2}. There are, however, reasons why supporting both mode 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 and its common for applications to only use a small subset of them. On the other hand, compiling all of them in the compiled mode takes a substantial amount of time. (Note that this can also be addressed by making the presence of optional components user-configurable.)| | \h#dont-main-target-root-buildfile|Don't build your main targets in root \c{buldfile}| 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 a bad idea except for the simplest projects. Firstly, this quickly gets messy since you have to combine managing \c{README}s, \c{LICENSE}s, and subdirectories with you main target builds. But, more importantly, this means that when you 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/} subproject, which is wasteful. Note also that it's easy to continue symlinking entire directories from \c{upstream/} without moving everything to the root \c{buildfile} by simply creating another subdirectory. Let's look at a concrete example. Here is the directory structure where everything 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 \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 \l{bdep-new(1)} invocation that can be used to automatically create this alternative structure (see \l{bdep-new.xhtml#src-layout SOURCE LAYOUT} for details): \ $ bdep new -t lib,prefix=libigl-core,no-subdir,no-version libigl-core \ \h1#howto|Packaging HOWTO| @@ howto make smoke test (and fix ref) \h#howto-bad-inclusion-practice|How do I deal with bad header inclusion practice| This sections explains how to deal with libraries that include their public, generically-named headers without a library name as directory prefix. Such libraries cannot coexist, neither in the same build nor when installed. For background and details, see \l{intro#proj-struct Canonical Project Structure}. @@ TODO \h#howto-extra-header-install-subdir|How do I handle extra header installation subdirectory| This sections explains how to handle an additional header installation subdirectory. @@ TODO \h1#faq|Packaging FAQ| \h#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 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 ... \ || "