summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Moody <daniel.moody@mongodb.com>2021-04-20 17:46:31 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-05-03 15:49:37 +0000
commit44ca57b15d7b94da9e2201a9135982ded21be0ce (patch)
tree685f4850766ad7fe775954cf7d4ba127c5e3863e
parent57f8c0d7c31a5a01c76748ae725591d44e08a2a8 (diff)
downloadmongo-44ca57b15d7b94da9e2201a9135982ded21be0ce.tar.gz
SERVER-56570 added LIBDEPS docs.
-rw-r--r--buildscripts/libdeps/analyzer_unittests.py4
-rwxr-xr-xbuildscripts/libdeps/gacli.py2
-rw-r--r--buildscripts/libdeps/graph_visualizer_web_stack/flask/flask_backend.py3
-rw-r--r--buildscripts/libdeps/libdeps/analyzer.py4
-rw-r--r--buildscripts/libdeps/libdeps/graph.py7
-rw-r--r--docs/build_system.md51
-rw-r--r--docs/build_system_reference.md381
7 files changed, 440 insertions, 12 deletions
diff --git a/buildscripts/libdeps/analyzer_unittests.py b/buildscripts/libdeps/analyzer_unittests.py
index 8061f23e27d..c0f9b3bbd2d 100644
--- a/buildscripts/libdeps/analyzer_unittests.py
+++ b/buildscripts/libdeps/analyzer_unittests.py
@@ -199,7 +199,7 @@ class Tests(unittest.TestCase):
"""Check results of analysis generically."""
analysis = [algo(graph, *args)]
- ga = libdeps.analyzer.LibdepsGraphAnalysis(graph, analysis)
+ ga = libdeps.analyzer.LibdepsGraphAnalysis(analysis)
printer = libdeps.analyzer.GaJsonPrinter(ga)
result = json.loads(printer.get_json())
self.assertEqual(result, expected)
@@ -210,7 +210,7 @@ class Tests(unittest.TestCase):
analysis = libdeps.analyzer.counter_factory(
graph,
[name[0] for name in CountTypes.__members__.items() if name[0] != CountTypes.ALL.name])
- ga = libdeps.analyzer.LibdepsGraphAnalysis(graph, analysis)
+ ga = libdeps.analyzer.LibdepsGraphAnalysis(analysis)
printer = libdeps.analyzer.GaJsonPrinter(ga)
result = json.loads(printer.get_json())
self.assertEqual(result, expected)
diff --git a/buildscripts/libdeps/gacli.py b/buildscripts/libdeps/gacli.py
index 6fcc0d23400..6b0bafb467b 100755
--- a/buildscripts/libdeps/gacli.py
+++ b/buildscripts/libdeps/gacli.py
@@ -217,7 +217,7 @@ def main():
if args.build_data:
analysis.append(libdeps_analyzer.BuildDataReport(libdeps_graph))
- ga = libdeps_analyzer.LibdepsGraphAnalysis(libdeps_graph=libdeps_graph, analysis=analysis)
+ ga = libdeps_analyzer.LibdepsGraphAnalysis(analysis)
if args.format == 'pretty':
ga_printer = libdeps_analyzer.GaPrettyPrinter(ga)
diff --git a/buildscripts/libdeps/graph_visualizer_web_stack/flask/flask_backend.py b/buildscripts/libdeps/graph_visualizer_web_stack/flask/flask_backend.py
index 09f7141560d..af34ffa65a9 100644
--- a/buildscripts/libdeps/graph_visualizer_web_stack/flask/flask_backend.py
+++ b/buildscripts/libdeps/graph_visualizer_web_stack/flask/flask_backend.py
@@ -229,8 +229,7 @@ class BackendServer:
analysis = libdeps.analyzer.counter_factory(
self._dependents_graph,
[name[0] for name in libdeps.analyzer.CountTypes.__members__.items()])
- ga = libdeps.analyzer.LibdepsGraphAnalysis(libdeps_graph=self._dependents_graph,
- analysis=analysis)
+ ga = libdeps.analyzer.LibdepsGraphAnalysis(analysis)
results = ga.get_results()
graph_data = []
diff --git a/buildscripts/libdeps/libdeps/analyzer.py b/buildscripts/libdeps/libdeps/analyzer.py
index 2ddc35662ca..58f18413e0a 100644
--- a/buildscripts/libdeps/libdeps/analyzer.py
+++ b/buildscripts/libdeps/libdeps/analyzer.py
@@ -694,11 +694,9 @@ class BuildDataReport(Analyzer):
class LibdepsGraphAnalysis:
"""Runs the given analysis on the input graph."""
- def __init__(self, libdeps_graph, analysis):
+ def __init__(self, analysis):
"""Perform analysis based off input args."""
- self._libdeps_graph = libdeps_graph
-
self._results = {}
for analyzer in analysis:
analyzer.report(self._results)
diff --git a/buildscripts/libdeps/libdeps/graph.py b/buildscripts/libdeps/libdeps/graph.py
index 3bd047cc79f..078b832ceb2 100644
--- a/buildscripts/libdeps/libdeps/graph.py
+++ b/buildscripts/libdeps/libdeps/graph.py
@@ -176,3 +176,10 @@ class LibdepsGraph(networkx.DiGraph):
self._progressbar = null_progressbar
return self._progressbar
+
+
+def load_libdeps_graph(graph_file):
+ """Load a graphml file and create a LibdepGraph."""
+
+ graph = networkx.read_graphml(graph_file)
+ return LibdepsGraph(graph=graph)
diff --git a/docs/build_system.md b/docs/build_system.md
index d3d1c384591..477d59b05c4 100644
--- a/docs/build_system.md
+++ b/docs/build_system.md
@@ -108,6 +108,7 @@
## Making source changes
### Adding a new dependency
+
### Linting and Lint Targets
#### What lint targets are available?
#### Using `clang-format`
@@ -141,12 +142,54 @@
#### Adding a new module
### `LIBDEPS` and the `LIBDEPS` Linter
#### Why `LIBDEPS`?
-#### `LIBDEPS` vs `LIBDEPS_PRIVATE vs LIBDEPS_INTERFACE`
-#### Reverse edges with `DEPS_DEPENDENTS`
+Libdeps is a subsystem within the build, which is centered around the LIBrary DEPendency graph. It tracks and maintains the dependency graph as well as lints, analyzes and provides useful metrics about the graph.
+#### Different `LIBDEPS` variable types
+The `LIBDEPS` variables are how the library relationships are defined within the build scripts. The primary variables are as follows:
+* `LIBDEPS`:
+ The 'public' type which propagates lower level dependencies onward automatically.
+* `LIBDEPS_PRIVATE`:
+ Creates a dependency only between the target and the dependency.
+* `LIBDEPS_INTERFACE`:
+ Same as `LIBDEPS` but excludes itself from the propagation onward.
+* `LIBDEPS_DEPENDENTS`:
+ Creates a reverse `LIBDEPS_PRIVATE` dependency where the dependency is the one declaring the relationship.
+* `PROGDEPS_DEPENDENTS`:
+ Same as `LIBDEPS_DEPENDENTS` but for use with Program builders.
+
+Libraries are added to these variables as lists per each SCons builder instance in the SConscripts depending on what type of relationship is needed. For more detailed information on theses types, refer to [`The LIBDEPS variables`](build_system_reference.md#the-libdeps-variables)
#### The `LIBDEPS` lint rules and tags
+The libdeps subsystem is capable of linting and automatically detecting issues. Some of these linting rules are automatically checked during build-time (while the SConscripts are read and the build is performed) while others need to be manually run post-build (after the the generated graph file has been built). Some rules will include exemption tags which can be added to a libraries `LIBDEPS_TAGS` to override a rule for that library.
+
+The build-time linter also has a print option `--libdeps-linting=print` which will print all issues without failing the build and ignoring exemption tags. This is useful for getting an idea of what issues are currently outstanding.
+
+For a complete list of build-time lint rules, please refer to [`Build-time Libdeps Linter`](build_system_reference.md#build-time-libdeps-linter)
#### `LIBDEPS_TAGS`
-##### `init-no-global-side-effects`
-#### Using the LIBDEPS Linter
+`LIBDEPS_TAGS` can also be used to supply flags to the libdeps subsystem to do special handling for certain libraries such as exemptions or inclusions for linting rules and also SCons command line expansion functions.
+
+For a full list of tags refer to [`LIBDEPS_TAGS`](build_system_reference.md#libdeps_tags)
+
+#### Using the post-build LIBDEPS Linter
+To use the post-build tools, you must first build the libdeps dependency graph by building the `generate-libdeps-graph` target.
+
+You must also install the requirements file:
+
+```
+python3 -m pip install -r etc/pip/libdeps-requirements.txt
+```
+
+After the graph file is created, it can be used as input into the `gacli` tool to perform linting and analysis on the complete dependency graph. The `gacli` tool has options for what types of analysis to perform. A complete list can be found using the `--help` option. Minimally, you can run the `gacli` tool by just passing the graph file you wish to analyze:
+
+```
+python3 buildscripts/libdeps/gacli.py --graph-file build/cached/libdeps/libdeps.graphml
+```
+
+Another tool which provides a graphical interface as well as visual representation of the graph is the graph visualizer. Minimally, it requires passing in a directory in which any files with the `.graphml` extension will be available for analysis. By default it will launch the web interface which is reachable in a web browser at http://localhost:3000.
+
+```
+python3 buildscripts/libdeps/graph_visualizer.py --graphml-dir build/opt/libdeps
+```
+
+For more information about the details of using the post-build linting tools refer to [`post-build linting and analysis`](build_system_reference.md#post-build-linting-and-analysis)
### Debugging build system failures
#### Using` -k` and `-n`
#### `--debug=[explain, time, stacktrace]`
diff --git a/docs/build_system_reference.md b/docs/build_system_reference.md
index 65cfea7bfea..8a09bbacb3d 100644
--- a/docs/build_system_reference.md
+++ b/docs/build_system_reference.md
@@ -18,9 +18,390 @@
#### Icecream tool
#### ccache tool
### LIBDEPS
+Libdeps is a subsystem within the build, which is centered around the LIBrary DEPendency graph. It tracks and maintains the dependency graph as well as lints, analyzes and provides useful metrics about the graph.
#### Design
+The libdeps subsystem is divided into several stages, described in order of use as follows.
+
+##### SConscript `LIBDEPS` definitions and built time linting
+
+During the build, the SConscripts are read and all the library relationships are setup via the `LIBDEPS` variables. Some issues can be identified early during processing of the SConscripts via the build-time linter. Most of these will be style and usage issues which can be realized without needing the full graph. This component lives within the build and is executed through the SCons emitters added via the libdeps subsystem.
+
+##### Libdeps graph generation for post-build analysis
+
+For more advanced analysis and linting, a full graph is necessary. The build target `generate-libdeps-graph` builds all libdeps and things which use libdeps, and generates the graph to a file in graphml format.
+
+##### The libdeps analyzer python module
+
+The libdeps analyzer module is a python library which provides and Application Programming Interface (API) to analyze and lint the graph. The library internally leverages the networkx python module for the generic graph interfaces.
+
+##### The CLI and Visualizer tools
+
+The libdeps analyzer module is used in the libdeps Graph Analysis Command Line Interface (gacli) tool and the libdeps Graph Visualizer web service. Both tools read in the graph file generated from the build and provide the Human Machine Interface (HMI) for analysis and linting.
+#### The `LIBDEPS` variables
+The variables include several types of lists to be added to libraries per a SCons builder instance:
+
+| Variable | Use |
+| ------------- |-------------|
+| `LIBDEPS` | transitive dependencies |
+| `LIBDEPS_PRIVATE` | local dependencies |
+| `LIBDEPS_INTERFACE` | transitive dependencies excluding self |
+| `LIBDEPS_DEPENDENTS` | reverse dependencies |
+| `PROGDEPS_DEPENDENTS` | reverse dependencies for Programs |
+
+
+_`LIBDEPS`_ is the 'public' type, such that libraries that are added to this list become a dependency of the current library, and also become dependencies of libraries which may depend on the current library. This propagation also includes not just the libraries in the `LIBDEPS` list, but all `LIBDEPS` of those `LIBDEPS` recursively, meaning that all dependencies of the `LIBDEPS` libraries, also become dependencies of the current library and libraries which depend on it.
+
+_`LIBDEPS_PRIVATE`_ should be a list of libraries which creates dependencies only between the current library and the libraries in the list. However, in static linking builds, this will behave the same as `LIBDEPS` due to the nature of static linking.
+
+_`LIBDEPS_INTERFACE`_ is very similar to `LIBDEPS`, however it does not create propagating dependencies for the libraries themselves in the `LIBDEPS_INTERFACE` list. Only the dependencies of those `LIBDEPS_INTERFACE` libraries are propagated forward.
+
+_`LIBDEPS_DEPENDENTS`_ are added to libraries which will force themselves as dependencies of the libraries in the supplied list. This is conceptually a reverse dependency, where the library which is a dependency is the one declaring itself as the dependency of some other library. By default this creates a `LIBDEPS_PRIVATE` like relationship, but a tuple can be used to force it to a `LIBDEPS` like or other relationship.
+
+_`PROGDEPS_DEPENDENTS`_ are the same as `LIBDEPS_DEPENDENTS`, but intended for use only with Program builders.
+
+#### `LIBDEPS_TAGS`
+The `LIBDEPS_TAGS` variable is used to mark certain libdeps for various reasons. Some `LIBDEPS_TAGS` are used to mark certain libraries for `LIBDEPS_TAG_EXPANSIONS` variable which is used to create a function which can expand to a string on the command line. Below is a table of available `LIBDEPS` tags:
+
+| Tag | Description |
+|---|---|
+| `illegal_cyclic_or_unresolved_dependencies_allowlisted` | SCons subst expansion tag to handle dependency cycles |
+| `init-no-global-side-effects` | SCons subst expansion tag for causing linkers to avoid pulling in all symbols |
+| `lint-public-dep-allowed` | Linting exemption tag exempting the `lint-no-public-deps` tag |
+| `lint-no-public-deps` | Linting inclusion tag ensuring a libdep has no `LIBDEPS` declared |
+| `lint-allow-non-alphabetic` | Linting exemption tag allowing `LIBDEPS` variable lists to be non-alphabetic |
+| `lint-leaf-node-allowed-dep` | Linting exemption tag exempting the `lint-leaf-node-no-deps` tag |
+| `lint-leaf-node-no-deps` | Linting inclusion tag ensuring a libdep has no libdeps and is a leaf node |
+| `lint-allow-nonlist-libdeps` | Linting exemption tag allowing a `LIBDEPS` variable to not be a list | `lint-allow-bidirectional-edges` | Linting exemption tag allowing reverse dependencies to also be a forward dependencies |
+| `lint-allow-nonprivate-on-deps-dependents` | Linting exemption tag allowing reverse dependencies to be transitive |
+| `lint-allow-dup-libdeps` | Linting exemption tag allowing `LIBDEPS` variables to contain duplicate libdeps on a given library |
+| `lint-allow-program-links-private` | Linting exemption tag allowing `Program`s to have `PRIVATE_LIBDEPS` |
+
+##### The `illegal_cyclic_or_unresolved_dependencies_allowlisted` tag
+This tag should not be used anymore because the library dependency graph has been successfully converted to a Directed Acyclic Graph (DAG). Prior to this accomplishment, it was necessary to handle
+cycles specifically with platform specific options on the command line.
+
+##### The `init-no-global-side-effects` tag
+Adding this flag to a library turns on platform specific compiler flags which will cause the linker to pull in just the symbols it needs. Note that by default, the build is configured to pull in all symbols from libraries because of the use of static initializers, however if a library is known to not have any of these initializers, then this flag can be added for some performance improvement.
+
#### Linting and linter tags
+The libdeps linter features automatically detect certain classes of LIBDEPS usage errors. The libdeps linters are implemented as build-time linting and post-build linting procedures to maintain order in usage of the libdeps tool and the build’s library dependency graph. You will need to comply with the rules enforced by the libdeps linter, and fix issues that it raises when modifying the build scripts. There are exemption tags to prevent the linter from blocking things, however these exemption tags should only be used in extraordinary cases, and with good reason. A goal of the libdeps linter is to drive and maintain the number of exemption tags in use to zero.
+##### Exemption Tags
+There are a number of existing issues that need to be addressed, but they will be addressed in future tickets. In the meantime, the use of specific strings in the LIBDEPS_TAGS variable can allow the libdeps linter to skip certain issues on given libraries. For example, to have the linter skip enforcement of the lint rule against bidirectional edges for "some_library":
+```
+env.Library(
+ target=’some_library’
+ ...
+ LIBDEPS_TAGS=[‘lint-allow-bidirectional-edges’]
+)
+```
+
+#### build-time Libdeps Linter
+If there is a build-time issue, the build will fail until it is addressed. This linting feature will be on by default and takes about half a second to complete in a full enterprise build (at the time of writing this), but can be turned off by using the --libdeps-linting=off option on your SCons invocation.
+
+The current rules and there exemptions are listed below:
+
+1. **A 'Program' can not link a non-public dependency, it can only have LIBDEPS links.**
+ ###### Example
+
+ ```
+ env.Program(
+ target=’some_program’,
+ ...
+ LIBDEPS=[‘lib1’], # OK
+ LIBDEPS_PRIVATE=[‘lib2’], # This is a Program, BAD
+ )
+ ```
+ ###### Rationale
+
+ A Program can not be linked into anything else, and there for the transitiveness does not apply. A default value of LIBDEPS was selected for consistency since most Program's were already doing this at the time the rule was created.
+
+ ###### Exemption
+ 'lint-allow-program-links-private' on the target node
+ ######
+
+2. **A 'Node' can only directly link a given library once.**
+
+ ###### Example
+
+ ```
+ env.Library(
+ target=’some_library’,
+ ...
+ LIBDEPS=[‘lib1’], # Linked once, OK
+ LIBDEPS_PRIVATE=[‘lib1’], # Also linked in LIBDEPS, BAD
+ LIBDEPS_INTERFACE=[‘lib2’, 'lib2'], # Linked twice, BAD
+ )
+ ```
+ ###### Rationale
+
+ Libdeps will ignore duplicate links, so this rule is mostly for consistency and neatness in the build scripts.
+
+ ###### Exemption
+ 'lint-allow-dup-libdeps' on the target node
+
+ ######
+3. **A 'Node' which uses LIBDEPS_DEPENDENTS or PROGDEPS_DEPENDENTS can only have LIBDEPS_PRIVATE links.**
+ ###### Example
+
+ ```
+ env.Library(
+ target=’some_library’,
+ ...
+ LIBDEPS_DEPENDENTS=['lib3'],
+ LIBDEPS=[‘lib1’], # LIBDEPS_DEPENDENTS is in use, BAD
+ LIBDEPS_PRIVATE=[‘lib2’], # OK
+ )
+ ```
+ ###### Rationale
+
+ The node that the library is using LIBDEPS_DEPENDENTS or PROGDEPS_DEPENDENT to inject its dependency onto should be conditional, therefore there should not be transitiveness for that dependency since it cannot be the source of any resolved symbols.
+ ###### Exemption
+ 'lint-allow-nonprivate-on-deps-dependents' on the target node
+ ######
+4. **A 'Node' can not link directly to a library that uses LIBDEPS_DEPENDENTS or PROGDEPS_DEPENDENTS.**
+ ###### Example
+ ```
+ env.Library(
+ target='other_library',
+ ...
+ LIBDEPS=['lib1'], # BAD, 'lib1' has LIBDEPS_DEPENDENTS
+
+ env.Library(
+ target=’lib1’,
+ ...
+ LIBDEPS_DEPENDENTS=['lib3'],
+ )
+ ```
+ ###### Rationale
+
+ A library that is using LIBDEPS_DEPENDENTS or PROGDEPS_DEPENDENT should only be used for reverse dependency edges. If a node does need to link directly to a library that does have reverse dependency edges, that indicates the library should be split into two separate libraries, containing its direct dependency content and its conditional reverse dependency content.
+
+ ###### Exemption
+ 'lint-allow-bidirectional-edges' on the target node
+ ######
+
+5. **All libdeps environment vars must be assigned as lists.**
+ ###### Example
+ ```
+ env.Library(
+ target='some_library',
+ ...
+ LIBDEPS='lib1', # not a list, BAD
+ LIBDEPS_PRIVATE=['lib2'], # OK
+ )
+ ```
+ ###### Rationale
+
+ Libdeps will handle non-list environment variables, so this is more for consistency and neatness in the build scripts.
+ ###### Exemption
+ 'lint-allow-nonlist-libdeps' on the target node
+ ######
+6. **Libdeps with the tag 'lint-leaf-node-no-deps' shall not link any libdeps.**
+ ###### Example
+
+ ```
+ env.Library(
+ target='lib2',
+ ...
+ LIBDEPS_TAGS=[
+ 'lint-leaf-node-allowed-dep'
+ ]
+ )
+
+ env.Library(
+ target='some_library',
+ ...
+ LIBDEPS=['lib1'], # BAD, should have no LIBDEPS
+ LIBDEPS_PRIVATE=['lib2'], # OK, has exemption tag
+ LIBDEPS_TAGS=[
+ 'lint-leaf-node-no-deps'
+ ]
+ )
+ ```
+
+ ###### Rationale
+
+ The special tag allows certain nodes to be marked and programmatically checked that they remain lead nodes. An example use-case is when we want to make sure certain nodes never link mongodb code.
+
+ ###### Exemption
+ 'lint-leaf-node-allowed-dep' on the exempted libdep
+
+ ###### Inclusion
+ 'lint-leaf-node-no-deps' on the target node
+ ######
+7. **Libdeps with the tag 'lint-no-public-deps' shall not link any libdeps.**
+ ###### Example
+ ```
+ env.Library(
+ target='lib2',
+ ...
+ LIBDEPS_TAGS=[
+ 'lint-public-dep-allowed'
+ ]
+ )
+
+ env.Library(
+ target='some_library',
+ ...
+ LIBDEPS=[
+ 'lib1' # BAD
+ 'lib2' # OK, has exemption tag
+ ],
+ LIBDEPS_TAGS=[
+ 'lint-no-public-deps'
+ ]
+ )
+ ```
+ ###### Rationale
+
+ The special tag allows certain nodes to be marked and programmatically checked that they do not link publicly. Some nodes such as mongod_main have special requirements that this programmatically checks.
+
+ ###### Exemption
+ 'lint-public-dep-allowed' on the exempted libdep
+ ###### Inclusion
+ 'lint-no-public-deps' on the target node
+ ######
+8. **Libdeps shall be sorted alphabetically in LIBDEPS lists in the SCons files.**
+ ###### Example
+ ```
+ env.Library(
+ target='lib2',
+ ...
+ LIBDEPS=[
+ '$BUILD/mongo/db/d', # OK, $ comes before c
+ 'c', # OK, c comes before s
+ 'src/a', # BAD, s should be after b
+ 'b', # BAD, b should be before c
+ ]
+ )
+ ```
+ ###### Rationale
+
+ Keeping the SCons files neat and ordered allows for easier Code Review diffs and generally better maintainability.
+ ###### Exemption
+ 'lint-allow-non-alphabetic' on the exempted libdep
+ ######
+
+
+
+
+##### The build-time print Option
+The libdeps linter also has the `--libdeps-linting=print` option which will perform linting, and instead of failing the build on an issue, just print and continue on. It will also ignore exemption tags, and still print the issue because it will not fail the build. This is a good way to see the entirety of existing issues that are exempted by tags, as well as printing other metrics such as time spent linting.
+
+#### post-build linting and analysis
+
+The dependency graph can be analyzed post-build by leveraging the completeness of the graph to perform more extensive analysis. You will need to install the libdeps requirements file to python when attempting to use the post-build analysis tools:
+
+```
+python3 -m pip install -r etc/pip/libdeps-requirements.txt
+```
+
+The command line interface tool (gacli) has a comprehensive help text which will describe the available analysis options and interface. The visualizer tool includes a GUI which displays the available analysis options graphically. These tools will be briefly covered in the following sections.
+
+##### Generating the graph file
+
+To generate the full graph, build the target `generate-libdeps-graph`. This will build all things involving libdeps and construct a graphml file representing the library dependency graph. The graph can be used in the command line interface tool or the visualizer web service tool. The minimal set of required SCons arguments to build the graph file is shown below:
+
+```
+python3 buildscripts/scons.py --link-model=dynamic --build-tools=next generate-libdeps-graph
+```
+
+The graph file by default will be generate to `build/opt/libdeps/libdeps.graphml` (where `build/opt` is the `$BUILD_DIR`).
+
+##### General libdeps analyzer API usage
+
+Below is a basic example of usage of the libdeps analyzer API:
+
+```
+import libdeps
+
+libdeps_graph = libdeps.graph.load_libdeps_graph('path/to/libdeps.graphml')
+
+list_of_analysis_to_run = [
+ libdeps.analyzer.NodeCounter(libdeps_graph),
+ libdeps.analyzer.DirectDependencies(libdeps_graph, node='path/to/library'),
+]
+
+analysis_results = libdeps.graph.LibdepsGraphAnalysis(list_of_analysis_to_run)
+libdeps.analyzer.GaPrettyPrinter(analysis_after_run).print()
+```
+
+Walking through this example, first the graph is loaded from file. Then a list of desired Analyzer instances is created. Some example analyzer classes are instantiated in the example above, but there are many others to choose from. Specific Analyzers have different interfaces and should be supplied an argument list corresponding to that analyzer.
+
+_Note:_ The graph file will contain the build dir that the graph data was created with and it expects all node arguments to be relative to the build dir. If you are using the libdeps module generically in some app, you can extract the build dir from the libdeps graph and append it to any generic library path.
+
+Once the list of analyzers is created, they can be used to create a LibdepsGraphAnalysis instance, which will upon instantiation, run the analysis list provided. Once the instance is created, it contains the results, and optionally can be fed into different printer classes. In this case, a human readable format printer called GaPrettyPrinter is used to print to the console.
+
+##### Using the gacli tool
+
+The command line interface tool can be used from the command line to run analysis on a given graph. The only required argument is the graph file. The default with no args will run all the counters and linters on the graph. Here is an example output:
+
+```
+(venv) Apr.20 02:46 ubuntu[mongo]: python buildscripts/libdeps/gacli.py --graph-file build/cached/libdeps/libdeps.graphml
+Loading graph data...Loaded!
+
+
+Graph built from git hash:
+1358cdc6ff0e53e4f4c01ea0e6fcf544fa7e1672
+
+Graph Schema version:
+2
+
+Build invocation:
+"/home/ubuntu/venv/bin/python" "buildscripts/scons.py" "--variables-files=etc/scons/mongodbtoolchain_v3_gcc.vars" "--cache=all" "--cache-dir=/home/ubuntu/scons-cache" "--link-model=dynamic" "--build-tools=next" "ICECC=icecc" "CCACHE=ccache" "-j200" "--cache-signature-mode=validate" "--cache-debug=-" "generate-libdeps-graph"
+
+Nodes in Graph: 867
+Edges in Graph: 90706
+Direct Edges in Graph: 5948
+Transitive Edges in Graph: 84758
+Direct Public Edges in Graph: 3483
+Public Edges in Graph: 88241
+Private Edges in Graph: 2440
+Interface Edges in Graph: 25
+Shim Nodes in Graph: 20
+Program Nodes in Graph: 136
+Library Nodes in Graph: 731
+
+LibdepsLinter: PUBLIC libdeps that could be PRIVATE: 0
+```
+
+Use the `--help` option to see detailing information about all the available options.
+
+##### Using the graph visualizer Tool
+
+The graph visualizer tools starts up a web service to provide a frontend GUI for navigating and examining the graph files. The Visualizer uses a Python Flask backend and React/Redux Javascript frontend.
+
+For installing the dependencies for the frontend, you will need node >= 12.0.0 and npm installed and in the PATH. To install the dependencies navigate to directory where package.json lives, and run:
+
+```
+cd buildscripts/libdeps/graph_visualizer_web_stack && npm install
+```
+
+Alternatively if you are on linux, you can use the setup_node_env.sh script to automatically download node 12 and npm, setup the local environment and install the dependencies. Run the command:
+
+```
+source buildscripts/libdeps/graph_visualizer_web_stack/setup_node_end.sh install
+```
+
+Assuming you are on a remote workstation and using defaults, you will need to make ssh tunnels to the web service to access the service in your local browser. The frontend and backend both use a port (this case 3000 is the frontend and 5000 is the backend), and the default host is localhost, so you will need to open two tunnels so the frontend running in your local web browser can communicate with the backend. If you are using the default host and port the tunnel command will look like this:
+
+```
+ssh -L 3000:localhost:3000 -L 5000:localhost:5000 ubuntu@workstation.hostname
+```
+
+Next we need to start the web service. It will require you to pass a directory where it will search for `.graphml` files which contain the graph data for various commits:
+
+```
+python3 buildscripts/libdeps/graph_visualizer.py --graphml-dir build/opt/libdeps
+```
+
+The script will launch the backend and then build the optimized production frontend and launch it. You can supply the `--debug` argument to work in development load which starts up much faster and allows real time updates as files are modified, with a small cost to performance on the frontend. Other options allow more configuration and can be viewed in the `--help` text.
+
+After the server has started up, it should notify you via the terminal that you can access it at http://localhost:3000 locally in your browser.
+
+
+
## Build system configuration
### SCons configuration
#### Frequently used flags and variables