[/ Copyright 2015 Peter Dimov Distributed under the Boost Software License, Version 1.0. See accompanying file LICENSE_1_0.txt or copy at http://boost.org/LICENSE_1_0.txt ] [article Boostdep [quickbook 1.6] [id boostdep] [copyright 2014, 2015, 2016 Peter Dimov] [license Distributed under the [@http://boost.org/LICENSE_1_0.txt Boost Software License, Version 1.0]. ] ] [template simplesect[title] [block ''''''[title]'''''']] [template endsimplesect[] [block '''''']] [section Introduction] /Boostdep/ is a tool for generating Boost dependency reports. It scans the header or source files of the Boost libraries for `#include` directives, builds a dependency graph from this information and outputs its findings in plain text or HTML. [section Modular Boost] /Boostdep/ requires the so-called "modular Boost" directory structure. If you already have a [@https://svn.boost.org/trac/boost/wiki/ModularBoost modular Boost installation], you can skip this section. Otherwise, read on. Boost libraries reside in subdirectories under the =libs= directory. For example, the contents of the Boost.Filesystem library are in =libs/filesystem=. This includes the build scripts (in =libs/filesystem/build=), the source files (in =libs/filesystem/src=), the tests (in =libs/filesystem/test=), the documentation (in =libs/filesystem/doc=), and so on. In the past, when Boost used SVN as its version control system, the header files were an exception. The header files of all libraries resided in the =boost= subdirectory, and it wasn't possible to accurately determine which header belonged to which library. When Boost moved to Git for version control, header files were moved to their corresponding libraries, into an =include= subdirectory. The header files of Boost.Filesystem are now in =libs/filesystem/include=. For compatibility, =boost= is now a "virtual" directory, containing links to the headers. It's maintained automatically by Boost.Build. (The command =b2 headers= creates or recreates the contents of the =boost= directory.) This new structure allows /Boostdep/ to determine that, when faced with an `#include ` directive, that this header is part of Boost.Filesystem, and that therefore, the current library being scanned depends on Boost.Filesystem. Unfortunately, Boost releases do not have this structure. For backward compatibility, they have an old-style =boost= directory containing all header files, whereas the per-library =include= subdirectories are missing. Therefore, /Boostdep/ will not work with a downloaded Boost release. To use /Boostdep/, you will have to clone the Boost Git repository instead. To do that, execute the following command: [pre git clone https://github.com/boostorg/boost.git boost ] This will download the Boost "superproject" (the master project, without any libraries) and place it into the subdirectory =boost= of the current directory. To override the directory name, pass it as a second argument instead of =boost=: [pre git clone https://github.com/boostorg/boost.git /mydir/ ] You can now =cd= into the newly created directory with [pre cd /mydir/ ] This directory is called the "Boost root". All of the commands below assume that it is the current directory. The above =git clone= commands download the default branch of the Boost Git repository, which is =master=. This is the current more-or-less stable version of Boost. To verify this, issue the command [pre git status ] from the Boost root. This will output [pre # On branch master nothing to commit, working directory clean ] To download a specific release instead, such as 1.58.0, issue the following command after =git clone=, from the Boost root: [pre git checkout boost-1.58.0 ] =git status= will now say [pre # HEAD detached at boost-1.58.0 nothing to commit, working directory clean ] Then, download all the libraries: [pre git submodule update --init ] This step will take a while. If all goes well, you will now have the complete contents of Boost's latest =master= branch (if you didn't =checkout= a specific release by name) or the corresponding Boost release (if you did). You can switch between the =master= branch, the =develop= (unstable) branch, and a release, by issuing the following commands: [:For the =master= branch:] [pre git checkout master git pull git submodule update --init ] [:(=git pull= updates your local copy of the =master= branch from the server, in case it has changed since your initial checkout.)] [:For the =develop= branch:] [pre git checkout develop git pull git submodule update --init ] [:For the =boost-1.58.0= release:] [pre git checkout boost-1.58.0 git submodule update --init ] [:For the =boost-1.57.0= release:] [pre git checkout boost-1.57.0 git submodule update --init ] Note that, while the initial =git submodule update= is quite slow, as it needs to download all the libraries, the subsequent invocations are a lot faster. Also note that if a new Boost library (=libs/convert=, for example) is present in, say, =master=, and you have it checked out, when you later switch to =boost-1.58.0=, where this library doesn't exist, Git will not delete =libs/convert=. In this case, =git status= will output [pre # HEAD detached at boost-1.58.0 # Untracked files: # (use "git add ..." to include in what will be committed) # # libs/convert/ nothing added to commit but untracked files present (use "git add" to track) ] and you will have to remove =libs/convert= by hand. Once you have the Boost contents which you want to analyze for dependencies, proceed with the next step, building /Boostdep/. [endsect] [section Building Boostdep] To build /Boostdep/, issue the following command from the Boost root: [pre b2 tools/boostdep/build//install ] This will build /Boostdep/ from source using the default "toolset" (a Boost.Build term meaning "compiler") and if successful, place it into the =dist/bin= subdirectory. The command assumes that =b2= (the Boost.Build executable) is somewhere in your path. If you don't have =b2=, execute [pre .\bootstrap ] under Windows or [pre ./bootstrap.sh ] under Unix-like systems, which should build =b2= and place it into the current directory. You can then use =./b2= instead of =b2=. [endsect] [section Running Boostdep] Once you have built /Boostdep/, execute it with the following command: [pre dist/bin/boostdep ] or [pre dist\bin\boostdep ] on Windows. The commands below are given as using =dist/bin/boostdep=; if you're using Windows, use =dist\bin\boostdep= instead. This will print out the following help message: [pre Usage: boostdep --list-modules boostdep --list-buildable boostdep \[--track-sources\] --list-dependencies boostdep \[options\] --module-overview boostdep \[options\] --module-levels boostdep \[options\] --module-weights boostdep \[options\] \[--primary\] boostdep \[options\] --secondary boostdep \[options\] --reverse boostdep \[options\] --subset boostdep \[options\] \[--header\]
\[options\]: \[--track-sources\] \[--title \] \[--footer <footer>\] \[--html\] ] (The 1.58.0 version of /Boostdep/ has an unfortunate bug that causes the above output to be truncated after =boostdep --list-modules=. The rest of the functionality is intact though, so you can still use it as described.) [endsect] [endsect] [section Usage] [section Simple Queries] To list the dependencies of a specific library, use the command [pre dist/bin/boostdep /library/ ] For Boost.Filesystem, for example, type [pre dist/bin/boostdep filesystem ] This will print out something similar to the following: [pre Primary dependencies for filesystem: assert: <boost/assert.hpp> from <boost/filesystem/operations.hpp> from <boost/filesystem/path_traits.hpp> config: <boost/config.hpp> from <boost/filesystem/config.hpp> from <boost/filesystem/convenience.hpp> from <boost/filesystem/fstream.hpp> from <boost/filesystem/operations.hpp> from <boost/filesystem/path.hpp> from <boost/filesystem/path_traits.hpp> /.../ functional: <boost/functional/hash_fwd.hpp> from <boost/filesystem/path.hpp> io: <boost/io/detail/quoted_manip.hpp> from <boost/filesystem/path.hpp> iterator: <boost/iterator/iterator_facade.hpp> from <boost/filesystem/path.hpp> /.../ ] This lists the immediate dependencies of Boost.Filesystem. =assert:= is the library, =<boost/assert.hpp>= is the file that is being included, and =from <boost/filesystem/config.hpp>= shows where =<boost/assert.hpp>= is being included. /Boostdep/ names libraries (or modules) after their directory name. The =libs/filesystem= directory, for example, is the =filesystem= module. The =libs/numeric/conversion= directory is the =numeric~conversion= module, according to the /Boostdep/ naming convention. The reason forward slashes are replaced with tildes is that =numeric~conversion= is a valid file name, which makes generating HTML reports a bit easier. To see where a given header resides and who includes it, type [pre dist/bin/boostdep /header/ ] For =boost/filesystem.hpp=, for example, type [pre dist/bin/boostdep boost/filesystem.hpp ] This will print something along the lines of [pre Inclusion report for <boost/filesystem.hpp> (in module filesystem): from spirit: <boost/spirit/home/x3/support/utility/testing.hpp> ] What this tells you is that =boost/filesystem.hpp= is part of Boost.Filesystem and is only included once, from =<boost/spirit/home/x3/support/utility/testing.hpp>=. Other headers, such as =boost/shared_ptr.hpp=, are more widely used, as you can see if you try [pre dist/bin/boostdep boost/shared_ptr.hpp ] To print the reverse dependencies of a library, use [pre dist/bin/boostdep --reverse /library/ ] For example, [pre dist/bin/boostdep --reverse filesystem ] will list which libraries depend on Boost.Filesystem: [pre Reverse dependencies for filesystem: graph_parallel: <boost/filesystem/operations.hpp> from <boost/graph/distributed/adjlist/serialization.hpp> <boost/filesystem/path.hpp> from <boost/graph/distributed/adjlist/serialization.hpp> log: <boost/filesystem/config.hpp> from <boost/log/detail/config.hpp> <boost/filesystem/path.hpp> from <boost/log/sinks/event_log_backend.hpp> from <boost/log/sinks/text_file_backend.hpp> from <boost/log/sinks/text_multifile_backend.hpp> spirit: <boost/filesystem.hpp> from <boost/spirit/home/x3/support/utility/testing.hpp> <boost/filesystem/fstream.hpp> from <boost/spirit/home/x3/support/utility/testing.hpp> <boost/filesystem/path.hpp> from <boost/spirit/home/x3/support/utility/error_reporting.hpp> wave: <boost/filesystem/operations.hpp> from <boost/wave/util/cpp_include_paths.hpp> from <boost/wave/util/cpp_iterator.hpp> from <boost/wave/util/filesystem_compatibility.hpp> <boost/filesystem/path.hpp> from <boost/wave/cpp_context.hpp> from <boost/wave/util/cpp_include_paths.hpp> from <boost/wave/util/cpp_iterator.hpp> from <boost/wave/util/cpp_macromap.hpp> from <boost/wave/util/filesystem_compatibility.hpp> ] [endsect] [section HTML reports] The primary purpose of /Boostdep/ is to generate HTML dependency reports. In the typical case, two types of reports are generated: overviews that contain information for all modules, and per-module ones that list information for a specific library. /Boostdep/ can generate three types of the first kind of report: module overview, module levels and module weights. To generate a module overview, use the command [pre dist/bin/boostdep --html --module-overview > module-overview.html ] For a module level report, use [pre dist/bin/boostdep --html --module-levels > module-levels.html ] For a module weight report, use [pre dist/bin/boostdep --html --module-weights > module-weights.html ] In these reports, module names such as /module/ are HTML links to [^/module/.html]. To make these links work as expected, you can generate HTML reports for each module as follows: [pre dist/bin/boostdep --title "Dependency Report for /module/" --html --primary /module/ --secondary /module/ --reverse /module/ > /module/.html ] This step can be automated if you generate a module list first with [pre dist/bin/boostdep --list-modules > list-modules.txt ] that will contain one module name per line, and then use a script to issue the previous command for each module name. For more information about the /Boostdep/ options and commands, see the [link boostdep.reference Reference] section. For an example of a report generation script, see the file =tools/boostdep/examples/report.bat=. This is a Windows batch file, but translating it to a Unix-style shell script should be straightforward. For convenience, the contents of =tools/boostdep/examples/report.bat= are given below: [pre SET BOOSTDEP=dist\bin\boostdep.exe FOR /f %%i IN ('git rev-parse HEAD') DO @SET REV=%%i FOR /f %%i IN ('git rev-parse --short HEAD') DO @SET SHREV=%%i FOR /f %%i IN ('git rev-parse --abbrev-ref HEAD') DO @SET BRANCH=%%i SET FOOTER=Generated on %DATE% %TIME% from revision %REV% on branch '%BRANCH%' SET OUTDIR=..\report-%BRANCH%-%SHREV% mkdir %OUTDIR% %BOOSTDEP% --list-modules > %OUTDIR%\list-modules.txt %BOOSTDEP% --footer "%FOOTER%" --html --module-overview > %OUTDIR%\module-overview.html %BOOSTDEP% --footer "%FOOTER%" --html --module-levels > %OUTDIR%\module-levels.html %BOOSTDEP% --footer "%FOOTER%" --html --module-weights > %OUTDIR%\module-weights.html FOR /f %%i IN (%OUTDIR%\list-modules.txt) DO %BOOSTDEP% --title "Dependency Report for %%i" --footer "%FOOTER%" --html --primary %%i --secondary %%i --reverse %%i > %OUTDIR%\%%i.html ] [endsect] [endsect] [section Reference] [section --list-modules] =boostdep --list-modules= prints the module list. /Boostdep/ considers a subdirectory of =libs= a module if it contains an =include= subdirectory. This command is typically used from scripts which then use the list to execute a command for each module. [endsect] [section --list-buildable] =boostdep --list-buildable= prints a list of the modules that require building. /Boostdep/ considers a module to require building if it contains subdirectories named =build= and =src=. This command is typically used from scripts. [endsect] [section --list-dependencies] =boostdep --list-dependencies= prints a module list in which each line is of the form [pre module -> dependency1 dependency2 /.../ ] By default, only the =include= directory is scanned for `#include` directives. If the option =--track-sources= is given, the =src= directory is also scanned. This command is typically used from scripts. The output is virtually identical to =--module-overview= in plain text, but slightly more machine-friendly. [endsect] [section --module-overview] =boostdep --module-overview= generates a module overview, in plain text or HTML. The plain text output is of the form [pre Module Overview: accumulators -> array assert circular_buffer concept_check config core fusion iterator mpl numeric~conversion numeric~ublas parameter preprocessor range static_assert throw_exception tuple type_traits typeof algorithm -> array assert bind concept_check config core exception function iterator mpl range regex static_assert tuple type_traits unordered align -> assert config core static_assert throw_exception ] whereas the HTML output is similar to [: *Module Overview* [*/accumulators/] \u21E2 array assert circular_buffer concept_check config core fusion iterator mpl numeric~conversion numeric~ublas parameter preprocessor range static_assert throw_exception tuple type_traits typeof ] where /accumulators/ is a link to =accumulators.html=. As before, if =--track-sources= is given, the =src= subdirectory is scanned for `#include` directives. HTML output is enabled by the =--html= option. The =--title= and =--footer= options set the HTML =<title>= and the page footer and need to precede =--html=, like in the following example: [pre dist/bin/boostdep --title "Module Overview" --footer "Generated on 21.05.2015 20:53:11" --html --module-overview > module-overview.html ] [endsect] [section --module-levels] =boostdep --module-levels= generates a report that groups modules by level. Levels are determined in such a way so that a module of level =N= never depends on modules of levels greater than =N=, and in the absence of cyclic dependencies, doesn't depend on other modules of level =N=. It takes the same options as =--module-overview=. [pre dist/bin/boostdep --title "Module Levels" --footer "Generated on 21.05.2015 20:53:11" --html --module-levels > module-levels.html ] [endsect] [section --module-weights] =boostdep --module-weights= generates a report that lists modules by weight. A module weight is the total number of its dependencies. This includes the indirect dependencies. =--module-weights= takes the same options as =--module-overview=. [pre dist/bin/boostdep --title "Module Weights" --footer "Generated on 21.05.2015 20:53:11" --html --module-weights > module-weights.html ] [endsect] [section --primary] [^boostdep --primary /module/] lists the primary (direct) dependencies of /module/. It takes the same options as =--module-overview=. [pre dist/bin/boostdep --title "Primary Dependencies of filesystem" --footer "Generated on 21.05.2015 20:53:11" --html --primary filesystem > filesystem-primary.html ] [endsect] [section --secondary] [^boostdep --secondary /module/] lists the secondary (indirect) dependencies of /module/. It takes the same options as =--module-overview=. [pre dist/bin/boostdep --title "Secondary Dependencies of filesystem" --footer "Generated on 21.05.2015 20:53:11" --html --secondary filesystem > filesystem-secondary.html ] You can combine =--primary= and =--secondary= in one invocation. [pre dist/bin/boostdep --title "Dependencies of filesystem" --footer "Generated on 21.05.2015 20:53:11" --html --primary filesystem --secondary filesystem > filesystem.html ] [endsect] [section --reverse] [^boostdep --reverse /module/] lists the reverse dependencies of /module/, that is, it lists which modules depend on /module/. It takes the same options as =--module-overview=. [pre dist/bin/boostdep --title "Reverse Dependencies of filesystem" --footer "Generated on 21.05.2015 20:53:11" --html --reverse filesystem > filesystem-reverse.html ] You can combine =--reverse= with =--primary= and =--secondary= for a complete module report. [pre dist/bin/boostdep --title "Dependency Report for filesystem" --footer "Generated on 21.05.2015 20:53:11" --html --primary filesystem --secondary filesystem --reverse filesystem > filesystem.html ] [endsect] [section --subset] [^boostdep --subset /module/] lists the subset dependencies of /module/, that is, it lists which modules comprise the subset which /module/ requires in order to be usable. The dependencies are determined by tracing the =#include= directives starting from /module/'s headers. The difference between using the modules reported by =--subset= and those reported by the sum of =--primary= and =--secondary= is that the former only guarantees that /module/ will be usable, whereas the latter guarantees it for every module in the subset. =--subset= takes the same options as =--module-overview=. [pre dist/bin/boostdep --title "Subset Dependencies of filesystem" --footer "Generated on 21.05.2015 20:53:11" --html --subset filesystem > filesystem-subset.html ] You can combine =--subset= with the other module report options. [endsect] [section --header] [^boostdep --header /header/] creates an inclusion report for /header/. It takes the same options as =--module-overview=. [pre dist/bin/boostdep --title "Inclusion Report for <boost/shared_ptr.hpp>" --footer "Generated on 21.05.2015 20:53:11" --html --header boost/shared_ptr.hpp > header-boost-shared_ptr.html ] [endsect] [section --track-sources] The =--track-sources= option instructs /Boostdep/ to scan the =src= library subdirectory for `#include` directives. By default, only the =include= subdirectory is scanned. [endsect] [section --title] [^--title /title/] sets the contents of the HTML =<title>= tag. It must precede =--html= to have an effect. [endsect] [section --footer] [^--footer /footer/] sets the page footer text. It has no effect if =--html= is not given. [endsect] [section --html] =--html= switches to HTML output mode (the default is plain text). It must precede the commands that generate output. [endsect] [endsect]