diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2018-05-07 16:20:33 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2018-05-07 16:20:33 +0200 |
commit | fb5712f17df87bc2be127c094fd9ecc3ac0a6c97 (patch) | |
tree | 1baa1e0b2560e7ac6a06247162241e93017413c9 /doc/intro2.cli | |
parent | 1c9421280f513b3662f00089b4f22eaa6de33fea (diff) |
Rename intro2 to intro, old intro to intro1
Diffstat (limited to 'doc/intro2.cli')
-rw-r--r-- | doc/intro2.cli | 1390 |
1 files changed, 0 insertions, 1390 deletions
diff --git a/doc/intro2.cli b/doc/intro2.cli deleted file mode 100644 index bb10b1d..0000000 --- a/doc/intro2.cli +++ /dev/null @@ -1,1390 +0,0 @@ -// file : doc/intro2.cli -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -"\name=build2-toolchain-intro" -"\subject=toolchain" -"\title=Toolchain Introduction" - -// TODO -// -// @@ refs to further docs -// -// STYLE -// -// @@ section boundary page breaks (<hr class="page-break"/>) -// @@ when printed, code background is gone, but spaces still there -// -// PDF -// -// @@ tree output is garbled -// @@ Could we use a nicer font, seeing that we embed them? -// - -// NOTES -// -// - Maximum <pre> line is 70 characters. -// - -" -\h1#tldr|TL;DR| - -\ -$ git clone ssh://example.org/hello.git -$ tree hello -hello/ -├── hello/ -│ ├── hello.cxx -│ └── buildfile -├── manifest -└── repositories.manifest - -$ cd hello -$ bdep init --config-create ../hello-gcc cc config.cxx=g++ -initializing project /tmp/hello/ -created configuration /tmp/hello-gcc/ (default, auto-synchronized) -synchronizing: - new hello/0.1.0 - -$ b -c++ hello/cxx{hello}@../hello-gcc/hello/hello/ -ld ../hello-gcc/hello/hello/exe{hello} -ln ../hello-gcc/hello/hello/exe{hello} -> hello/ - -$ hello/hello World -Hello, World! - -$ edit repositories.manifest # add https://example.org/libhello.git -$ edit manifest # add 'depends: libhello ^1.0.0' -$ edit hello/buildfile # import libhello -$ edit hello/hello.cxx # use libhello - -$ b -fetching from https://example.org/libhello.git -synchronizing /tmp/hello-gcc/: - new libhello/1.0.0 (required by hello) - reconfigure hello/0.1.0 -c++ ../hello-gcc/libhello-1.0.0/libhello/cxx{hello} -ld ../hello-gcc/libhello-1.0.0/libhello/libs{hello} -c++ hello/cxx{hello}@../hello-gcc/hello/hello/ -ld ../hello-gcc/hello/hello/exe{hello} -ln ../hello-gcc/hello/hello/exe{hello} -> hello/ - -$ bdep fetch # refresh available versions -$ bdep status -i # review available versions -hello configured 0.1.0 - libhello ^1.0.0 configured 1.0.0 available [1.1.0] - -$ bdep sync libhello # upgrade to latest -synchronizing: - new libformat/1.0.0 (required by libhello) - new libprint/1.0.0 (required by libhello) - upgrade libhello/1.1.0 - reconfigure hello/0.1.0 - -$ bdep sync libhello/1.0.0 # downgrade -synchronizing: - drop libprint/1.0.0 (unused) - drop libformat/1.0.0 (unused) - downgrade libhello/1.0.0 - reconfigure hello/0.1.0 -\ - -\h1#guide|Getting Started Guide| - -The aim of this guide is to get you started developing C/C++ projects with the -\c{build2} toolchain. All the examples in this section include the relevant -command output so if you just want to get a sense of what \c{build2} is about, -then you don't have to install the toolchain and run the commands in order to -follow along. If at the end you find \c{build2} appealing and would like to -start using it or try the examples for yourself, you can jump straight to -\l{build2-toolchain-install.xhtml The \c{build2} Toolchain Installation and -Upgrade}. - -One of the primary goals of the \c{build2} toolchain is to provide a uniform -interface across all the platforms and compilers. While the examples in this -document assume a UNIX-like operation system, they will look pretty similar if -you are on Windows. You just have to use appropriate paths, compilers, and -options. - -The question we will try to answer in this section can be summarized as: - -\ -$ git clone .../hello.git && now-what? -\ - -That is, we clone an existing C/C++ project or would like to create a new one -and then start hacking on it. We want to spend as little time and energy as -possible on the initial and ongoing infrastructure maintenance: setting up -build configurations, managing dependencies, continuous integration and -testing, release management, etc. Or, as one C++ user aptly put it, \"\i{All I -want to do is program.}\" - -\h#guide-hello|Hello, World| - -Let's see what programming with \c{build2} feels like by starting with a -customary \i{\"Hello, World!\"} program (here we assume our current working -directory is \c{/tmp}): - -\ -$ bdep new -t exe -l c++ hello -created new executable project hello in /tmp/hello/ -\ - -The \l{bdep-new(1)} command creates a \i{canonical} \c{build2} project. In -our case it is an executable implemented in C++. - -\N|To create a library, pass \c{-t\ lib}. By default \c{new} also initializes -a \c{git} repository and generates suitable \c{.gitignore} files (pass \c{-s\ -none} if you don't want that).| - -Let's take a look inside our new project: - -\ -$ tree hello -hello/ -├── .git/ -├── .bdep/ -├── build/ -├── hello/ -│ ├── hello.cxx -│ ├── buildfile -│ └── testscript -├── buildfile -├── manifest -└── repositories.manifest -\ - -\N|While the canonical project structure is strongly recommended, especially -for new projects, \c{build2} is flexible enough to allow most commonly used -arrangements.| - -Similar to version control tools, we normally run all \c{build2} tools from -the project's source directory or one of its subdirectories, so: - -\ -$ cd hello -\ - -While the project layout is discussed in more detail in later sections, let's -examine a couple of interesting files to get a sense of what's going on. We -start with the source file which should look familiar: - -\ -$ cat hello/hello.cxx - -#include <iostream> - -using namespace std; - -int main (int argc, char* argv[]) -{ - if (argc < 2) - { - cerr << \"error: missing name\" << endl; - return 1; - } - - cout << \"Hello, \" << argv[1] << '!' << endl; -} -\ - -\N|If you prefer the \c{.?pp} extensions over \c{.?xx} for your C++ source -files, pass \c{-l\ c++,cpp} to the \c{new} command. See \l{bdep-new(1)} for -details on this and other customization options.| - -Let's take a look at the accompanying \c{buildfile}: - -\ -$ cat hello/buildfile - -libs = -#import libs += libhello%lib{hello} - -exe{hello}: {hxx ixx txx cxx}{*} $libs test{testscript} -\ - -As the name suggests, this file describes how to build things. While its -content might look a bit cryptic, let's try to infer a couple of points -without going into too much detail (the details are discussed in the following -sections). That \c{exe{hello\}} on the left of \c{:} is a \i{target} -(executable named \c{hello}) and what we have on the right are -\i{prerequisites} (C++ source files, libraries, etc). This \c{buildfile} uses -\l{b#name-patterns wildcard patterns} (that \c{*}) to automatically locate all -the C++ source files. This means we don't have to edit our \c{buildfile} every -time we add a source file to our project. There also appears to be some -(commented out) infrastructure for importing and linking libraries (that -\c{libs} variable). We will see how to use it in a moment. Finally, the -\c{buildfile} also lists \c{testscript} as a prerequisite of \c{hello}. This -file tests our target. Let's take a look inside: - -\ -$ cat hello/testscript - -: basics -: -$* 'World' >'Hello, World!' - -: missing-name -: -$* 2>>EOE != 0 -error: missing name -EOE -\ - -Again, we are not going into detail here (see \l{testscript#intro Testscript -Introduction} for a proper introduction), but to give you an idea, here we -have two tests: the first (with id \c{basics}) verifies that our program -prints the expected greeting while the second makes sure it handles the -missing name error condition. Tests written in Testscript are concise, -portable, and executed in parallel. - -Next up is \c{manifest}: - -\ -$ cat manifest -: 1 -name: hello -version: 0.1.0-a.0.z -summary: hello executable project -license: proprietary -url: https://example.org/hello -email: you@example.org -#depends: libhello ^1.0.0 -\ - -The \c{manifest} file is what makes a build system project a \i{package}. It -contains all the metadata that a user of a package might need to know: its -name, version, license, dependencies, etc., all in one place. - -\N|Refer to \l{bpkg#manifest-format Manifest Format} for the general format of -\c{build2} manifest files and to \l{bpkg#manifest-package Package Manifest} -for details on the package manifest values.| - -As you can see, \c{manifest} created by \l{bdep-new(1)} contains some dummy -values which you would want to adjust before publishing your package. But -let's resist the urge to adjust that strange looking \c{0.1.0-a.0.z} until we -discuss package versioning. - -\N|Next to \c{manifest} you might have noticed the \c{repositories.manifest} -file \- we will discuss its function later, when we talk about dependencies -and where they come from.| - -Project in hand, let's build it. Unlike other programming languages, C++ -development usually involves juggling a handful of build configurations: -several compilers and/or targets (\c{build2} is big on cross-compiling), -debug/release, different sanitizers and/or static analysis tools, and so -on. As a result, \c{build2} is optimized for multi-configuration -usage. However, as we will see shortly, one build configuration can be -designated as the default with additional conveniences. - -The \l{bdep-init(1)} command is used to initialize a project in a build -configuration. As a shortcut, it can also create a new build configuration in -the process, which is just what we need here. Let's start with GCC (remember -we are in the project's root directory): - -\ -$ bdep init -C ../hello-gcc @gcc cc config.cxx=g++ -initializing project /tmp/hello/ -created configuration @gcc /tmp/hello-gcc/ (default, auto-synchronized) -synchronizing: - new hello/0.1.0-a.0.19700101000000 -\ - -The \cb{--create|-C} option instructs \c{init} to create a new configuration -in the specified directory (\c{../hello-gcc} in our case). To make referring -to configurations easier, we can give it a name, which is what we do with -\c{@gcc}. The next argument (\c{cc}, stands for \i{C-common}) is the build -system module we would like to configure. It implements compilation and -linking rules for the C and C++ languages. Finally, \c{config.cxx=g++} is (one -of) this module's configuration variables that specifies the C++ compiler we -would like to use (the corresponding C compiler will be determined -automatically). Let's for now also ignore that \c{synchronizing:...} bit along -with strange-looking \c{19700101000000} in the version \- it will become clear -what's going on here in a moment. - -Now the same for Clang: - -\ -$ bdep init -C ../hello-clang @clang cc config.cxx=clang++ -initializing project /tmp/hello/ -created configuration @clang /tmp/hello-clang/ (auto-synchronized) -synchronizing: - new hello/0.1.0-a.0.19700101000000 -\ - -If we check the parent directory, we should now see two build configurations -next to our project: - -\ -$ ls .. -hello/ -hello-gcc/ -hello-clang/ -\ - -Things will also look pretty similar if you are on Windows instead of a -UNIX-like operating system. For example, to initialize our project on Windows -with Visual Studio, start the Visual Studio development command prompt and -then run: - -\N|Currently we have to run \c{build2} tools from a suitable Visual Studio -development command prompt. This requirement will likely be removed in the -future.| - -\ -> bdep init -C ..\hello-debug @debug cc ^ - config.cxx=cl ^ - \"config.cc.coptions=/MDd /Z7\" ^ - config.cc.loptions=/DEBUG - -> bdep init -C ..\hello-release @release cc ^ - config.cxx=cl ^ - config.cc.coptions=/O2 -\ - -\N|Besides the \c{coptions} (compile options) and \c{loptions} (link options), -other commonly used \c{cc} module configuration variables are \c{poptions} -(preprocess options) and \c{libs} (extra libraries to link). We can also use -their \c{config.c.*} (C compilation) and \c{config.cxx.*} (C++ compilation) -variants if we only want them applied during the respective language -compilation. For example: - -\ -$ bdep init ... cc \ - config.cxx=clang++ \ - config.cc.coptions=-g \ - config.cxx.coptions=-stdlib=libc++ -\ - -| - -One difference you might have noticed when creating the \c{gcc} and \c{clang} -configurations above is that the first one was designated as the default. The -default configuration is used by \c{bdep} commands if no configuration is -specified explicitly (see \l{bdep-projects-configs(1)} for details). It is -also the configuration that is used if we run the build system in the -project's source directory. So, normally, you would make your every day -development configuration the default. Let's try that: - -\ -$ bdep status -hello configured 0.1.0-a.0.19700101000000 - -$ b -c++ hello/cxx{hello}@../hello-gcc/hello/hello/ -ld ../hello-gcc/hello/hello/exe{hello} -ln ../hello-gcc/hello/hello/exe{hello} -> hello/ - -$ b test -test hello/test{testscript} ../hello-gcc/hello/hello/exe{hello} - -$ hello/hello World -Hello, World! -\ - -In contrast, the Clang configuration has to be requested explicitly: - -\ -$ bdep status @clang -hello configured 0.1.0-a.0.19700101000000 - -$ b ../hello-clang/hello/ -c++ hello/cxx{hello}@../hello-clang/hello/hello/ -ld ../hello-clang/hello/hello/exe{hello} - -$ b test: ../hello-clang/hello/ -test hello/test{testscript} ../hello-clang/hello/hello/exe{hello} - -$ ../hello-clang/hello/hello/hello World -Hello, World! -\ - -\N|To see the actual compilation command lines, run \c{b\ -v} and for even -more details, run \c{b\ -V}. See \l{b(1)} for more information on these -and other build system options.| - -While we are here, let's also check how hard it would be to cross-compile: - -\ -$ bdep init -C ../hello-mingw @mingw cc config.cxx=x86_64-w64-mingw32-g++ -initializing project /tmp/hello/ -created configuration @mingw /tmp/hello-mingw/ (auto-synchronized) -synchronizing: - new hello/0.1.0-a.0.19700101000000 - -$ b ../hello-mingw/hello/ -c++ hello/cxx{hello}@../hello-mingw/hello/hello/ -ld ../hello-mingw/hello/hello/exe{hello} -\ - -As you can see, cross-compiling in \c{build2} is nothing special. In our case, -on a properly setup GNU/Linux machine (that automatically uses \c{wine} as an -\c{.exe} interpreter) we can even run tests (in \c{build2} this is called -\i{cross-testing}): - -\ -$ b test: ../hello-mingw/hello/ -test hello/test{testscript} ../hello-mingw/hello/hello/exe{hello} - -$ ../hello-mingw/hello/hello/hello.exe Windows -Hello, Windows! -\ - -Let's review what it takes to initialize a project's infrastructure and -perform the first build. For an existing project: - -\ -$ git clone .../hello.git -$ cd hello -$ bdep init -C ../hello-gcc @gcc cc config.cxx=g++ -$ b -\ - -For a new project: - -\ -$ bdep new -t exe -l c++ hello -$ cd hello -$ bdep init -C ../hello-gcc @gcc cc config.cxx=g++ -$ b -\ - -If you prefer, the \c{new} and \c{init} steps can be combined into a single -command: - -\ -$ bdep new -t exe -l c++ hello -C hello-gcc @gcc cc config.cxx=g++ -\ - -Now is also a good time to get an overview of the \c{build2} toolchain. After -all, we have already used two of its tools (\c{bdep} and \c{b}) without a -clear understanding of what they actually are. - -Unlike most other programming languages that encapsulate the build system, -package dependency manager, and project dependency manager into a single tool -(such as Rust's \c{cargo} or Go's \c{go}), \c{build2} is a hierarchy of -several tools that you will be using directly and which together with your -version control system (VCS) will constitute the core of your project -management toolset. - -\N|While \c{build2} can work without a VCS, this will result in reduced -functionality.| - -At the bottom of the hierarchy is the build system, \l{b(1)}. Next comes the -package dependency manager, \l{bpkg(1)}. It is primarily used for \i{package -consumption} and depends on the build system. The top of the hierarchy is the -project dependency manager, \l{bdep(1)}. It is used for \i{project -development} and relies on \c{bpkg} for building project packages and their -dependencies. - -\N|The main reason for this separation is modularity and the resulting -flexibility: there are situations where we only need the build system (for -example, when building a package for a system package manager where all the -dependencies should be satisfied from the system repository), or only the -build system and package manager (for example, when a build bot is building a -package for testing). - -Note also that strictly speaking \c{build2} is not C/C++-specific; its build -model is general enough to handle any DAG-based operations and its -package/project dependency management can be used for any compiled language.| - -\N|As we will see in a moment, \c{build2} also integrates with your VCS in -order to automate project versioning. Note that currently only \c{git(1)} is -supported.| - -Let's now move on to the reason why there is \i{dep} in the \c{bdep} name: -dependency management. - - -\h#guide-repositories|Package Repositories| - -Say we have realized that writing \i{\"Hello, World!\"} programs is a fairly -common task and that someone must have written a library to help with that. So -let's see if we can find something suitable to use in our project. - -Where should we look? That's a good question. But before we can try to answer -it, we need to understand where \c{build2} can source dependencies. In -\c{build2} packages come from \i{package repositories}. Two commonly used -repository types are \i{version control} and \i{archive}-based (see -\l{bpkg-repository-types(1)} for details). - -As the name suggests, a version control-based repository uses a VCS as its -distribution mechanism. \N{Currently, only \c{git} is supported.} Such a -repository normally contains multiple versions of a single package or, -perhaps, of a few related packages. - -An archive-based repository contains multiple, potentially unrelated -packages/versions as archives along with some meta information (package list, -prerequisite/complement repositories, signatures, etc) that are all accessible -via HTTP(S). - -Version control and archive-based repositories have different -trade-offs. Version control-based repositories are great for package -developers: With services like GitHub they are trivial to setup. In fact, your -project's (already existing) VCS repository will normally be the \c{build2} -package repository \- you might need to add a few files, but that's about it. - -However, version control-based repositories are not without drawbacks: It will -be hard for your users to discover your packages (try searching for \"hello -library\" on GitHub \- most of the results are not even in C++ let alone -packaged for \c{build2}). There is also the issue of continuous availability: -users can delete their repositories, services may change their policies or go -out of business, and so on. Version control-based repositories also lack -repository authentication and package signing. Finally, obtaining the -available package list for such repositories can be slow. - -A central, archive-based repository would address all these drawbacks: It -would be a single place to search for packages. Published packages will never -disappear and can be easily mirrored. Packages are signed and the repository -is authenticated (see \l{bpkg-repository-signing(1)} for details). And, last, -but not least, archive-based repositories are fast. - -\l{https://cppget.org cppget.org} is the \c{build2} community's central -package repository (which we hope one day will become \i{the C++ package -repository}). As an added benefit, packages on \l{https://cppget.org -cppget.org} are continuously \l{https://cppget.org/?builds built and tested} on -all the major platform/compiler combinations with the results available as -part of the package description. - -\N|The main drawback of archive-based repositories is the setup cost. Getting -a basic repository going is relatively easy \- all you need is an HTTP(S) -server. Adding a repository web interface like that on \l{https://cppget.org -cppget.org} will require running \l{https://cppget.org/brep \c{brep}}. And -adding CI will require running a bunch of build bots -(\l{https://cppget.org/bbot \c{bbot}}).| - -\N|CI support for version control-based repositories is a work in progress.| - -To summarize, version control-based repositories are great for package -developers while a central, archive-based repository is convenient for package -consumers. A reasonable strategy is then for package developers to publish -their releases to a central repository. Package consumers can then decide -which repository to use based on their needs. For example, one could use -\l{https://cppget.org cppget.org} as a (fast, reliable, and secure) source of -stable versions but also add, say, \c{git} repositories for select packages -(perhaps with the \c{#HEAD} fragment filter to improve download speed) for -testing development snapshots. In this model the two repository types -complement each other. - -\N|Support for automated publishing of tagged releases to an archive-based -repository is a work in progress.| - -Let's see how all this works in practice. Go over to \l{https://cppget.org -cppget.org} and type \"hello library\" in the search box. At the top of the -search result you should see the \l{https://cppget.org/libhello \c{libhello}} -package and if you follow the link you will see the package description page -along with a list of available versions. Pick a version that you like and you -will see the package version description page with quite a bit of information, -including the list of platform/compiler combinations that this version has -been successfully (or unsuccessfully) tested with. If you like what you see, -copy the \c{location} value \- this is the repository location where this -package version can be sourced from. - -\N|The \l{https://cppget.org cppget.org} repository is split into several -sections: \c{stable}, \c{testing}, \c{beta}, \c{alpha} and \c{legacy}, with -each section having its own repository location (see the repository's -\l{https://cppget.org/?about about} page for details on each section's -policies). Note also that \c{testing} is complemented by \c{stable}, \c{beta} -by \c{testing}, and so on, so you only need to choose the lowest stability -level and you will automatically \"see\" packages from the more stable -sections.| - -\N|The \l{https://cppget.org cppget.org} \c{stable} sections will always -contain the \c{libhello} library version \c{1.0.X} that was generated using -the following \l{bdep-new(1)} command line: - -\ -$ bdep new -t lib -l c++ libhello -\ - -It can be used as a predictable test dependency when setting up new projects.| - -Let's say we've visited the \c{libhello} project's -\l{https://git.build2.org/cgit/hello/libhello/ home page} (for example by -following a link from the package details page) and noticed that it is being -developed in a \c{git} repository. How can we see what's available there? If -the releases are tagged, then we can infer the available released versions -from the tags. But that doesn't tell us anything about what's happening on the -\c{HEAD} or in the branches. For that we can use the package manager's -\l{bpkg-rep-info(1)} command: - -\ -$ bpkg rep-info https://git.build2.org/hello/libhello.git -libhello/1.0.0 -libhello/1.1.0 -\ - -As you can see, besides \c{1.0.0} that we have seen on \c{cppget.org/stable}, -there is also \c{1.1.0} (which is perhaps being tested in -\c{cppget.org/testing}). We can also check what might be available on the -\c{HEAD} (see \l{bpkg-repository-types(1)} for details on the \c{git} -repository URL format): - -\ -$ bpkg rep-info https://git.build2.org/hello/libhello.git#HEAD -libhello/1.1.1-a.0.20180504111511.2e82f7378519 -\ - -\N|We can also use the \c{rep-info} command on archive-based repositories, -however, if available, the web interface is usually more convenient and -provides more information.| - -To summarize, we found two repositories for the \c{libhello} package: the -archive-based \l{https://cppget.org cppget.org} that contains the released -versions as well as its development \c{git} repository where we can get the -bleeding edge stuff. Let's now see how we can add \c{libhello} to our -project. - - -\h#guide-add-remove-deps|Adding and Removing Dependencies| - -So we found \c{libhello} that we would like to use in our \c{hello} -project. First, we edit the \c{repositories.manifest} file found in the root -directory of our project and add one of the \c{libhello} repositories as a -prerequisite. Let's start with \l{https://cppget.org cppget.org}: - -\ -role: prerequisite -location: https://pkg.cppget.org/1/stable -\ - -\N|Refer to \l{bpkg#manifest-repository Repository Manifest} for details on -the repository manifest values.| - -Next, we edit the \c{manifest} file (again, found in the root of our project) -and specify the dependency on \c{libhello} with optional version constraint. -For example: - -\ -depends: libhello ^1.0.0 -\ - -Let's briefly discuss version constraints (for details see the -\l{bpkg#manifest-package-depends \c{depends}} value documentation). A version -constraint can be expressed with a comparison operator (\c{==}, \c{>}, -\c{<}, \c{>=}, \c{<=}), a range shortcut operator (\c{~} and \c{^}), or a -range. Here are a few examples: - -\ -depends: libhello == 1.2.3 -depends: libhello >= 1.2.3 - -depends: libhello ~1.2.3 -depends: libhello ^1.2.3 - -depends: libhello [1.2.3 1.2.9) -\ - -You may already be familiar with the tilde (\c{~}) and caret (\c{^}) -constraints from dependency managers for other languages. To recap, tilde -allows upgrades to any further patch versions while caret also allows upgrades -to further minor versions. They are equivalent to the following ranges: - -\ -~X.Y.Z [X.Y.Z X.Y+1.0) - -^X.Y.Z [X.Y.Z X+1.0.0) if X > 0 -^0.Y.Z [0.Y.Z 0.Y+1.0) if X == 0 -\ - -\N|Zero major version component is customarily used during early development -where the minor version effectively becomes major. As a result, the tilde -constraint has a special treatment of this case.| - -Unless you have good reasons not to (for example, a dependency does not use -semantic versioning), we suggest that you use the \c{^} constraint which -provides a good balance between compatibility and upgradability with \c{~} -being a more conservative option. - -Ok, we've specified where our package comes from (\c{repositories.manifest}) -and which versions we find acceptable (\c{manifest}). The next step is to edit -\c{hello/buildfile} and import the \c{libhello} library into our build: - -\ -import libs += libhello%lib{hello} -\ - -Finally, we modify our source code to use the library: - -\ -#include <libhello/hello.hxx> -... - -int main (int argc, char* argv[]) -{ - ... - hello::say_hello (cout, argv[1]); -} -\ - -\N|You are probably wondering why we have to specify this repeating -information in so many places. Let's start with the source code: we can't -specify the version constraint or location there because it will have to be -repeated in every source file that uses the dependency. - -Moving up, \c{buildfile} is also not a good place to specify this information -for the same reason (a library can be imported in multiple buildfiles) plus -the build system doesn't really know anything about version constraints or -repositories which is the purview of the dependency management tools. - -Finally, we have to separate the version constraint and the location because -the same package can be present in multiple repositories with different -policies. For example, when a package from a version control-based repository -is published in an archive-based repository, its \c{repositories.manifest} -file is ignored and all its dependencies should be available from the -archive-based repository itself (or its fixed set of prerequisite -repositories). In other words, \c{manifest} belongs to a package while -\c{repositories.manifest} \- to a repository. - -Also note that this is unlikely to become burdensome since adding new -dependencies is not something that happens often. There are also plans to -automate this with a \c{bdep-add(1)} command in the future.| - -To summarize, these are the files we had to modify to add a dependency -to our project: - -\ -repositories.manifest # add https://pkg.cppget.org/1/stable -manifest # add 'depends: libhello ^1.0.0' -buildfile # import libhello -hello.cxx # use libhello -\ - -With a new dependency added, let's check the status of our project: - -\ -$ bdep status -fetching pkg:cppget.org/stable (prerequisite of dir:/tmp/hello) -warning: authenticity of the certificate for pkg:cppget.org/stable - cannot be established -certificate is for cppget.org, \"Code Synthesis\" <admin@cppget.org> -certificate SHA256 fingerprint: -86:BA:D4:DE:2C:87:1A:EE:38:<...>:5A:EA:F4:F7:8C:1D:63:30:C6 -trust this certificate? [y/n] y - -hello configured 0.1.0-a.0.19700101000000 - available 0.1.0-a.0.19700101000000#1 -\ - -The \l{bdep-status(1)} command has detected that the dependency information -has changed and tells us that a new \i{iteration} of our project (that \c{#1}) -is now available for \i{synchronization} with the build configuration. - -We've also been prompted to authenticate the prerequisite repository. This -will have to happen once for every build configuration we initialize our -project in and can quickly become tedious. To overcome this, we can mention -the certificate fingerprint that we wish to automatically trust in the -\c{repositories.manifest} file (replace it with the actual fingerprint from -the repository's about page): - -\ -role: prerequisite -location: https://pkg.cppget.org/1/stable -trust: 86:BA:D4:DE:2C:87:1A:EE:38:<...>:5A:EA:F4:F7:8C:1D:63:30:C6 -\ - -To synchronize a project with one or more build configurations we use the -\l{bdep-sync(1)} command: - -\ -$ bdep sync -synchronizing: - new libhello/1.0.0 (required by hello) - upgrade hello/0.1.0-a.0.19700101000000#1 -\ - -Or we could just build the project without an explicit \c{sync} \- if -necessary, it will be automatically synchronized: - -\ -$ b -synchronizing: - new libhello/1.0.0 (required by hello) - upgrade hello/0.1.0-a.0.19700101000000#1 -c++ ../hello-gcc/libhello-1.0.0/libhello/cxx{hello} -ld ../hello-gcc/libhello-1.0.0/libhello/libs{hello} -c++ hello/cxx{hello}@../hello-gcc/hello/hello/ -ld ../hello-gcc/hello/hello/exe{hello} -ln ../hello-gcc/hello/hello/exe{hello} -> hello/ -\ - -The synchronization as performed by the \c{sync} command is two-way: -dependency packages are first added, removed, upgraded, or downgraded in build -configurations according to the project's version constraints and user -input. Then the actual versions of the dependencies present in the build -configurations are recorded in the project's \c{lockfile} so that if desired, -the build can be reproduced exactly. \N{The \c{lockfile} functionality is not -yet implemented.} For a new dependency the latest available version that -satisfies the version constraint is used. - -\N|Synchronization is also the last step in the \l{bdep-init(1)} command's -logic.| - -Let's now examine the status in all (\c{--all|-a}) the build configurations -and include the immediate dependencies (\c{--immediate|-i}): - -\ -$ bdep status -ai -in configuration @gcc: -hello configured 0.1.0-a.0.19700101000000#1 - libhello ^1.0.0 configured 1.0.0 - -in configuration @clang: -hello configured 0.1.0-a.0.19700101000000 - available 0.1.0-a.0.19700101000000#1 -\ - -Since we didn't specify a configuration explicitly, only the default (\c{gcc}) -was synchronized. Normally, you would try a new dependency in one -configuration, make sure everything looks good, then synchronize the rest with -\c{--all|-a} (or, again, just build what you need directly). Here are a few -examples (see \l{bdep-projects-configs(1)} for details): - -\ -$ bdep sync -a -$ bdep sync @gcc @clang -$ bdep sync -c ../hello-mingw -\ - -To get rid of a dependency, we simply remove it from the \c{manifest} file -and synchronize the project. For example, assuming \c{libhello} is no longer -mentioned as a dependency in our \c{manifests}: - -\ -$ bdep status -hello configured 0.1.0-a.0.19700101000000#1 - available 0.1.0-a.0.19700101000000#2 - -$ bdep sync -synchronizing: - drop libhello/1.0.0 (unused) - upgrade hello/0.1.0-a.0.19700101000000#2 -\ - - -\h#guide-upgrade-downgrade-deps|Upgrading and Downgrading Dependencies| - -Let's say we would like to try that \c{1.1.0} version we have seen in -the \c{libhello} \c{git} repository. First, we need to add the -repository to the \c{repositories.manifest} file: - -\ -role: prerequisite -location: https://git.build2.org/hello/libhello.git -\ - -\N|Note that we don't need the \c{trust} value since \c{git} repositories -are not authenticated.| - -To refresh the list of available dependency versions we use the -\l{bdep-fetch(1)} command (or the \c{--fetch|-f} option to \c{status}): - -\ -$ bdep fetch -$ bdep status libhello -libhello configured 1.0.0 available [1.1.0] -\ - -To upgrade (or downgrade) dependencies we again use the \l{bdep-sync(1)} -command. We can upgrade one or more specific dependencies by listing them -as arguments to \c{sync}: - -\ -$ bdep sync libhello -synchronizing: - new libformat/1.0.0 (required by libhello) - new libprint/1.0.0 (required by libhello) - upgrade libhello/1.1.0 - upgrade hello/0.1.0-a.0.19700101000000#3 -\ - -Without an explicit version or the \c{--patch|-p} option, \c{sync} will -upgrade the specified dependencies to the latest available versions. For -example, if we don't like version \c{1.1.0}, we can downgrade it back to -\c{1.0.0} by specifying the version explicitly (we pass \c{--old-available|-o} -to \c{status} to see the old versions): - -\ -$ bdep status -o libhello -libhello configured 1.1.0 available (1.1.0) [1.0.0] - -$ bdep sync libhello/1.0.0 -synchronizing: - drop libprint/1.0.0 (unused) - drop libformat/1.0.0 (unused) - downgrade libhello/1.0.0 - reconfigure hello/0.1.0-a.0.19700101000000#3 -\ - -\N|The available versions are listed in the descending order with \c{[]} -indicating that the version is only available as a dependency and \c{()} -marking the current version.| - -Instead of specific dependencies we can also upgrade (\c{--upgrade|-u}) or -patch (\c{--patch|-p}) immediate (\c{--immediate|-i}) or all -(\c{--recursive|-r}) dependencies of our project. - -As a more realistic example, version \c{1.1.0} of \c{libhello} depends on two -other libraries: \c{libformat} and \c{libprint}. Here is our project's -dependency tree while we were still using that version: - -\ -$ bdep status -r -hello configured 0.1.0-a.0.19700101000000#3 - libhello ^1.0.0 configured 1.1.0 - libformat ^1.0.0 configured 1.0.0 - libprint ^1.0.0 configured 1.0.0 -\ - -A typical conservative dependency management workflow would look like this: - -\ -$ bdep status -fi # refresh and examine immediate dependencies -hello configured 0.1.0-a.0.19700101000000#3 - libhello configured 1.1.0 available [2.0.0] [1.2.0] [1.1.2] [1.1.1] - -$ bdep sync -pi # upgrade immediate to latest patch version -synchronizing: - upgrade libhello/1.1.2 - reconfigure hello/0.1.0-a.0.19700101000000#3 -continue? [Y/n] y -\ - -Notice that in case of such mass upgrades you are prompted for confirmation -before anything is actually changed (unless you pass \c{--yes|-y}). - -In contrast, the following would be a fairly aggressive workflow where we -upgrade everything to the latest available version (version constraints -permitting; here we assume \c{^1.0.0} was used for all the dependencies): - -\ -$ bdep status -fr # refresh and examine all dependencies -hello configured 0.1.0-a.0.19700101000000#3 - libhello configured 1.1.0 available [2.0.0] [1.2.0] [1.1.1] - libprint configured 1.0.0 available [2.0.0] [1.1.0] [1.0.1] - libformat configured 1.0.0 available [2.0.0] [1.1.0] [1.0.1] - -$ bdep sync -ur # upgrade all to latest available version -synchronizing: - upgrade libprint/1.1.0 - upgrade libformat/1.1.0 - upgrade libhello/1.2.0 - reconfigure hello/0.1.0-a.0.19700101000000#3 -continue? [Y/n] y -\ - -We can also have something in between: patch all (\c{sync\ -pr}), upgrade -immediate (\c{sync\ -ui}), or even upgrade immediate and patch the rest -(\c{sync\ -ui} followed by \c{sync\ -pr}). - - -\h#guide-versioning-releasing|Versioning and Release Management| - -Let's now discuss versioning and release management and, yes, that -strange-looking \c{0.1.0-a.0.19700101000000} we keep seeing. While a build -system project doesn't need a version and a \c{bpkg} package can use custom -versioning schemes (see \l{bpkg#package-version Package Version}), a project -managed by \c{bdep} must use \i{standard versioning}. \N{A dependency, which -is a \c{bpkg} package, need not use standard versioning.} - -Standard versioning (\i{stdver}) is a \l{https://semver.org semantic -versioning} (\i{semver}) scheme with a more precisely defined pre-release -component and without any build metadata. - -\N|If you believe that \i{semver} is just \c{\i{major}.\i{minor}.\i{patch}}, -then in your worldview \i{stdver} would be the same as \i{semver}. In reality, -\i{semver} also allows loosely defined pre-release and build metadata -components. For example, \c{1.2.3-beta.1+build.23456} is a valid \i{semver}.| - -A standard version has the following form: - -\c{\i{major}\b{.}\i{minor}\b{.}\i{patch}[\b{-}\i{prerel}]} - -The \ci{major}, \ci{minor}, and \ci{patch} components have the same meaning as -in \i{semver}. The \ci{prerel} component is used to provide \i{continuous -versioning} of our project between releases. Specifically, during development -of a new version we may want to publish several pre-releases, for example, -alpha or beta. In between those we may also want to publish a number of -snapshots, for example, for CI. With continuous versioning all these releases, -pre-releases, and snapshots are assigned unique, properly ordered versions. - -\N|Continuous versioning is a cornerstone of the \c{build2} project dependency -management. In case of snapshots, an appropriate version is assigned -automatically in cooperation with your VCS.| - -The \ci{prerel} component for a pre-release has the following form: - -\c{(\b{a}|\b{b})\b{.}\i{num}} - -Here \cb{a} stands for alpha, \cb{b} stands for beta, and \ci{num} is the -alpha/beta number. For example: - -\ -1.1.0 # final release for 1.1.0 -1.2.0-a.1 # first alpha pre-release for 1.2.0 -1.2.0-a.2 # second alpha pre-release for 1.2.0 -1.2.0-b.1 # first beta pre-release for 1.2.0 -1.2.0 # final release for 1.2.0 -\ - -The \ci{prerel} component for a snapshot has the following form: - -\c{(\b{a}|\b{b})\b{.}\i{num}\b{.}\i{snapsn}[\b{.}\i{snapid}]} - -Where \ci{snapsn} is the snapshot sequence number and \ci{snapid} is -the snapshot id. In case of \c{git}, \ci{snapsn} is the commit timestamp -in the \c{YYYYMMDDhhmmss} form and UTC timezone while \ci{snapid} is -a 12-character abbreviated commit id. For example: - -\ -1.2.3-a.1.20180319215815.26efe301f4a7 -\ - -Notice also that a snapshot version is ordered \i{after} the corresponding -pre-release version. That is, \c{1.2.3-a.1\ <\ 1.2.3-a.1.1}. As a result, it -is customary to start the development of a new version with \c{X.Y.Z-a.0.z}, -that is, a snapshot after the (non-existent) zero'th alpha release. \N{We will -explain the meaning of \cb{z} in this version momentarily.} The following -chronologically-ordered versions illustrate a typical release flow of a -project that uses \c{git} as its VCS: - -\ -0.1.0-a.0.19700101000000 # snapshot (no commits yet) -0.1.0-a.0.20180319215815.26efe301f4a7 # snapshot (first commit) -... # more commits/snapshots -0.1.0-a.1 # pre-release (first alpha) -0.1.0-a.1.20180319221826.a6f0f41205b8 # snapshot -... # more commits/snapshots -0.1.0-a.2 # pre-release (second alpha) -0.1.0-a.2.20180319231937.b701052316c9 # snapshot -... # more commits/snapshots -0.1.0-b.1 # pre-release (first beta) -0.1.0-b.1.20180319242038.c812163417da # snapshot -... # more commits/snapshots -0.1.0 # release -0.2.0-a.0.20180319252139.d923274528eb # snapshot (first in 0.2.0) -... -\ - -For a more detailed discussion of standard versioning and its support in -\c{build2} refer to \l{b#module-version Version Module}. - -Let's now see how this works in practice by publishing a couple of versions -for our \c{hello} project. By now it should be clear what that -\c{0.1.0-a.0.19700101000000} means \- it is the first snapshot version of our -project. Since there are no commits yet, it has the UNIX epoch as its commit -timestamp. As the first step, let's try to commit our project and see what -changes: - -\ -$ git add . -$ git commit -m \"Start hello project\" - -$ bdep status -hello configured 0.1.0-a.0.19700101000000 - available 0.1.0-a.0.20180507062614.ee006880fc7e -\ - -Just like with changes to dependency information, \c{status} has detected that -a new (snapshot) version of our project is available for synchronization. - -\N|Another way to view the project's version (which works even if we are -not using \c{bdep}) is with the build system's \c{info} operation: - -\ -$ b info -project: hello -version: 0.1.0-a.0.20180507062614.ee006880fc7e -summary: hello executable project -... -\ - -| - -Let's synchronize with the default build configuration: - -\ -$ bdep sync -synchronizing: - upgrade hello/0.1.0-a.0.20180507062614.ee006880fc7e - -$ bdep status -hello configured 0.1.0-a.0.20180507062614.ee006880fc7e -\ - -\N|Notice that we didn't have to manually change the version anywhere. All we -had to do was commit our changes and a new snapshot version was automatically -derived by \c{build2} from the new \c{git} commit. Without this automation -continuous versioning would hardly be practical.| - -If we now make another commit, we will see a similar picture: - -\ -$ bdep status -hello configured 0.1.0-a.0.20180507062614.ee006880fc7e - available 0.1.0-a.0.20180507062615.8fb9de05b38f -\ - -\N|Note that you don't need to manually run \c{sync} after every commit. As -discussed earlier, you can simply run the build system to update your project -and things will get automatically synchronized if necessary.| - -Ok, time for our first release. Let's start with \c{0.1.0-a.1}. Unlike -snapshots, for pre-releases as well as final releases we have to update the -version in the \c{manifest} file manually: - -\ -version: 0.1.0-a.1 -\ - -\N|The \c{manifest} file is the singular place where we specify the package -version. The build system's \l{b#module-version \c{version} module} makes it -available in various forms in buildfiles and even source code.| - -To ensure continuous versioning, this change to version must be the last commit -for this (pre-)release which itself must be immediately followed by a second -change to the version starting the development of the next (pre-)release. We -also recommend that you tag the release commit with a tag name in the -\c{\b{v}\i{X}.\i{Y}.\i{Z}} form. - -\N|Having regular release tag names with the \cb{v} prefix allows one to -distinguish them from other tags, for example, with wildcard patterns.| - -Here is the release workflow for our example: - -\ -$ git commit -a -m \"Release version 0.1.0-a.1\" -$ git tag -a v0.1.0-a.1 -m \"Tag version 0.1.0-a.1\" -$ git push --follow-tags - -# Version 0.1.0-a.1 is now public. - -$ edit manifest # change 'version: 0.1.0-a.1.z' -$ git commit -a -m \"Change version to 0.1.0-a.1.z\" -$ git push - -# Master is now open for business. -\ - -\N|In the future release management will be automated with a -\c{bdep-release(1)} command.| - -Notice also that when specifying a snapshot version in \c{manifest} we use the -special \cb{z} snapshot value (for example, \c{0.1.0-a.1.z}) which is -recognized and automatically replaced by \c{build2} with, in case of \c{git}, -a commit timestamp and id (refer to \l{b#module-version Version Module} for -details). - -Publishing the final release is exactly the same. For completeness, here -are the commands: - -\ -$ edit manifest # change 'version: 0.1.0' -$ git commit -a -m \"Release version 0.1.0\" -$ git tag -a v0.1.0 -m \"Tag version 0.1.0\" -$ git push --follow-tags - -$ edit manifest # change 'version: 0.2.0-a.0.z' -$ git commit -a -m \"Change version to 0.2.0-a.0.z\" -$ git push -\ - -\N|One sticky point of continuous versioning is choosing the next version. -For example, above should we continue with \c{0.1.1-a.0}, \c{0.2.0-a.0}, -or \c{1.0.0-a.0}? The important rule to keep in mind is that we can jump -forward to any further version at any time and without breaking continuous -versioning. But we can never jump backwards. - -For example, we can start with \c{0.2.0-a.0} but if we later realize that this -will actually be a new major release, we can easily change it to -\c{1.0.0-a.0}. As a result, the general recommendation is to start -conservatively by either incrementing the patch or the minor version -component. The recommended strategy is to increment the minor component and, -if required, release patch versions from a separate branch (created by -branching off from the release commit). - -Note also that you don't have to make any pre-releases if you don't need them. -While during development you would still keep the version as \c{X.Y.Z-a.0}, at -release you simply change it directly to the final \c{X.Y.Z}.| - -When publishing the final release you may also want to clean up now -obsolete pre-release tags. For example: - -\ -$ git tag -l 'v0.1.0-*' | xargs git push --delete origin -$ git tag -l 'v0.1.0-*' | xargs git tag --delete -\ - -\N|While at first removing such tags may seem like a bad idea, pre-releases -are by nature temporary and their use only makes sense until the final release -is published. - -Also note that having a \c{git} repository with a large number of published -but unused version tags may result in a significant download overhead.| - -Let's also briefly discuss in which situations we should increment each of the -version components. While \i{semver} gives basic guidelines, there are several -ways to apply them in the context of C/C++ where there is a distinction -between binary and source compatibility. We recommend that you reserve -\i{patch} releases for specific bug fixes and security issues that you can -guarantee with a high level of certainty to be binary-compatible. Otherwise, -if the changes are source-compatible, increment \i{minor}. And if they are -breaking (that is, the user code likely will need adjustments), increment -\i{major}. During early development, when breaking changes are frequent, it is -customary to use the \c{0.Y.Z} versions where \c{Y} effectively becomes the -\i{major} component. Again, refer to the \l{b#module-version Version Module} -for a more detailed discussion of this topic. - -\h#guide-consume-pkg|Package Consumption| - -Ok, now that we have published a few releases of \c{hello}, how would the -users of our project get them? While they could clone the repository and use -\c{bdep} just like we did, this is more of a development rather than -consumption workflow. For consumption it is much easier to use the package -dependency manager, \l{bpkg(1)}, directly. - -First, we create a suitable build configuration with the -\l{bpkg-cfg-create(1)} command. We can use the same place for building all our -tools so let's call the directory \c{tools}. Seeing that we are only -interested in using (rather than developing) such tools, let's build them -optimized and also configure a suitable installation location: - -\ -$ bpkg create -d tools cc \ - config.cxx=g++ \ - config.cc.coptions=-O3 \ - config.install.root=/usr/local \ - config.install.sudo=sudo -created new configuration in /tmp/tools/ - -$ cd tools -\ - -\N|The \c{bdep} build configurations we were creating with \c{init\ -C} are -actually \c{bpkg} build configurations. In fact, underneath, \l{bdep-init(1)} -calls \l{bpkg-cfg-create(1)}.| - -To fetch and build packages (as well as all their dependencies) we use the -\l{bpkg-pkg-build(1)} command. We can use either an archive-based repository -like \l{https://cppget.org cppget.org} or build directly from \c{git}: - -\ -$ bpkg build hello@https://git.build2.org/hello/hello.git -fetching from https://git.build2.org/hello/hello.git - new libformat/1.0.0 (required by libhello) - new libprint/1.0.0 (required by libhello) - new libhello/1.1.0 (required by hello) - new hello/1.0.0 -continue? [Y/n] y -configured libformat/1.0.0 -configured libprint/1.0.0 -configured libhello/1.1.0 -configured hello/1.0.0 -c++ libprint-1.0.0/libprint/cxx{print} -c++ hello-1.0.0/hello/cxx{hello} -c++ libhello-1.1.0/libhello/cxx{hello} -c++ libformat-1.0.0/libformat/cxx{format} -ld libprint-1.0.0/libprint/libs{print} -ld libformat-1.0.0/libformat/libs{format} -ld libhello-1.1.0/libhello/libs{hello} -ld hello-1.0.0/hello/exe{hello} -updated hello/1.0.0 -\ - -\N|Passing a repository URL to the \c{build} command is a shortcut to the -following sequence of commands: - -\ -$ bpkg add https://git.build2.org/hello/hello.git # add repository -$ bpkg fetch # fetch package list -$ bpkg build hello # build package by name -\ - -| - -Once built, we can install the package to the location that we have specified -with \c{config.install.root} using the \l{bpkg-pkg-install(1)} command: - -\ -$ bpkg install hello -... -install libformat-1.0.0/libformat/libs{format} -install libprint-1.0.0/libprint/libs{print} -install libhello-1.1.0/libhello/libs{hello} -install hello-1.0.0/hello/exe{hello} - -$ hello World -Hello, World! -\ - -\N|If on your system the installed executables don't run from \c{/usr/local} -because of the unresolved shared libraries (or if you are installing somewhere -else, such as \c{/opt}), then the easiest way to fix this is with \i{rpath}. -Simply add the following configuration variable when creating the build -configuration (or as an argument to the \c{install} command): - -\ -config.bin.rpath=/usr/local/lib -\ - -| - -If we need to uninstall a previously installed package, there is the -\l{bpkg-pkg-uninstall(1)} command: - -\ -$ bpkg uninstall hello -uninstall hello-1.0.0/hello/exe{hello} -uninstall libhello-1.1.0/libhello/libs{hello} -uninstall libprint-1.0.0/libprint/libs{print} -uninstall libformat-1.0.0/libformat/libs{format} -... -\ - -To upgrade or downgrade packages we again use the \c{build} command. Here -is a typical upgrade workflow: - -\ -$ bpkg fetch # refresh available package list -$ bpkg status # see if new versions are available - -$ bpkg uninstall hello # uninstall old version -$ bpkg build hello # upgrade to the latest version -$ bpkg install hello # install new version -\ - -Similar to \c{bdep}, to downgrade we have to specify the desired version -explicitly. There are also the \c{--upgrade|-u} and \c{--patch|-p} as well as -\c{--immediate|-i} and \c{--recursive|-r} options that allow us to upgrade or -patch packages that we have built and/or their immediate or all dependencies -(see \l{bpkg-pkg-build(1)} for details). For example, to make sure everything -is patched, run: - -\ -$ bpkg fetch -$ bpkg build -pr -\ - -If a package is no longer needed, we can remove it from the configuration with -\l{bpkg-pkg-drop(1)}: - -\ -$ bpkg drop hello -following dependencies were automatically built but -will no longer be used: - libhello - libformat - libprint -drop unused packages? [Y/n] y - drop hello - drop libhello - drop libformat - drop libprint -continue? [Y/n] y -purged hello -purged libhello -purged libformat -purged libprint -\ -" |