summaryrefslogtreecommitdiff
path: root/site_scons/libdeps_next.py
diff options
context:
space:
mode:
Diffstat (limited to 'site_scons/libdeps_next.py')
-rw-r--r--site_scons/libdeps_next.py1430
1 files changed, 0 insertions, 1430 deletions
diff --git a/site_scons/libdeps_next.py b/site_scons/libdeps_next.py
deleted file mode 100644
index ddf1bc93631..00000000000
--- a/site_scons/libdeps_next.py
+++ /dev/null
@@ -1,1430 +0,0 @@
-"""Extension to SCons providing advanced static library dependency tracking.
-
-These modifications to a build environment, which can be attached to
-StaticLibrary and Program builders via a call to setup_environment(env),
-cause the build system to track library dependencies through static libraries,
-and to add them to the link command executed when building programs.
-
-For example, consider a program 'try' that depends on a lib 'tc', which in
-turn uses a symbol from a lib 'tb' which in turn uses a library from 'ta'.
-
-Without this package, the Program declaration for "try" looks like this:
-
-Program('try', ['try.c', 'path/to/${LIBPREFIX}tc${LIBSUFFIX}',
- 'path/to/${LIBPREFIX}tb${LIBSUFFIX}',
- 'path/to/${LIBPREFIX}ta${LIBSUFFIX}',])
-
-With this library, we can instead write the following
-
-Program('try', ['try.c'], LIBDEPS=['path/to/tc'])
-StaticLibrary('tc', ['c.c'], LIBDEPS=['path/to/tb'])
-StaticLibrary('tb', ['b.c'], LIBDEPS=['path/to/ta'])
-StaticLibrary('ta', ['a.c'])
-
-And the build system will figure out that it needs to link libta.a and libtb.a
-when building 'try'.
-
-A StaticLibrary S may also declare programs or libraries, [L1, ...] to be dependent
-upon S by setting LIBDEPS_DEPENDENTS=[L1, ...], using the same syntax as is used
-for LIBDEPS, except that the libraries and programs will not have LIBPREFIX/LIBSUFFIX
-automatically added when missing.
-"""
-
-# Copyright (c) 2010, Corensic Inc., All Rights Reserved.
-#
-# Permission is hereby granted, free of charge, to any person obtaining
-# a copy of this software and associated documentation files (the
-# "Software"), to deal in the Software without restriction, including
-# without limitation the rights to use, copy, modify, merge, publish,
-# distribute, sublicense, and/or sell copies of the Software, and to
-# permit persons to whom the Software is furnished to do so, subject to
-# the following conditions:
-#
-# The above copyright notice and this permission notice shall be included
-# in all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
-# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
-# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-from collections import defaultdict
-from functools import partial
-import enum
-import copy
-import json
-import os
-import sys
-import glob
-import textwrap
-import hashlib
-import json
-import fileinput
-
-try:
- import networkx
- from buildscripts.libdeps.libdeps.graph import EdgeProps, NodeProps, LibdepsGraph
-except ImportError:
- pass
-
-import SCons.Errors
-import SCons.Scanner
-import SCons.Util
-import SCons
-from SCons.Script import COMMAND_LINE_TARGETS
-
-
-
-class Constants:
- Libdeps = "LIBDEPS"
- LibdepsCached = "LIBDEPS_cached"
- LibdepsDependents = "LIBDEPS_DEPENDENTS"
- LibdepsGlobal = "LIBDEPS_GLOBAL"
- LibdepsNoInherit = "LIBDEPS_NO_INHERIT"
- LibdepsInterface ="LIBDEPS_INTERFACE"
- LibdepsPrivate = "LIBDEPS_PRIVATE"
- LibdepsTags = "LIBDEPS_TAGS"
- LibdepsTagExpansion = "LIBDEPS_TAG_EXPANSIONS"
- MissingLibdep = "MISSING_LIBDEP_"
- ProgdepsDependents = "PROGDEPS_DEPENDENTS"
- SysLibdeps = "SYSLIBDEPS"
- SysLibdepsCached = "SYSLIBDEPS_cached"
- SysLibdepsPrivate = "SYSLIBDEPS_PRIVATE"
-
-
-class deptype(tuple, enum.Enum):
- Global: tuple = (0, 'GLOBAL')
- Public: tuple = (1, 'PUBLIC')
- Private: tuple = (2, 'PRIVATE')
- Interface: tuple = (3, 'INTERFACE')
-
- def __lt__(self, other):
- if self.__class__ is other.__class__:
- return self.value[0] < other.value[0]
- return NotImplemented
-
- def __str__(self):
- return self.value[1]
-
- def __int__(self):
- return self.value[0]
-
-
-class dependency:
- def __init__(self, value, deptype, listed_name):
- self.target_node = value
- self.dependency_type = deptype
- self.listed_name = listed_name
-
- def __str__(self):
- return str(self.target_node)
-
-
-class FlaggedLibdep:
- """
- Utility class used for processing prefix and postfix flags on libdeps. The class
- can keep track of separate lists for prefix and postfix as well separators,
- allowing for modifications to the lists and then re-application of the flags with
- modifications to a larger list representing the link line.
- """
-
- def __init__(self, libnode=None, env=None, start_index=None):
- """
- The libnode should be a Libdep SCons node, and the env is the target env in
- which the target has a dependency on the libdep. The start_index is important as
- it determines where this FlaggedLibdep starts in the larger list of libdeps.
-
- The start_index will cut the larger list, and then re-apply this libdep with flags
- at that location. This class will exract the prefix and postfix flags
- from the Libdep nodes env.
- """
- self.libnode = libnode
- self.env = env
-
- # We need to maintain our own copy so as not to disrupt the env's original list.
- try:
- self.prefix_flags = copy.copy(getattr(libnode.attributes, 'libdeps_prefix_flags', []))
- self.postfix_flags = copy.copy(getattr(libnode.attributes, 'libdeps_postfix_flags', []))
- except AttributeError:
- self.prefix_flags = []
- self.postfix_flags = []
-
- self.start_index = start_index
-
- def __str__(self):
- return str(self.libnode)
-
- def add_lib_to_result_list(self, result):
- """
- This function takes in the current list of libdeps for a given target, and will
- apply the libdep taking care of the prefix, postfix and any required separators when
- adding to the list.
- """
- if self.start_index != None:
- result[:] = result[:self.start_index]
- self._add_lib_and_flags(result)
-
- def _get_separators(self, flags):
-
- separated_list = []
-
- for flag in flags:
- separators = self.env.get('LIBDEPS_FLAG_SEPARATORS', {}).get(flag, {})
- separated_list.append(separators.get('prefix', ' '))
- separated_list.append(flag)
- separated_list.append(separators.get('suffix', ' '))
-
- return separated_list
-
- def _get_lib_with_flags(self):
-
- lib_and_flags = []
-
- lib_and_flags += self._get_separators(self.prefix_flags)
- lib_and_flags += [str(self)]
- lib_and_flags += self._get_separators(self.postfix_flags)
-
- return lib_and_flags
-
- def _add_lib_and_flags(self, result):
- """
- This function will clean up the flags for the link line after extracting everything
- from the environment. This will mostly look for separators that are just a space, and
- remove them from the list, as the final link line will add spaces back for each item
- in the list. It will take to concat flags where the separators don't allow for a space.
- """
- next_contig_str = ''
-
- for item in self._get_lib_with_flags():
- if item != ' ':
- next_contig_str += item
- else:
- if next_contig_str:
- result.append(next_contig_str)
- next_contig_str = ''
-
- if next_contig_str:
- result.append(next_contig_str)
-
-
-
-class LibdepLinter:
- """
- This class stores the rules for linting the libdeps. Using a decorator,
- new rules can easily be added to the class, and will be called when
- linting occurs. Each rule is run on each libdep.
-
- When a rule is broken, a LibdepLinterError exception will be raised.
- Optionally the class can be configured to print the error message and
- keep going with the build.
-
- Each rule should provide a method to skip that rule on a given node,
- by supplying the correct flag in the LIBDEPS_TAG environment var for
- that node.
-
- """
-
- skip_linting = False
- print_linter_errors = False
-
- linting_time = 0
- linting_infractions = 0
- linting_rules_run = 0
- registered_linting_time = False
-
- dangling_dep_dependents = set()
-
- @staticmethod
- def _make_linter_decorator():
- """
- This is used for gathering the functions
- by decorator that will be used for linting a given libdep.
- """
-
- funcs = {}
- def linter_rule_func(func):
- funcs[func.__name__] = func
- return func
-
- linter_rule_func.all = funcs
- return linter_rule_func
-
- linter_rule = _make_linter_decorator.__func__()
- linter_final_check = _make_linter_decorator.__func__()
-
- @classmethod
- def _skip_linting(cls):
- return cls.skip_linting
-
- @classmethod
- def _start_timer(cls):
- # Record time spent linting if we are in print mode.
- if cls.print_linter_errors:
- from timeit import default_timer as timer
- return timer()
-
- @classmethod
- def _stop_timer(cls, start, num_rules):
- # Record time spent linting if we are in print mode.
- if cls.print_linter_errors:
- from timeit import default_timer as timer
- cls.linting_time += timer() - start
- cls.linting_rules_run += num_rules
-
- def __init__(self, env, target=None):
- self.env = env
- self.target = target
- self.unique_libs = set()
- self._libdeps_types_previous = dict()
-
-
- # If we are in print mode, we will record some linting metrics,
- # and print the results at the end of the build.
- if self.__class__.print_linter_errors and not self.__class__.registered_linting_time:
- import atexit
- def print_linting_time():
- print(f"Spent {self.__class__.linting_time} seconds linting libdeps.")
- print(f"Found {self.__class__.linting_infractions} issues out of {self.__class__.linting_rules_run} libdeps rules checked.")
- atexit.register(print_linting_time)
- self.__class__.registered_linting_time = True
-
- def lint_libdeps(self, libdeps):
- """
- Lint the given list of libdeps for all
- rules.
- """
-
- # Build performance optimization if you
- # are sure your build is clean.
- if self._skip_linting():
- return
- start = self._start_timer()
-
- linter_rules = [
- getattr(self, linter_rule)
- for linter_rule in self.linter_rule.all
- ]
-
- for libdep in libdeps:
- for linter_rule in linter_rules:
- linter_rule(libdep)
-
- self._stop_timer(start, len(linter_rules)*len(libdeps))
-
- def final_checks(self):
- # Build performance optimization if you
- # are sure your build is clean.
- if self._skip_linting():
- return
- start = self._start_timer()
-
- linter_rules = [
- getattr(self.__class__, rule)
- for rule in self.__class__.linter_final_check.all
- ]
-
- for linter_rule in linter_rules:
- linter_rule(self)
-
- self._stop_timer(start, len(linter_rules))
-
- def _raise_libdep_lint_exception(self, message):
- """
- Raises the LibdepLinterError exception or if configure
- to do so, just prints the error.
- """
- prefix = "LibdepLinter: \n\t"
- message = prefix + message.replace('\n', '\n\t') + '\n'
- if self.__class__.print_linter_errors:
- self.__class__.linting_infractions += 1
- print(message)
- else:
- raise LibdepLinterError(message)
-
- def _check_for_lint_tags(self, lint_tag, env=None, inclusive_tag=False):
- """
- Used to get the lint tag from the environment,
- and if printing instead of raising exceptions,
- will ignore the tags.
- """
-
- # If print mode is on, we want to make sure to bypass checking
- # exclusive tags so we can make sure the exceptions are not excluded
- # and are printed. If it's an inclusive tag, we want to ignore this
- # early return completely, because we want to make sure the node
- # gets included for checking, and the exception gets printed.
- if not inclusive_tag and self.__class__.print_linter_errors:
- return False
-
- target_env = env if env else self.env
-
- if lint_tag in target_env.get(Constants.LibdepsTags, []):
- return True
-
- def _get_deps_dependents(self, env=None):
- """ util function to get all types of DEPS_DEPENDENTS"""
- target_env = env if env else self.env
- deps_dependents = target_env.get(Constants.LibdepsDependents, []).copy()
- deps_dependents += target_env.get(Constants.ProgdepsDependents, [])
- return deps_dependents
-
- def _get_deps_dependents_with_types(self, builder, type):
- return [
- (dependent[0], builder) if isinstance(dependent, tuple) else
- (dependent, builder)
- for dependent in self.env.get(type, [])
- ]
-
- @linter_rule
- def linter_rule_leaf_node_no_deps(self, libdep):
- """
- LIBDEP RULE:
- Nodes marked explicitly as a leaf node should not have any dependencies,
- unless those dependencies are explicitly marked as allowed as leaf node
- dependencies.
- """
- if not self._check_for_lint_tags('lint-leaf-node-no-deps', inclusive_tag=True):
- return
-
- # Ignore dependencies that explicitly exempt themselves.
- if self._check_for_lint_tags('lint-leaf-node-allowed-dep', libdep.target_node.env):
- return
-
- # Global dependencies will apply to leaf nodes, so they should
- # be automatically exempted.
- if libdep.dependency_type == deptype.Global:
- return
-
- target_type = self.target[0].builder.get_name(self.env)
- lib = os.path.basename(str(libdep))
- self._raise_libdep_lint_exception(
- textwrap.dedent(f"""\
- {target_type} '{self.target[0]}' has dependency '{lib}' and is marked explicitly as a leaf node,
- and '{lib}' does not exempt itself as an exception to the rule."""
- ))
-
- @linter_rule
- def linter_rule_no_dangling_deps(self, libdep):
- """
- LIBDEP RULE:
- All reverse dependency edges must point to a node which will be built.
- """
- if self._check_for_lint_tags('lint-allow-dangling-dep-dependent'):
- return
-
- # Gather the DEPS_DEPENDENTS and store them for a final check to make sure they were
- # eventually defined as being built by some builder
- libdep_libbuilder = self.target[0].builder.get_name(self.env)
- deps_depends = self._get_deps_dependents_with_types(libdep_libbuilder, Constants.LibdepsDependents)
- deps_depends += self._get_deps_dependents_with_types("Program", Constants.ProgdepsDependents)
- self.__class__.dangling_dep_dependents.update(deps_depends)
-
- @linter_final_check
- def linter_rule_no_dangling_dep_final_check(self):
- # At this point the SConscripts have defined all the build items,
- # and so we can go check any DEPS_DEPENDENTS listed and make sure a builder
- # was instanciated to build them.
- for dep_dependent in self.__class__.dangling_dep_dependents:
- dep_node = _get_node_with_ixes(self.env, dep_dependent[0], dep_dependent[1])
- if not dep_node.has_builder():
- self._raise_libdep_lint_exception(
- textwrap.dedent(f"""\
- Found reverse dependency linked to node '{dep_node}'
- which will never be built by any builder.
- Remove the reverse dependency or add a way to build it."""
- ))
-
- @linter_rule
- def linter_rule_no_public_deps(self, libdep):
- """
- LIBDEP RULE:
- Nodes explicitly marked as not allowed to have public dependencies, should not
- have public dependencies, unless the dependency is explicitly marked as allowed.
- """
- if not self._check_for_lint_tags('lint-no-public-deps', inclusive_tag=True):
- return
-
- if libdep.dependency_type not in (deptype.Global, deptype.Private):
- # Check if the libdep exempts itself from this rule.
- if self._check_for_lint_tags('lint-public-dep-allowed', libdep.target_node.env):
- return
-
- target_type = self.target[0].builder.get_name(self.env)
- lib = os.path.basename(str(libdep))
- self._raise_libdep_lint_exception(
- textwrap.dedent(f"""\
- {target_type} '{self.target[0]}' has public dependency '{lib}'
- while being marked as not allowed to have public dependencies
- and '{lib}' does not exempt itself."""
- ))
-
- @linter_rule
- def linter_rule_no_dups(self, libdep):
- """
- LIBDEP RULE:
- A given node shall not link the same LIBDEP across public, private
- or interface dependency types because it is ambiguous and unnecessary.
- """
- if self._check_for_lint_tags('lint-allow-dup-libdeps'):
- return
-
- if str(libdep) in self.unique_libs:
- target_type = self.target[0].builder.get_name(self.env)
- lib = os.path.basename(str(libdep))
- self._raise_libdep_lint_exception(
- f"{target_type} '{self.target[0]}' links '{lib}' multiple times."
- )
-
- self.unique_libs.add(str(libdep))
-
- @linter_rule
- def linter_rule_alphabetic_deps(self, libdep):
- """
- LIBDEP RULE:
- Libdeps shall be listed alphabetically by type in the SCons files.
- """
-
- if self._check_for_lint_tags('lint-allow-non-alphabetic'):
- return
-
- # Start checking order after the first item in the list is recorded to compare with.
- if libdep.dependency_type in self._libdeps_types_previous:
- if self._libdeps_types_previous[libdep.dependency_type] > libdep.listed_name:
- target_type = self.target[0].builder.get_name(self.env)
- self._raise_libdep_lint_exception(
- f"{target_type} '{self.target[0]}' has '{libdep.listed_name}' listed in {dep_type_to_env_var[libdep.dependency_type]} out of alphabetical order."
- )
-
- self._libdeps_types_previous[libdep.dependency_type] = libdep.listed_name
-
- @linter_rule
- def linter_rule_programs_link_private(self, libdep):
- """
- LIBDEP RULE:
- All Programs shall only have public dependency's
- because a Program will never be a dependency of another Program
- or Library, and LIBDEPS transitiveness does not apply. Public
- transitiveness has no meaning in this case and is used just as default.
- """
- if self._check_for_lint_tags('lint-allow-program-links-private'):
- return
-
- if (self.target[0].builder.get_name(self.env) == "Program"
- and libdep.dependency_type not in (deptype.Global, deptype.Public)):
-
- lib = os.path.basename(str(libdep))
- self._raise_libdep_lint_exception(
- textwrap.dedent(f"""\
- Program '{self.target[0]}' links non-public library '{lib}'
- A 'Program' can only have {Constants.Libdeps} libs,
- not {Constants.LibdepsPrivate} or {Constants.LibdepsInterface}."""
- ))
-
- @linter_rule
- def linter_rule_no_bidirectional_deps(self, libdep):
- """
- LIBDEP RULE:
- And Library which issues reverse dependencies, shall not be directly
- linked to by another node, to prevent forward and reverse linkages existing
- at the same node. Instead the content of the library that needs to issue reverse
- dependency needs to be separated from content that needs direct linkage into two
- separate libraries, which can be linked correctly respectively.
- """
-
- if not libdep.target_node.env:
- return
- elif self._check_for_lint_tags('lint-allow-bidirectional-edges', libdep.target_node.env):
- return
- elif len(self._get_deps_dependents(libdep.target_node.env)) > 0:
-
- target_type = self.target[0].builder.get_name(self.env)
- lib = os.path.basename(str(libdep))
- self._raise_libdep_lint_exception(textwrap.dedent(f"""\
- {target_type} '{self.target[0]}' links directly to a reverse dependency node '{lib}'
- No node can link directly to a node that has {Constants.LibdepsDependents} or {Constants.ProgdepsDependents}."""
- ))
-
- @linter_rule
- def linter_rule_nonprivate_on_deps_dependents(self, libdep):
- """
- LIBDEP RULE:
- A Library that issues reverse dependencies, shall not link libraries
- with any kind of transitiveness, and will only link libraries privately.
- This is because functionality that requires reverse dependencies should
- not be transitive.
- """
- if self._check_for_lint_tags('lint-allow-nonprivate-on-deps-dependents'):
- return
-
- if (libdep.dependency_type != deptype.Private and libdep.dependency_type != deptype.Global
- and len(self._get_deps_dependents()) > 0):
-
- target_type = self.target[0].builder.get_name(self.env)
- lib = os.path.basename(str(libdep))
- self._raise_libdep_lint_exception(textwrap.dedent(f"""\
- {target_type} '{self.target[0]}' links non-private libdep '{lib}' and has a reverse dependency.
- A {target_type} can only have {Constants.LibdepsPrivate} depends if it has {Constants.LibdepsDependents} or {Constants.ProgdepsDependents}."""
- ))
-
- @linter_rule
- def linter_rule_libdeps_must_be_list(self, libdep):
- """
- LIBDEP RULE:
- LIBDEPS, LIBDEPS_PRIVATE, and LIBDEPS_INTERFACE must be set as lists in the
- environment.
- """
- if self._check_for_lint_tags('lint-allow-nonlist-libdeps'):
- return
-
- libdeps_vars = list(dep_type_to_env_var.values()) + [
- Constants.LibdepsDependents,
- Constants.ProgdepsDependents]
-
- for dep_type_val in libdeps_vars:
-
- libdeps_list = self.env.get(dep_type_val, [])
- if not SCons.Util.is_List(libdeps_list):
-
- target_type = self.target[0].builder.get_name(self.env)
- self._raise_libdep_lint_exception(textwrap.dedent(f"""\
- Found non-list type '{libdeps_list}' while evaluating {dep_type_val[1]} for {target_type} '{self.target[0]}'
- {dep_type_val[1]} must be setup as a list."""
- ))
-
-dependency_visibility_ignored = {
- deptype.Global: deptype.Public,
- deptype.Interface: deptype.Public,
- deptype.Public: deptype.Public,
- deptype.Private: deptype.Public,
-}
-
-dependency_visibility_honored = {
- deptype.Global: deptype.Private,
- deptype.Interface: deptype.Interface,
- deptype.Public: deptype.Public,
- deptype.Private: deptype.Private,
-}
-
-dep_type_to_env_var = {
- deptype.Global: Constants.LibdepsGlobal,
- deptype.Interface: Constants.LibdepsInterface,
- deptype.Public: Constants.Libdeps,
- deptype.Private: Constants.LibdepsPrivate,
-}
-
-class DependencyCycleError(SCons.Errors.UserError):
- """Exception representing a cycle discovered in library dependencies."""
-
- def __init__(self, first_node):
- super(DependencyCycleError, self).__init__()
- self.cycle_nodes = [first_node]
-
- def __str__(self):
- return "Library dependency cycle detected: " + " => ".join(
- str(n) for n in self.cycle_nodes
- )
-
-class LibdepLinterError(SCons.Errors.UserError):
- """Exception representing a discongruent usages of libdeps"""
-
-class MissingSyslibdepError(SCons.Errors.UserError):
- """Exception representing a discongruent usages of libdeps"""
-
-def _get_sorted_direct_libdeps(node):
- direct_sorted = getattr(node.attributes, "libdeps_direct_sorted", None)
- if direct_sorted is None:
- direct = getattr(node.attributes, "libdeps_direct", [])
- direct_sorted = sorted(direct, key=lambda t: str(t.target_node))
- setattr(node.attributes, "libdeps_direct_sorted", direct_sorted)
- return direct_sorted
-
-
-class LibdepsVisitationMark(enum.IntEnum):
- UNMARKED = 0
- MARKED_PRIVATE = 1
- MARKED_PUBLIC = 2
-
-
-def _libdeps_visit_private(n, marked, walking, debug=False):
- if marked[n.target_node] >= LibdepsVisitationMark.MARKED_PRIVATE:
- return
-
- if n.target_node in walking:
- raise DependencyCycleError(n.target_node)
-
- walking.add(n.target_node)
-
- try:
- for child in _get_sorted_direct_libdeps(n.target_node):
- _libdeps_visit_private(child, marked, walking)
-
- marked[n.target_node] = LibdepsVisitationMark.MARKED_PRIVATE
-
- except DependencyCycleError as e:
- if len(e.cycle_nodes) == 1 or e.cycle_nodes[0] != e.cycle_nodes[-1]:
- e.cycle_nodes.insert(0, n.target_node)
- raise
-
- finally:
- walking.remove(n.target_node)
-
-
-def _libdeps_visit(n, tsorted, marked, walking, debug=False):
- # The marked dictionary tracks which sorts of visitation a node
- # has received. Values for a given node can be UNMARKED/absent,
- # MARKED_PRIVATE, or MARKED_PUBLIC. These are to be interpreted as
- # follows:
- #
- # 0/UNMARKED: Node is not not marked.
- #
- # MARKED_PRIVATE: Node has only been explored as part of looking
- # for cycles under a LIBDEPS_PRIVATE edge.
- #
- # MARKED_PUBLIC: Node has been explored and any of its transiive
- # dependencies have been incorporated into `tsorted`.
- #
- # The __libdeps_visit_private function above will only mark things
- # at with MARKED_PRIVATE, while __libdeps_visit will mark things
- # MARKED_PUBLIC.
- if marked[n.target_node] == LibdepsVisitationMark.MARKED_PUBLIC:
- return
-
- # The walking set is used for cycle detection. We record all our
- # predecessors in our depth-first search, and if we observe one of
- # our predecessors as a child, we know we have a cycle.
- if n.target_node in walking:
- raise DependencyCycleError(n.target_node)
-
- walking.add(n.target_node)
-
- if debug:
- print(f" * {n.dependency_type} => {n.listed_name}")
-
- try:
- children = _get_sorted_direct_libdeps(n.target_node)
-
- # We first walk all of our public dependencies so that we can
- # put full marks on anything that is in our public transitive
- # graph. We then do a second walk into any private nodes to
- # look for cycles. While we could do just one walk over the
- # children, it is slightly faster to do two passes, since if
- # the algorithm walks into a private edge early, it would do a
- # lot of non-productive (except for cycle checking) walking
- # and marking, but if another public path gets into that same
- # subtree, then it must walk and mark it again to raise it to
- # the public mark level. Whereas, if the algorithm first walks
- # the whole public tree, then those are all productive marks
- # and add to tsorted, and then the private walk will only need
- # to examine those things that are only reachable via private
- # edges.
-
- for child in children:
- if child.dependency_type != deptype.Private:
- _libdeps_visit(child, tsorted, marked, walking, debug)
-
- for child in children:
- if child.dependency_type == deptype.Private:
- _libdeps_visit_private(child, marked, walking, debug)
-
- marked[n.target_node] = LibdepsVisitationMark.MARKED_PUBLIC
- tsorted.append(n.target_node)
-
- except DependencyCycleError as e:
- if len(e.cycle_nodes) == 1 or e.cycle_nodes[0] != e.cycle_nodes[-1]:
- e.cycle_nodes.insert(0, n.target_node)
- raise
-
- finally:
- walking.remove(n.target_node)
-
-
-def _get_libdeps(node, debug=False):
- """Given a SCons Node, return its library dependencies, topologically sorted.
-
- Computes the dependencies if they're not already cached.
- """
-
- cache = getattr(node.attributes, Constants.LibdepsCached, None)
- if cache is not None:
- if debug:
- print(" Cache:")
- for dep in cache:
- print(f" * {str(dep)}")
- return cache
-
- if debug:
- print(f" Edges:")
-
- tsorted = []
-
- marked = defaultdict(lambda: LibdepsVisitationMark.UNMARKED)
- walking = set()
-
- for child in _get_sorted_direct_libdeps(node):
- if child.dependency_type != deptype.Interface:
- _libdeps_visit(child, tsorted, marked, walking, debug=debug)
- tsorted.reverse()
-
- setattr(node.attributes, Constants.LibdepsCached, tsorted)
- return tsorted
-
-
-def _missing_syslib(name):
- return Constants.MissingLibdep + name
-
-
-def update_scanner(env, builder_name=None, debug=False):
- """Update the scanner for "builder" to also scan library dependencies."""
-
- builder = env["BUILDERS"][builder_name]
- old_scanner = builder.target_scanner
-
- if old_scanner:
- path_function = old_scanner.path_function
- else:
- path_function = None
-
- def new_scanner(node, env, path=()):
- if debug:
- print(f"LIBDEPS SCANNER: {str(node)}")
- print(f" Declared dependencies:")
- print(f" global: {env.get(Constants.LibdepsGlobal, None)}")
- print(f" private: {env.get(Constants.LibdepsPrivate, None)}")
- print(f" public: {env.get(Constants.Libdeps, None)}")
- print(f" interface: {env.get(Constants.LibdepsInterface, None)}")
- print(f" no_inherit: {env.get(Constants.LibdepsNoInherit, None)}")
-
- if old_scanner:
- result = old_scanner.function(node, env, path)
- else:
- result = []
- result.extend(_get_libdeps(node, debug=debug))
- if debug:
- print(f" Build dependencies:")
- print('\n'.join([' * ' + str(t) for t in result]))
- print('\n')
- return result
-
- builder.target_scanner = SCons.Scanner.Scanner(
- function=new_scanner, path_function=path_function
- )
-
-
-def get_libdeps(source, target, env, for_signature, debug=False):
- """Implementation of the special _LIBDEPS environment variable.
-
- Expands to the library dependencies for a target.
- """
-
- target = env.Flatten([target])
- return _get_libdeps(target[0], debug=debug)
-
-
-def get_libdeps_objs(source, target, env, for_signature, debug=False):
- objs = []
- for lib in get_libdeps(source, target, env, for_signature, debug=debug):
- # This relies on Node.sources being order stable build-to-build.
- objs.extend(lib.sources)
- return objs
-
-
-def stringify_deps(env, deps):
- lib_link_prefix = env.subst("$LIBLINKPREFIX")
- lib_link_suffix = env.subst("$LIBLINKSUFFIX")
-
- # Elements of libdeps are either strings (str or unicode), or they're File objects.
- # If they're File objects, they can be passed straight through. If they're strings,
- # they're believed to represent library short names, that should be prefixed with -l
- # or the compiler-specific equivalent. I.e., 'm' becomes '-lm', but 'File("m.a") is passed
- # through whole cloth.
- return [f"{lib_link_prefix}{d}{lib_link_suffix}" if isinstance(d, str) else d for d in deps]
-
-
-def get_syslibdeps(source, target, env, for_signature, debug=False, shared=True):
- """ Given a SCons Node, return its system library dependencies.
-
- These are the dependencies listed with SYSLIBDEPS, and are linked using -l.
- """
-
- deps = getattr(target[0].attributes, Constants.SysLibdepsCached, None)
- if deps is None:
-
- # Get the syslibdeps for the current node
- deps = target[0].get_env().Flatten(copy.copy(target[0].get_env().get(Constants.SysLibdepsPrivate)) or [])
- deps += target[0].get_env().Flatten(target[0].get_env().get(Constants.SysLibdeps) or [])
-
- for lib in _get_libdeps(target[0]):
-
- # For each libdep get its syslibdeps, and then check to see if we can
- # add it to the deps list. For static build we will also include private
- # syslibdeps to be transitive. For a dynamic build we will only make
- # public libdeps transitive.
- syslibs = []
- if not shared:
- syslibs += lib.get_env().get(Constants.SysLibdepsPrivate) or []
- syslibs += lib.get_env().get(Constants.SysLibdeps) or []
-
- # Validate the libdeps, a configure check has already checked what
- # syslibdeps are available so we can hard fail here if a syslibdep
- # is being attempted to be linked with.
- for syslib in syslibs:
- if not syslib:
- continue
-
- if isinstance(syslib, str) and syslib.startswith(Constants.MissingLibdep):
- raise MissingSyslibdepError(textwrap.dedent(f"""\
- LibdepsError:
- Target '{str(target[0])}' depends on the availability of a
- system provided library for '{syslib[len(Constants.MissingLibdep):]}',
- but no suitable library was found during configuration."""
- ))
-
- deps.append(syslib)
-
- setattr(target[0].attributes, Constants.SysLibdepsCached, deps)
- return stringify_deps(env, deps)
-
-
-def _append_direct_libdeps(node, prereq_nodes):
- # We do not bother to decorate nodes that are not actual Objects
- if type(node) == str:
- return
- if getattr(node.attributes, "libdeps_direct", None) is None:
- node.attributes.libdeps_direct = []
- node.attributes.libdeps_direct.extend(prereq_nodes)
-
-
-def _get_flagged_libdeps(source, target, env, for_signature):
- for lib in get_libdeps(source, target, env, for_signature):
- # Make sure lib is a Node so we can get the env to check for flags.
- libnode = lib
- if not isinstance(lib, (str, SCons.Node.FS.File, SCons.Node.FS.Entry)):
- libnode = env.File(lib)
-
- # Create a libdep and parse the prefix and postfix (and separators if any)
- # flags from the environment.
- cur_lib = FlaggedLibdep(libnode, env)
- yield cur_lib
-
-
-def _get_node_with_ixes(env, node, node_builder_type):
- """
- Gets the node passed in node with the correct ixes applied
- for the given builder type.
- """
-
- if not node:
- return node
-
- node_builder = env["BUILDERS"][node_builder_type]
- node_factory = node_builder.target_factory or env.File
-
- # Cache the 'ixes' in a function scope global so we don't need
- # to run SCons performance intensive 'subst' each time
- cache_key = (id(env), node_builder_type)
- try:
- prefix, suffix = _get_node_with_ixes.node_type_ixes[cache_key]
- except KeyError:
- prefix = node_builder.get_prefix(env)
- suffix = node_builder.get_suffix(env)
-
- # TODO(SERVER-50681): Find a way to do this that doesn't hard
- # code these extensions. See the code review for SERVER-27507
- # for additional discussion.
- if suffix == ".dll":
- suffix = ".lib"
-
- _get_node_with_ixes.node_type_ixes[cache_key] = (prefix, suffix)
-
- node_with_ixes = SCons.Util.adjustixes(node, prefix, suffix)
- return node_factory(node_with_ixes)
-
-_get_node_with_ixes.node_type_ixes = dict()
-
-def add_node_from(env, node):
-
- env.GetLibdepsGraph().add_nodes_from([(
- str(node.abspath),
- {
- NodeProps.bin_type.name: node.builder.get_name(env),
- })])
-
-def add_edge_from(env, from_node, to_node, visibility, direct):
-
- env.GetLibdepsGraph().add_edges_from([(
- from_node,
- to_node,
- {
- EdgeProps.direct.name: direct,
- EdgeProps.visibility.name: int(visibility)
- })])
-
-def add_libdeps_node(env, target, libdeps):
-
- if str(target).endswith(env["SHLIBSUFFIX"]):
- node = _get_node_with_ixes(env, str(target.abspath), target.get_builder().get_name(env))
- add_node_from(env, node)
-
- for libdep in libdeps:
- if str(libdep.target_node).endswith(env["SHLIBSUFFIX"]):
- add_edge_from(
- env,
- str(node.abspath),
- str(libdep.target_node.abspath),
- visibility=libdep.dependency_type,
- direct=True)
-
-
-def get_libdeps_nodes(env, target, builder, debug=False, visibility_map=None):
- if visibility_map is None:
- visibility_map = dependency_visibility_ignored
-
- if not SCons.Util.is_List(target):
- target = [target]
-
- # Get the current list of nodes not to inherit on each target
- no_inherit = set(env.get(Constants.LibdepsNoInherit, []))
-
- # Get all the libdeps from the env so we can
- # can append them to the current target_node.
- libdeps = []
- for dep_type in sorted(visibility_map.keys()):
-
- if dep_type == deptype.Global:
- if any("conftest" in str(t) for t in target):
- # Ignore global dependencies for conftests
- continue
-
- # Libraries may not be stored as a list in the env,
- # so we must convert single library strings to a list.
- libs = env.get(dep_type_to_env_var[dep_type], []).copy()
- if not SCons.Util.is_List(libs):
- libs = [libs]
-
- for lib in libs:
- if not lib:
- continue
-
- lib_with_ixes = _get_node_with_ixes(env, lib, builder)
-
- if lib in no_inherit:
- if debug and not any("conftest" in str(t) for t in target):
- print(f" {dep_type[1]} =/> {lib}")
-
- else:
- if debug and not any("conftest" in str(t) for t in target):
- print(f" {dep_type[1]} => {lib}")
-
- libdeps.append(dependency(lib_with_ixes, dep_type, lib))
-
- return libdeps
-
-
-def libdeps_emitter(target, source, env, debug=False, builder=None, visibility_map=None, ignore_progdeps=False):
- """SCons emitter that takes values from the LIBDEPS environment variable and
- converts them to File node objects, binding correct path information into
- those File objects.
-
- Emitters run on a particular "target" node during the initial execution of
- the SConscript file, rather than during the later build phase. When they
- run, the "env" environment's working directory information is what you
- expect it to be -- that is, the working directory is considered to be the
- one that contains the SConscript file. This allows specification of
- relative paths to LIBDEPS elements.
-
- This emitter also adds LIBSUFFIX and LIBPREFIX appropriately.
-
- NOTE: For purposes of LIBDEPS_DEPENDENTS propagation, only the first member
- of the "target" list is made a prerequisite of the elements of LIBDEPS_DEPENDENTS.
- """
-
- if visibility_map is None:
- visibility_map = dependency_visibility_ignored
-
- if debug and not any("conftest" in str(t) for t in target):
- print(f"LIBDEPS EMITTER: {str(target[0])}")
- print(f" Declared dependencies:")
- print(f" global: {env.get(Constants.LibdepsGlobal, None)}")
- print(f" private: {env.get(Constants.LibdepsPrivate, None)}")
- print(f" public: {env.get(Constants.Libdeps, None)}")
- print(f" interface: {env.get(Constants.LibdepsInterface, None)}")
- print(f" no_inherit: {env.get(Constants.LibdepsNoInherit, None)}")
- print(f" Edges:")
-
- libdeps = get_libdeps_nodes(env, target, builder, debug, visibility_map)
-
- if debug and not any("conftest" in str(t) for t in target):
- print(f"\n")
-
- # Lint the libdeps to make sure they are following the rules.
- # This will skip some or all of the checks depending on the options
- # and LIBDEPS_TAGS used.
- if not any("conftest" in str(t) for t in target):
- LibdepLinter(env, target).lint_libdeps(libdeps)
-
- if env.get('SYMBOLDEPSSUFFIX', None):
- for t in target:
- add_libdeps_node(env, t, libdeps)
-
- # We ignored the visibility_map until now because we needed to use
- # original dependency value for linting. Now go back through and
- # use the map to convert to the desired dependencies, for example
- # all Public in the static linking case.
- for libdep in libdeps:
- libdep.dependency_type = visibility_map[libdep.dependency_type]
-
- for t in target:
- # target[0] must be a Node and not a string, or else libdeps will fail to
- # work properly.
- _append_direct_libdeps(t, libdeps)
-
- for dependent in env.get(Constants.LibdepsDependents, []):
- if dependent is None:
- continue
-
- visibility = deptype.Private
- if isinstance(dependent, tuple):
- visibility = dependent[1]
- dependent = dependent[0]
-
- dependentNode = _get_node_with_ixes(
- env, dependent, builder
- )
- _append_direct_libdeps(
- dependentNode, [dependency(target[0], visibility_map[visibility], dependent)]
- )
-
- if not ignore_progdeps:
- for dependent in env.get(Constants.ProgdepsDependents, []):
- if dependent is None:
- continue
-
- visibility = deptype.Public
- if isinstance(dependent, tuple):
- # TODO: Error here? Non-public PROGDEPS_DEPENDENTS probably are meaningless
- visibility = dependent[1]
- dependent = dependent[0]
-
- dependentNode = _get_node_with_ixes(
- env, dependent, "Program"
- )
- _append_direct_libdeps(
- dependentNode, [dependency(target[0], visibility_map[visibility], dependent)]
- )
-
- return target, source
-
-
-def expand_libdeps_tags(source, target, env, for_signature):
- results = []
- for expansion in env.get(Constants.LibdepsTagExpansion, []):
- results.append(expansion(source, target, env, for_signature))
- return results
-
-
-def expand_libdeps_with_flags(source, target, env, for_signature):
-
- libdeps_with_flags = []
-
- # Used to make modifications to the previous libdep on the link line
- # if needed. An empty class here will make the switch_flag conditionals
- # below a bit cleaner.
- prev_libdep = None
-
- for flagged_libdep in _get_flagged_libdeps(source, target, env, for_signature):
-
- # If there are no flags to process we can move on to the next lib.
- # start_index wont mater in the case because if there are no flags
- # on the previous lib, then we will never need to do the chopping
- # mechanism on the next iteration.
- if not flagged_libdep.prefix_flags and not flagged_libdep.postfix_flags:
- libdeps_with_flags.append(str(flagged_libdep))
- prev_libdep = flagged_libdep
- continue
-
- # This for loop will go through the previous results and remove the 'off'
- # flag as well as removing the new 'on' flag. For example, let libA and libB
- # both use on and off flags which would normally generate on the link line as:
- # -Wl--on-flag libA.a -Wl--off-flag -Wl--on-flag libA.a -Wl--off-flag
- # This loop below will spot the cases were the flag was turned off and then
- # immediately turned back on
- for switch_flag in getattr(flagged_libdep.libnode.attributes, 'libdeps_switch_flags', []):
- if (prev_libdep and switch_flag['on'] in flagged_libdep.prefix_flags
- and switch_flag['off'] in prev_libdep.postfix_flags):
-
- flagged_libdep.prefix_flags.remove(switch_flag['on'])
- prev_libdep.postfix_flags.remove(switch_flag['off'])
-
- # prev_lib has had its list modified, and it has a start index
- # from the last iteration, so it will chop of the end the current
- # list and reapply the end with the new flags.
- prev_libdep.add_lib_to_result_list(libdeps_with_flags)
-
- # Store the information of the len of the current list before adding
- # the next set of flags as that will be the start index for the previous
- # lib next time around in case there are any switch flags to chop off.
- start_index = len(libdeps_with_flags)
- flagged_libdep.add_lib_to_result_list(libdeps_with_flags)
-
- # Done processing the current lib, so set it to previous for the next iteration.
- prev_libdep = flagged_libdep
- prev_libdep.start_index = start_index
-
- return libdeps_with_flags
-
-def generate_libdeps_graph(env):
- if env.get('SYMBOLDEPSSUFFIX', None):
-
- find_symbols = env.Dir("$BUILD_DIR").path + "/libdeps/find_symbols"
- libdeps_graph = env.GetLibdepsGraph()
-
- symbol_deps = []
- for symbols_file, target_node in env.get('LIBDEPS_SYMBOL_DEP_FILES', []):
-
- direct_libdeps = []
- for direct_libdep in _get_sorted_direct_libdeps(target_node):
- add_node_from(env, direct_libdep.target_node)
- add_edge_from(
- env,
- str(target_node.abspath),
- str(direct_libdep.target_node.abspath),
- visibility=int(direct_libdep.dependency_type),
- direct=True)
- direct_libdeps.append(direct_libdep.target_node.abspath)
-
- for libdep in _get_libdeps(target_node):
- if libdep.abspath not in direct_libdeps:
- add_node_from(env, libdep)
- add_edge_from(
- env,
- str(target_node.abspath),
- str(libdep.abspath),
- visibility=int(deptype.Public),
- direct=False)
- if env['PLATFORM'] == 'darwin':
- sep = ' '
- else:
- sep = ':'
- ld_path = sep.join([os.path.dirname(str(libdep)) for libdep in _get_libdeps(target_node)])
- symbol_deps.append(env.Command(
- target=symbols_file,
- source=target_node,
- action=SCons.Action.Action(
- f'{find_symbols} $SOURCE "{ld_path}" $TARGET',
- "Generating $SOURCE symbol dependencies" if not env['VERBOSE'] else "")))
-
- def write_graph_hash(env, target, source):
-
- with open(target[0].path, 'w') as f:
- json_str = json.dumps(networkx.readwrite.json_graph.node_link_data(env.GetLibdepsGraph()), sort_keys=True).encode('utf-8')
- f.write(hashlib.sha256(json_str).hexdigest())
-
- graph_hash = env.Command(target="$BUILD_DIR/libdeps/graph_hash.sha256",
- source=symbol_deps,
- action=SCons.Action.FunctionAction(
- write_graph_hash,
- {"cmdstr": None}))
- env.Depends(graph_hash, [
- env.File("#SConstruct")] +
- glob.glob("**/SConscript", recursive=True) +
- [os.path.abspath(__file__),
- env.File('$BUILD_DIR/mongo/util/version_constants.h')])
-
- graph_node = env.Command(
- target=env.get('LIBDEPS_GRAPH_FILE', None),
- source=symbol_deps,
- action=SCons.Action.FunctionAction(
- generate_graph,
- {"cmdstr": "Generating libdeps graph"}))
-
- env.Depends(graph_node, [graph_hash] + env.Glob("#buildscripts/libdeps/libdeps/*"))
-
-def generate_graph(env, target, source):
-
- libdeps_graph = env.GetLibdepsGraph()
-
- for symbol_deps_file in source:
- with open(str(symbol_deps_file)) as f:
- symbols = {}
- try:
- for symbol, lib in json.load(f).items():
- # ignore symbols from external libraries,
- # they will just clutter the graph
- if lib.startswith(env.Dir("$BUILD_DIR").path):
- if lib not in symbols:
- symbols[lib] = []
- symbols[lib].append(symbol)
- except json.JSONDecodeError:
- env.FatalError(f"Failed processing json file: {str(symbol_deps_file)}")
-
- for libdep in symbols:
- from_node = os.path.abspath(str(symbol_deps_file)[:-len(env['SYMBOLDEPSSUFFIX'])])
- to_node = os.path.abspath(libdep).strip()
- libdeps_graph.add_edges_from([(
- from_node,
- to_node,
- {EdgeProps.symbols.name: " ".join(symbols[libdep]) })])
- node = env.File(str(symbol_deps_file)[:-len(env['SYMBOLDEPSSUFFIX'])])
- add_node_from(env, node)
-
- libdeps_graph_file = f"{env.Dir('$BUILD_DIR').path}/libdeps/libdeps.graphml"
- networkx.write_graphml(libdeps_graph, libdeps_graph_file, named_key_ids=True)
- with fileinput.FileInput(libdeps_graph_file, inplace=True) as file:
- for line in file:
- print(line.replace(str(env.Dir("$BUILD_DIR").abspath + os.sep), ''), end='')
-
-
-def setup_environment(env, emitting_shared=False, debug='off', linting='on'):
- """Set up the given build environment to do LIBDEPS tracking."""
-
- LibdepLinter.skip_linting = linting == 'off'
- LibdepLinter.print_linter_errors = linting == 'print'
-
- try:
- env["_LIBDEPS"]
- except KeyError:
- env["_LIBDEPS"] = "$_LIBDEPS_LIBS"
-
- env["_LIBDEPS_TAGS"] = expand_libdeps_tags
- env["_LIBDEPS_GET_LIBS"] = partial(get_libdeps, debug=debug)
- env["_LIBDEPS_OBJS"] = partial(get_libdeps_objs, debug=debug)
- env["_SYSLIBDEPS"] = partial(get_syslibdeps, debug=debug, shared=emitting_shared)
-
- env[Constants.Libdeps] = SCons.Util.CLVar()
- env[Constants.SysLibdeps] = SCons.Util.CLVar()
-
- # Create the alias for graph generation, the existence of this alias
- # on the command line will cause the libdeps-graph generation to be
- # configured.
- env['LIBDEPS_GRAPH_ALIAS'] = env.Alias(
- 'generate-libdeps-graph',
- "${BUILD_DIR}/libdeps/libdeps.graphml")[0]
-
- if str(env['LIBDEPS_GRAPH_ALIAS']) in COMMAND_LINE_TARGETS:
-
- # Detect if the current system has the tools to perform the generation.
- if env.GetOption('ninja') != 'disabled':
- env.FatalError("Libdeps graph generation is not supported with ninja builds.")
- if not emitting_shared:
- env.FatalError("Libdeps graph generation currently only supports dynamic builds.")
-
- if env['PLATFORM'] == 'darwin':
- required_bins = ['awk', 'sed', 'otool', 'nm']
- else:
- required_bins = ['awk', 'grep', 'ldd', 'nm']
- for bin in required_bins:
- if not env.WhereIs(bin):
- env.FatalError(f"'{bin}' not found, Libdeps graph generation requires {bin}.")
-
-
- # The find_symbols binary is a small fast C binary which will extract the missing
- # symbols from the target library, and discover what linked libraries supply it. This
- # setups the binary to be built.
- find_symbols_env = env.Clone()
- find_symbols_env.VariantDir('${BUILD_DIR}/libdeps', 'buildscripts/libdeps', duplicate = 0)
- find_symbols_node = find_symbols_env.Program(
- target='${BUILD_DIR}/libdeps/find_symbols',
- source=['${BUILD_DIR}/libdeps/find_symbols.c'],
- CFLAGS=['-O3'])
-
- # Here we are setting up some functions which will return single instance of the
- # network graph and symbol deps list. We also setup some environment variables
- # which are used along side the functions.
- symbol_deps = []
- def append_symbol_deps(env, symbol_deps_file):
- env.Depends(env['LIBDEPS_GRAPH_FILE'], symbol_deps_file[0])
- symbol_deps.append(symbol_deps_file)
- env.AddMethod(append_symbol_deps, "AppendSymbolDeps")
-
- env['LIBDEPS_SYMBOL_DEP_FILES'] = symbol_deps
- env['LIBDEPS_GRAPH_FILE'] = env.File("${BUILD_DIR}/libdeps/libdeps.graphml")
- env['LIBDEPS_GRAPH_SCHEMA_VERSION'] = 3
- env["SYMBOLDEPSSUFFIX"] = '.symbol_deps'
-
- libdeps_graph = LibdepsGraph()
- libdeps_graph.graph['invocation'] = " ".join([env['ESCAPE'](str(sys.executable))] + [env['ESCAPE'](arg) for arg in sys.argv])
- libdeps_graph.graph['git_hash'] = env['MONGO_GIT_HASH']
- libdeps_graph.graph['graph_schema_version'] = env['LIBDEPS_GRAPH_SCHEMA_VERSION']
- libdeps_graph.graph['build_dir'] = env.Dir('$BUILD_DIR').path
- libdeps_graph.graph['deptypes'] = json.dumps({key: value[0] for key, value in deptype.__members__.items() if isinstance(value, tuple)})
-
- def get_libdeps_graph(env):
- return libdeps_graph
- env.AddMethod(get_libdeps_graph, "GetLibdepsGraph")
-
- # Now we will setup an emitter, and an additional action for several
- # of the builder involved with dynamic builds.
- def libdeps_graph_emitter(target, source, env):
- if "conftest" not in str(target[0]):
- symbol_deps_file = env.File(str(target[0]) + env['SYMBOLDEPSSUFFIX'])
- env.Depends(symbol_deps_file, '${BUILD_DIR}/libdeps/find_symbols')
- env.AppendSymbolDeps((symbol_deps_file,target[0]))
-
- return target, source
-
- for builder_name in ("Program", "SharedLibrary", "LoadableModule"):
- builder = env['BUILDERS'][builder_name]
- base_emitter = builder.emitter
- new_emitter = SCons.Builder.ListEmitter([base_emitter, libdeps_graph_emitter])
- builder.emitter = new_emitter
-
-
- env.Append(
- LIBDEPS_LIBEMITTER=partial(
- libdeps_emitter,
- debug=debug,
- builder="StaticLibrary"),
- LIBEMITTER=lambda target, source, env: env["LIBDEPS_LIBEMITTER"](target, source, env),
- LIBDEPS_SHAREMITTER=partial(
- libdeps_emitter,
- debug=debug,
- builder="SharedArchive", ignore_progdeps=True),
- SHAREMITTER=lambda target, source, env: env["LIBDEPS_SHAREMITTER"](target, source, env),
- LIBDEPS_SHLIBEMITTER=partial(
- libdeps_emitter,
- debug=debug,
- builder="SharedLibrary",
- visibility_map=dependency_visibility_honored
- ),
- SHLIBEMITTER=lambda target, source, env: env["LIBDEPS_SHLIBEMITTER"](target, source, env),
- LIBDEPS_PROGEMITTER=partial(
- libdeps_emitter,
- debug=debug,
- builder="SharedLibrary" if emitting_shared else "StaticLibrary"
- ),
- PROGEMITTER=lambda target, source, env: env["LIBDEPS_PROGEMITTER"](target, source, env),
- )
-
- env["_LIBDEPS_LIBS_WITH_TAGS"] = expand_libdeps_with_flags
-
- env["_LIBDEPS_LIBS"] = (
- "$LINK_LIBGROUP_START "
- "$_LIBDEPS_LIBS_WITH_TAGS "
- "$LINK_LIBGROUP_END "
- )
-
- env.Prepend(_LIBFLAGS="$_LIBDEPS_TAGS $_LIBDEPS $_SYSLIBDEPS ")
- for builder_name in ("Program", "SharedLibrary", "LoadableModule", "SharedArchive"):
- try:
- update_scanner(env, builder_name, debug=debug)
- except KeyError:
- pass
-
-
-def setup_conftests(conf):
- def FindSysLibDep(context, name, libs, **kwargs):
- var = "LIBDEPS_" + name.upper() + "_SYSLIBDEP"
- kwargs["autoadd"] = False
- for lib in libs:
- result = context.sconf.CheckLib(lib, **kwargs)
- context.did_show_result = 1
- if result:
- context.env[var] = lib
- return context.Result(result)
- context.env[var] = _missing_syslib(name)
- return context.Result(result)
-
- conf.AddTest("FindSysLibDep", FindSysLibDep)