summaryrefslogtreecommitdiff
path: root/site_scons/libdeps.py
diff options
context:
space:
mode:
authorDaniel Moody <daniel.moody@mongodb.com>2020-08-04 07:07:47 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-08-27 01:08:39 +0000
commit31361990d9bd79ba3a3c578546ac62b57af11847 (patch)
tree18946194c8f4caf4336119831bed6e23c191e8d3 /site_scons/libdeps.py
parent6663d804ebf22ec5c4295d082ad3685bf801da6b (diff)
downloadmongo-31361990d9bd79ba3a3c578546ac62b57af11847.tar.gz
SERVER-49119 Added way for libdeps to generically handle prefix/postfix flags, moved --whole-archive out to SConstruct and add --as-needed for dynamic
Diffstat (limited to 'site_scons/libdeps.py')
-rw-r--r--site_scons/libdeps.py186
1 files changed, 153 insertions, 33 deletions
diff --git a/site_scons/libdeps.py b/site_scons/libdeps.py
index 1158bcda557..1fce726a828 100644
--- a/site_scons/libdeps.py
+++ b/site_scons/libdeps.py
@@ -51,6 +51,7 @@ automatically added when missing.
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+import copy
import os
import textwrap
@@ -72,7 +73,7 @@ class Constants:
LibdepsTags = "LIBDEPS_TAGS"
LibdepsTagExpansion = "LIBDEPS_TAG_EXPANSIONS"
-class dependency(object):
+class dependency:
Public, Private, Interface = list(range(3))
def __init__(self, value, deptype):
@@ -82,7 +83,95 @@ class dependency(object):
def __str__(self):
return str(self.target_node)
-class LibdepLinter(object):
+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(libnode.get_env().get('LIBDEPS_PREFIX_FLAGS', []))
+ self.postfix_flags = copy.copy(libnode.get_env().get('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
@@ -459,7 +548,7 @@ def __get_libdeps(node):
def __get_syslibdeps(node):
""" Given a SCons Node, return its system library dependencies.
- These are the depencencies listed with SYSLIBDEPS, and are linked using -l.
+ These are the dependencies listed with SYSLIBDEPS, and are linked using -l.
"""
result = getattr(node.attributes, Constants.SysLibdepsCached, None)
if result is not None:
@@ -558,6 +647,19 @@ def __append_direct_libdeps(node, prereq_nodes):
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
@@ -688,35 +790,55 @@ def expand_libdeps_tags(source, target, env, for_signature):
return results
-def expand_libdeps_with_extraction_flags(source, target, env, for_signature):
- result = []
- libs = get_libdeps(source, target, env, for_signature)
- whole_archive_start = env.subst("$LINK_WHOLE_ARCHIVE_LIB_START")
- whole_archive_end = env.subst("$LINK_WHOLE_ARCHIVE_LIB_END")
- whole_archive_separator = env.get("LINK_WHOLE_ARCHIVE_SEP", " ")
-
- for lib in libs:
- if isinstance(lib, (str, SCons.Node.FS.File, SCons.Node.FS.Entry)):
- lib_target = str(lib)
- lib_tags = lib.get_env().get(Constants.LibdepsTags, [])
- else:
- lib_target = env.subst("$TARGET", target=lib)
- lib_tags = env.File(lib).get_env().get(Constants.LibdepsTags, [])
+def expand_libdeps_with_flags(source, target, env, for_signature):
- if "init-no-global-side-effects" in lib_tags:
- result.append(lib_target)
- else:
- whole_archive_flag = "{}{}{}".format(
- whole_archive_start, whole_archive_separator, lib_target
- )
- if whole_archive_end:
- whole_archive_flag += "{}{}".format(
- whole_archive_separator, whole_archive_end
- )
+ libdeps_with_flags = []
- result.extend(whole_archive_flag.split())
+ # 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
- return result
+ 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 env.get('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 setup_environment(env, emitting_shared=False, linting='on'):
@@ -741,7 +863,7 @@ def setup_environment(env, emitting_shared=False, linting='on'):
# We need a way for environments to alter just which libdeps
# emitter they want, without altering the overall program or
# library emitter which may have important effects. The
- # subsitution rules for emitters are a little strange, so build
+ # substitution rules for emitters are a little strange, so build
# ourselves a little trampoline to use below so we don't have to
# deal with it.
def make_indirect_emitter(variable):
@@ -765,14 +887,12 @@ def setup_environment(env, emitting_shared=False, linting='on'):
PROGEMITTER=make_indirect_emitter("LIBDEPS_PROGEMITTER"),
)
- env["_LIBDEPS_LIBS_WITH_TAGS"] = expand_libdeps_with_extraction_flags
+ env["_LIBDEPS_LIBS_WITH_TAGS"] = expand_libdeps_with_flags
env["_LIBDEPS_LIBS"] = (
- "$LINK_WHOLE_ARCHIVE_START "
"$LINK_LIBGROUP_START "
"$_LIBDEPS_LIBS_WITH_TAGS "
"$LINK_LIBGROUP_END "
- "$LINK_WHOLE_ARCHIVE_END"
)
env.Prepend(_LIBFLAGS="$_LIBDEPS_TAGS $_LIBDEPS $_SYSLIBDEPS ")