diff --git a/v2/doc/src/tutorial.xml b/v2/doc/src/tutorial.xml index 03fc08b86..cafc1b57c 100644 --- a/v2/doc/src/tutorial.xml +++ b/v2/doc/src/tutorial.xml @@ -2,16 +2,18 @@ - + + + Tutorial
Hello, world The simplest project that Boost.Build can construct is - stored in example/hello/ directory. The - project is described by a file - called Jamfile that contains: + stored in example/hello/ directory. The + project is described by a file + called Jamfile that contains: exe hello : hello.cpp ; @@ -64,84 +66,84 @@ bjam --clean debug release bjam hello2 bjam --clean hello2 - + -
-
- Properties +
+
+ Properties - - To portably represent aspects of target configuration such as - debug and release variants, or single- and multi-threaded - builds, Boost.Build uses features with - associated values. For - example, the "debug-symbols" feature can have a value of "on" or - "off". A property is just a (feature, - value) pair. When a user initiates a build, Boost.Build - automatically translates the requested properties into appropriate - command-line flags for invoking toolset components like compilers - and linkers. + + To portably represent aspects of target configuration such as + debug and release variants, or single- and multi-threaded + builds, Boost.Build uses features with + associated values. For + example, the "debug-symbols" feature can have a value of "on" or + "off". A property is just a (feature, + value) pair. When a user initiates a build, Boost.Build + automatically translates the requested properties into appropriate + command-line flags for invoking toolset components like compilers + and linkers. - There are many built-in features that can be combined to - produce arbitrary build configurations. The following command - builds the project's "release" variant with inlining - disabled and debug symbols enabled: + There are many built-in features that can be combined to + produce arbitrary build configurations. The following command + builds the project's "release" variant with inlining + disabled and debug symbols enabled: bjam release inlining=off debug-symbols=on -Properties on the command-line are specified with the syntax: + Properties on the command-line are specified with the syntax: feature-name=feature-value -The "release" and "debug" that we've seen - in bjam invocations are just a shorthand way to - specify values of the "variant" feature. For example, the command - above could also have been written this way: + The "release" and "debug" that we've seen + in bjam invocations are just a shorthand way to + specify values of the "variant" feature. For example, the command + above could also have been written this way: - + bjam variant=release inlining=off debug-symbols=on - - + + - "variant" is so commonly-used that it has been given - special status as an implicit feature - — Boost.Build will deduce the its identity just from the name - of one of its values. - + "variant" is so commonly-used that it has been given + special status as an implicit feature + — Boost.Build will deduce the its identity just from the name + of one of its values. + - + A complete description of features can be found here. - +
Build Requests and Target Requirements - - The set of properties specified in the command line constitute a - build request — a description of - the desired properties for building the requested targets (or, - if no targets were explicitly requested, the project in the - current directory). The actual properties - used for building targets is typically a combination of the - build request and properties derived from the - project's Jamfiles. For example, the - locations of #included header files are normally - not specified on the command-line, but described - in Jamfiles as target - requirements and automatically combined with the - build request for those targets. Multithread-enabled - compilation is another example of a typical target requirement. - The Jamfile fragment below illustrates how - these requirements might be specified. - + + The set of properties specified in the command line constitute a + build request — a description of + the desired properties for building the requested targets (or, + if no targets were explicitly requested, the project in the + current directory). The actual properties + used for building targets is typically a combination of the + build request and properties derived from the + project's Jamfiles. For example, the + locations of #included header files are normally + not specified on the command-line, but described + in Jamfiles as target + requirements and automatically combined with the + build request for those targets. Multithread-enabled + compilation is another example of a typical target requirement. + The Jamfile fragment below illustrates how + these requirements might be specified. + exe hello @@ -150,32 +152,35 @@ exe hello ; - When hello is built, the two requirements - specified above will normally always be present. If the build - request given on the bjam command-line - explictly contradicts a target's requirements, the command-line - usually overrides (or, in the case of "free" feautures - like <include>, augments) the target - requirements. However, when a contradiction of a target's - requrements involves certain - link-incompatible features, the target will - be skipped. See for more information. - + + When hello is built, the two + requirements specified above will normally always be present. + If the build request given on the bjam + command-line explictly contradicts a target's requirements, + the command-line usually overrides (or, in the case of + "free" feautures like <include> + See , + augments) the target requirements. However, when a + contradiction of a target's requrements involves certain + link-incompatible features, the target + will be skipped. See for more information. +
Project Attributes - - If we want the same requirements for our other - target, hello2, we could simply duplicate - them. However, as projects grow, that approach leads to a great - deal of repeated boilerplate in Jamfiles. - - Fortunately, there's a better way. Each project (i.e. each - Jamfile), can specify a set of attributes, - including requirements: + + If we want the same requirements for our other + target, hello2, we could simply duplicate + them. However, as projects grow, that approach leads to a great + deal of repeated boilerplate in Jamfiles. + + Fortunately, there's a better way. Each project (i.e. each + Jamfile), can specify a set of attributes, + including requirements: project @@ -186,181 +191,216 @@ exe hello : hello.cpp ; exe hello2 : hello.cpp ; - The effect would be as if we specified the same requirement for - both hello and hello2. + The effect would be as if we specified the same requirement for + both hello and hello2.
-
+ -
- Project hierarchy +
+ Project Hierarchies - So far we only considered examples with one project (i.e. with - one Jamfile). Typically, you'd have a lot of projects organized - into a tree. At the top of the tree there's project - root. This is a directory which contains, besides Jamfile, a - file called "project-root.jam". Each other Jamfile has a single - parent, which is the Jamfile in the nearest parent directory. For - example, in the following directory layout: + So far we've only considered examples with one project + (i.e. with one Jamfile). A typical large + software project would be composed of sub-projects organized + into a tree. The top of the tree is called the + project root. Besides a + Jamfile, the project root directory + contains a file called project-root.jam. Every other + Jamfile in the project has a single parent + project, rooted in the nearest parent directory containing a + Jamfile. For example, in the following + directory layout: -[top] +top/ | - |-- Jamfile - |-- project-root.jam + +-- Jamfile + +-- project-root.jam | - |-- src + +-- src/ | | - | |-- Jamfile - | \-- app.cpp + | +-- Jamfile + | `-- app.cpp | - \-- lib + `-- util/ | - |-- lib1 - | | - | |-- Jamfile - |-- lib1.cpp + +-- foo/ + . | + . +-- Jamfile + . `-- bar.cpp - -project root is at top. Both src/Jamfile and lib/lib1/Jamfile -have [top]/Jamfile as parent project. Projects inherit all -attributes (such as requirements) from their parents. When the same -attributes are specified in the project, they are combined with -inherited ones. For example, if [top]/Jamfile has + + + the project root is top/. Because there is + no Jamfile in + top/lib/, the projects in + top/src/ and + top/util/foo/ are immediate children of the + root project. + + + + Projects inherit all attributes (such as requirements) + from their parents. Inherited requirements are combined with + any requirements specified by the sub-project. + For example, if top/Jamfile has <include>/home/ghost/local -in requirements, then all other projects will have that in -their requirements too. Of course, any project can add additional -includes. More details can be found in the section on projects. Projects are not automatically -built when -their parents are built. You should specify this explicitly. In our -example, [top]/Jamfile might contain: + in its requirements, then all of its sub-projects will have it + in their requirements, too. Of course, any project can add + additional includes. Many features will be overridden, + rather than added-to, in sub-projects. See for more + information More details can be found in the section + on projects. + + + + Invoking bjam without explicitly specifying + any targets on the command-line builds the project rooted in the + current directory. Building a project does not automatically + cause its sub-projects to be built unless the parent project's + Jamfile explicitly requests it. In our + example, top/Jamfile might contain: build-project src ; - It will cause project in src to be built whenever project in -[top] is built. However, targets in lib/lib1 will be built only if -required. For example, there may be 10 targets, and two of them are -used by targets in src/Jamfile. Then, only those two targets will -be built. - -
+ which would cause the project in top/src/ + to be built whenever the project in top/ is + built. However, targets in top/util/foo/ + will be built only if they are needed by targets in + top/ or top/src/. + +
-
- Using libraries +
+ Libraries and Dependent Targets - Let's continue the above example and see how src/Jamfile - can use libraries from - lib/lib1. (TODO: need to make this section consistent with - "examples-v2/libraries". Assume lib/lib1/Jamfile contains: + TODO: need to make this + section consistent with "examples-v2/libraries". + + + Targets that are "needed" by other targets are called + dependencies of those other targets. The + targets that need the other targets are called + dependent targets. + + + To get a feeling for target dependencies, let's continue the + above example and see how src/Jamfile can + use libraries from util/foo. Assume + util/foo/Jamfile contains: -lib lib1 : lib1.cpp ; +lib bar : bar.cpp ; - Then, to use this library in src/Jamfile, we can write: + Then, to use this library in src/Jamfile, we can write: -exe app : app.cpp ../lib/lib1//lib1 ; +exe app : app.cpp ../util/foo//bar ; -While "app.cpp" is a regular source file, "../lib/lib1//lib1" -is a reference to another target, here, library "lib1" declared in -Jamfile at "../lib/lib1". When linking the "app" binary, the needed -version of the library will be built and linked in. But what is -meant by "needed"? For example, we can request to build "app" with -properties + While app.cpp refers to a regular source file, + ../util/foo//bar is a reference to another target: + a library "bar" declared in the Jamfile at + ../util/foo. When linking the + app executable, the appropriate version of + bar will be built and linked in. What do we mean by + "appropriate"? For example, suppose we build "app" with: - -<optimization>full <cxxflags>-w-8080 - + +bjam app optimization=full cxxflags=-w-8080 + -Which properties must be used for "lib1"? The answer is that -some properties are propagated — Boost.Build attemps -to use dependencies with the same value of propagated features. The -<optimization> feature is propagated, so both "app" and -"lib1" will be compiled with full optimization. But -<cxxflags> feature is not propagated: its value will be added -as-is to compiler flags for "a.cpp", but won't affect "lib1". There -is still a couple of problems. First, the library probably has some -headers which must be used when compiling "app.cpp". We could use -requirements on "app" to add those includes, but then this work -will be repeated for all programs which use "lib1". A better -solution is to modify lib/lib1/Jamfilie in this way: + Which properties must be used to build foo? The + answer is that some properties are + propagated — Boost.Build attempts to + use dependencies with the same value of propagated features. The + <optimization> feature is propagated, so both "app" and + "foo" will be compiled with full optimization. But + <cxxflags> feature is not propagated: its value will be + added as-is to compiler flags for "a.cpp", but won't affect + "foo". There is still a couple of problems. First, the library + probably has some headers which must be used when compiling + "app.cpp". We could use requirements on "app" to add those + includes, but then this work will be repeated for all programs + which use "foo". A better solution is to modify + util/foo/Jamfilie in this way: project : usage-requirements <include>. ; -lib lib1 : lib1.cpp ; +lib foo : foo.cpp ; -Usage requirements are requirements which are applied to -dependents. In this case, <include> will be applied to all -targets which use "lib1" — i.e. targets which have "lib1" -either in sources or in dependency properties. You'd need to -specify usage requirements only once, and programs which use "lib1" -don't have to care about include paths any longer. Or course, the -path will be interpreted relatively to "lib/lib1" and will be -adjusted according to the bjams invocation -directory. For -example, if building from project root, the final compiler's -command line will contain . - + Usage requirements are requirements which are applied to + dependents. In this case, <include> will be applied to all + targets which use "foo" — i.e. targets which have "foo" + either in sources or in dependency properties. You'd need to + specify usage requirements only once, and programs which use "foo" + don't have to care about include paths any longer. Or course, the + path will be interpreted relatively to "util/foo" and will be + adjusted according to the bjams invocation + directory. For + example, if building from project root, the final compiler's + command line will contain . + - The second problem is that we hardcode the path to library's - Jamfile. Imagine it's hardcoded in 20 different places and we - change the directory layout. The solution is to use project ids - — symbolic names, not tied to directory layout. First, we - assign a project id to Jamfile in lib/lib1: + The second problem is that we hardcode the path to library's + Jamfile. Imagine it's hardcoded in 20 different places and we + change the directory layout. The solution is to use project ids + — symbolic names, not tied to directory layout. First, we + assign a project id to Jamfile in util/foo: -project lib1 +project foo : usage-requirements <include>. ; - -Second, we use the project id to refer to the library in -src/Jamfile: + + Second, we use the project id to refer to the library in + src/Jamfile: -exe app : app.cpp /lib1//lib1 ; +exe app : app.cpp /foo//bar ; -The "/lib1//lib1" syntax is used to refer to target "lib1" in -project with global id "/lib1" (the slash is used to specify global -id). This way, users of "lib1" do not depend on its location, only -on id, which is supposedly stable. The only thing left, it to make -sure that src/Jamfile knows the project id that it uses. We add to -[top]/Jamfile the following line: + The "/foo//bar" syntax is used to refer to target "foo" in + project with global id "/foo" (the slash is used to specify global + id). This way, users of "foo" do not depend on its location, only + on id, which is supposedly stable. The only thing left, it to make + sure that src/Jamfile knows the project id that it uses. We add to + top/Jamfile the following line: -use-project /lib1 : lib/lib1 ; +use-project /foo : util/foo ; -Now, all projects can refer to "lib1" using the symbolic -name. If the library is moved somewhere, only a single line in the -top-level Jamfile should be changed. - -
+ Now, all projects can refer to "foo" using the symbolic + name. If the library is moved somewhere, only a single line in the + top-level Jamfile should be changed. + +
-
- Library dependencies +
+ Library dependencies - The previous example was simple. Often, there are long chains - of dependencies between libraries. The main application is a thin - wrapper on top of library with core logic, which uses library of - utility functions, which uses boost filesystem library. - Expressing these dependencies is straightforward: + The previous example was simple. Often, there are long chains + of dependencies between libraries. The main application is a thin + wrapper on top of library with core logic, which uses library of + utility functions, which uses boost filesystem library. + Expressing these dependencies is straightforward: lib utils : utils.cpp /boost/filesystem//fs ; @@ -368,27 +408,27 @@ lib core : core.cpp utils ; exe app : app.cpp core ; - So, what's the reason to even mention this case? First, - because it's a bit more complex that it seems. When using shared - linking, libraries are build just as written, and everything will - work. However, what happens with static linking? It's not - possible to include another library in static library. - Boost.Build solves this problem by returning back library targets - which appear as sources for static libraries. In this case, if - everything is built statically, the "app" target will link not - only "core" library, but also "utils" and - "/boost/filesystem//fs". + So, what's the reason to even mention this case? First, + because it's a bit more complex that it seems. When using shared + linking, libraries are build just as written, and everything will + work. However, what happens with static linking? It's not + possible to include another library in static library. + Boost.Build solves this problem by returning back library targets + which appear as sources for static libraries. In this case, if + everything is built statically, the "app" target will link not + only "core" library, but also "utils" and + "/boost/filesystem//fs". - So, the net result is that the above code will work for both - static linking and for shared linking. + So, the net result is that the above code will work for both + static linking and for shared linking. - Sometimes, you want all applications in some project to link - to a certain library. Putting the library in sources of all - targets is possible, but verbose. You can do better by using the - <source> property. For example, if "/boost/filesystem//fs" - should be linked to all applications in your project, you can add - <source>/boost/filesystem//fs to requirements of the - project, like this: + Sometimes, you want all applications in some project to link + to a certain library. Putting the library in sources of all + targets is possible, but verbose. You can do better by using the + <source> property. For example, if "/boost/filesystem//fs" + should be linked to all applications in your project, you can add + <source>/boost/filesystem//fs to requirements of the + project, like this: project @@ -397,105 +437,105 @@ project
-
- Static and shared libaries +
+ Static and shared libaries - While the - previous section explained how to create and use libraries, it - omitted one important detail. Libraries can be either - static, which means they are included in executable - files which use them, or shared (a.k.a. - dynamic), which are only referred to from executables, - and must be available at run time. Boost.Build can work with both - types. By default, all libraries are shared. This is much more - efficient in build time and space. But the need to install all - libraries to some location is not always convenient, especially - for debug builds. Also, if the installed shared library changes, - all application which use it might start to behave differently. - + While the + previous section explained how to create and use libraries, it + omitted one important detail. Libraries can be either + static, which means they are included in executable + files which use them, or shared (a.k.a. + dynamic), which are only referred to from executables, + and must be available at run time. Boost.Build can work with both + types. By default, all libraries are shared. This is much more + efficient in build time and space. But the need to install all + libraries to some location is not always convenient, especially + for debug builds. Also, if the installed shared library changes, + all application which use it might start to behave differently. + - Static libraries do not suffer from these problems, but - considerably increase the size of application. Before describing - static libraries, it's reasonable to give another, quite simple - approach. If your project is built with - <hardcode-dll-paths>true property, then the application - will include the full paths for all shared libraries, eliminating - the above problems. Unfortunately, you no longer can move shared - library to a different location, which makes this option suitable - only for debug builds. Further, only gcc compiler supports this - option. + Static libraries do not suffer from these problems, but + considerably increase the size of application. Before describing + static libraries, it's reasonable to give another, quite simple + approach. If your project is built with + <hardcode-dll-paths>true property, then the application + will include the full paths for all shared libraries, eliminating + the above problems. Unfortunately, you no longer can move shared + library to a different location, which makes this option suitable + only for debug builds. Further, only gcc compiler supports this + option. - Building a library statically is easy. You'd need to change - the value of <link> feature from it's deafault value - shared, to static. So, to build everything as - static libraries, you'd say + Building a library statically is easy. You'd need to change + the value of <link> feature from it's deafault value + shared, to static. So, to build everything as + static libraries, you'd say bjam link=static - -on the command line. The linking mode can be fine-tuned on -per-target basis. + + on the command line. The linking mode can be fine-tuned on + per-target basis. - - - + + + Suppose your library can be only build statically. This is easily achieved using requirements: lib l : l.cpp : <link>static ; - - - + + + - - + + What if library can be both static and shared, but when using it in specific executable, you want it static? Target - references are here to help: + references are here to help: exe important : main.cpp helpers/<link>static ; - - + + - - - What if the library is defined in some other project, which - you cannot change. But still, you want static linking to that - library in all cases. You can use target references everywhere: + + + What if the library is defined in some other project, which + you cannot change. But still, you want static linking to that + library in all cases. You can use target references everywhere: -exe e1 : e1.cpp /other_project//lib1/<link>static ; -exe e10 : e10.cpp /other_project//lib1/<link>static ; +exe e1 : e1.cpp /other_project//bar/<link>static ; +exe e10 : e10.cpp /other_project//bar/<link>static ; -but that's far from being convenient. Another way is to -introduce a level of indirection: create a local target, which will -refer to static version of lib1. Here's the -solution: + but that's far from being convenient. Another way is to + introduce a level of indirection: create a local target, which will + refer to static version of foo. Here's the + solution: -alias lib1 : /other_project//lib1/<link>static ; -exe e1 : e1.cpp lib1 ; -exe e10 : e10.cpp lib1 ; +alias foo : /other_project//bar/<link>static ; +exe e1 : e1.cpp foo ; +exe e10 : e10.cpp foo ; -Note that the alias -rule is specifically used for rename a reference to a target and possibly -change the properties. + Note that the alias + rule is specifically used for rename a reference to a target and possibly + change the properties. - - - - -
+ + + + +
Conditions and alternatives @@ -536,19 +576,19 @@ lib demangler : demangler_msvc.cpp : <toolset>msvc ; The proper alternative will be automatically selected. -
+
-
- Prebuilt targets +
+ Prebuilt targets - + We've just learned how to use libraries which are created by Boost.Build. But some libraries are not. At the same time, those libraries can have different versions (release and debug, for example), that we - should select depending on build properties. Prebuilt targets - provide a mechanism for that. Jamfile in lib/lib2 can contain: + should select depending on build properties. Prebuilt targets + provide a mechanism for that. Jamfile in util/lib2 can contain: lib lib2 @@ -562,59 +602,69 @@ lib lib2 ; -This defines two alternatives for target "lib2", and for each -one names a prebuilt file. Naturally, there are no sources. -Instead, the <file> feature is used to specify the file name. -Which alternative is selected depends on properties of dependents. -If "app" binary should use "lib2", we can write: + This defines two alternatives for target "lib2", and for each + one names a prebuilt file. Naturally, there are no sources. + Instead, the <file> feature is used to specify the file name. + Which alternative is selected depends on properties of dependents. + If "app" binary should use "lib2", we can write: -exe app : app.cpp /lib/lib1//lib2 ../lib/lib2//lib2 ; +exe app : app.cpp /util/foo//lib2 ../util/lib2//lib2 ; -If we build release version of "app", then it will be linked -with "lib2_release.a", and debug version will use "lib2_debug.a". -Another important kind of prebuilt targets are system libraries -— more specifically, libraries which are automatically found -by the compiler. E.g. gcc uses "-l" switch for that. Such libraries -should be declared almost like regular ones: + If we build release version of "app", then it will be linked + with "lib2_release.a", and debug version will use "lib2_debug.a". + Another important kind of prebuilt targets are system libraries + — more specifically, libraries which are automatically found + by the compiler. E.g. gcc uses "-l" switch for that. Such libraries + should be declared almost like regular ones: lib zlib : : <name>z ; -We again don't specify any sources, but give a name which -should be passed to the compiler. In this example, and for gcc -compiler, the "-lz" option will be added. Paths where library -should be searched can also be specified: + We again don't specify any sources, but give a name which + should be passed to the compiler. In this example, and for gcc + compiler, the "-lz" option will be added. Paths where library + should be searched can also be specified: lib zlib : : <name>z <search>/opt/lib ; -And, of course, two variants can be used: + And, of course, two variants can be used: lib zlib : : <name>z <variant>release ; lib zlib : : <name>z_d <variant>debug ; -Of course, you'll probably never in your life need debug -version of zlib, but for other libraries this is quite reasonable. - + Of course, you'll probably never in your life need debug + version of zlib, but for other libraries this is quite reasonable. + - More advanced use of prebuilt target is descibed in recipes. + More advanced use of prebuilt target is descibed in recipes. -
+