diff options
-rw-r--r-- | SConstruct | 77 | ||||
-rw-r--r-- | site_scons/libdeps.py | 57 |
2 files changed, 123 insertions, 11 deletions
diff --git a/SConstruct b/SConstruct index 0a3273a7158..e3700eb5398 100644 --- a/SConstruct +++ b/SConstruct @@ -455,6 +455,14 @@ add_option('variables-files', help="Specify variables files to load", ) +link_model_choices = ['auto', 'object', 'static', 'dynamic', 'dynamic-strict'] +add_option('link-model', + choices=link_model_choices, + default='object', + help='Select the linking model for the project', + type='choice' +) + variable_parse_mode_choices=['auto', 'posix', 'other'] add_option('variable-parse-mode', choices=variable_parse_mode_choices, @@ -992,6 +1000,59 @@ if has_option("cache"): env.FatalError("Mixing --cache and --gcov doesn't work correctly yet. See SERVER-11084") env.CacheDir(str(env.Dir(cacheDir))) +# Normalize the link model. If it is auto, then a release build uses 'object' mode. Otherwise +# we automatically select the 'static' model on non-windows platforms, or 'object' if on +# Windows. If the user specified, honor the request, unless it conflicts with the requirement +# that release builds use the 'object' mode, in which case, error out. +# +# We require the use of the 'object' mode for release builds because it is the only linking +# model that works across all of our platforms. We would like to ensure that all of our +# released artifacts are built with the same known-good-everywhere model. +link_model = get_option('link-model') + +if link_model == "auto": + link_model = "object" if (env.TargetOSIs('windows') or has_option("release")) else "static" +elif has_option("release") and link_model != "object": + print("The link model for release builds is required to be 'object'") + Exit(1) + +# The only link model currently supported on Windows is 'object', since there is no equivalent +# to --whole-archive. +if env.TargetOSIs('windows') and link_model != 'object': + print("Windows builds must use the 'object' link model"); + Exit(1); + +# The 'object' mode for libdeps is enabled by setting _LIBDEPS to $_LIBDEPS_OBJS. The other two +# modes operate in library mode, enabled by setting _LIBDEPS to $_LIBDEPS_LIBS. +env['_LIBDEPS'] = '$_LIBDEPS_OBJS' if link_model == "object" else '$_LIBDEPS_LIBS' + +if link_model.startswith("dynamic"): + + # Redirect the 'Library' target, which we always use instead of 'StaticLibrary' for things + # that can be built in either mode, to point to SharedLibrary. + env['BUILDERS']['Library'] = env['BUILDERS']['SharedLibrary'] + + # TODO: Ideally, the conditions below should be based on a detection of what linker we are + # using, not the local OS, but I doubt very much that we will see the mach-o linker on + # anything other than Darwin, or a BFD/sun-esque linker elsewhere. + + # On Darwin, we need to tell the linker that undefined symbols are resolved via dynamic + # lookup; otherwise we get build failures. On other unixes, we need to suppress as-needed + # behavior so that initializers are ensured present, even if there is no visible edge to + # the library in the symbol graph. + # + # NOTE: The darwin linker flag is only needed because the library graph is not a DAG. Once + # the graph is a DAG, we will require all edges to be expressed, and we should drop the + # flag. When that happens, we should also add -z,defs flag on ELF platforms to ensure that + # missing symbols due to unnamed dependency edges result in link errors. + if env.TargetOSIs('osx'): + if link_model != "dynamic-strict": + env.AppendUnique(SHLINKFLAGS=["-Wl,-undefined,dynamic_lookup"]) + else: + env.AppendUnique(SHLINKFLAGS=["-Wl,--no-as-needed"]) + if link_model == "dynamic-strict": + env.AppendUnique(SHLINKFLAGS=["-Wl,-z,defs"]) + if optBuild: env.SetConfigHeaderDefine("MONGO_CONFIG_OPTIMIZED_BUILD") @@ -1022,8 +1083,6 @@ if endian == "little": elif endian == "big": env.SetConfigHeaderDefine("MONGO_CONFIG_BYTE_ORDER", "4321") -env['_LIBDEPS'] = '$_LIBDEPS_OBJS' - if env['_LIBDEPS'] == '$_LIBDEPS_OBJS': # The libraries we build in LIBDEPS_OBJS mode are just placeholders for tracking dependencies. # This avoids wasting time and disk IO on them. @@ -1040,17 +1099,23 @@ if env['_LIBDEPS'] == '$_LIBDEPS_OBJS': env['RANLIBCOM'] = noop_action env['RANLIBCOMSTR'] = 'Skipping ranlib for $TARGET' -libdeps.setup_environment( env ) +libdeps.setup_environment(env, emitting_shared=(link_model.startswith("dynamic"))) -if env.TargetOSIs('linux', 'freebsd'): +if env.TargetOSIs('linux', 'freebsd', 'openbsd'): env['LINK_LIBGROUP_START'] = '-Wl,--start-group' env['LINK_LIBGROUP_END'] = '-Wl,--end-group' + env['LINK_WHOLE_ARCHIVE_START'] = '-Wl,--whole-archive' + env['LINK_WHOLE_ARCHIVE_END'] = '-Wl,--no-whole-archive' elif env.TargetOSIs('osx'): env['LINK_LIBGROUP_START'] = '' env['LINK_LIBGROUP_END'] = '' + env['LINK_WHOLE_ARCHIVE_START'] = '-Wl,-all_load' + env['LINK_WHOLE_ARCHIVE_END'] = '-Wl,-noall_load' elif env.TargetOSIs('solaris'): - env['LINK_LIBGROUP_START'] = '-z rescan' - env['LINK_LIBGROUP_END'] = '' + env['LINK_LIBGROUP_START'] = '-z rescan-start' + env['LINK_LIBGROUP_END'] = '-z rescan-end' + env['LINK_WHOLE_ARCHIVE_START'] = '-z allextract' + env['LINK_WHOLE_ARCHIVE_END'] = '-z defaultextract' # ---- other build setup ----- if debugBuild: diff --git a/site_scons/libdeps.py b/site_scons/libdeps.py index 971bd0d004b..4c8dbf1b24f 100644 --- a/site_scons/libdeps.py +++ b/site_scons/libdeps.py @@ -247,7 +247,48 @@ def libdeps_emitter(target, source, env): return target, source -def setup_environment(env): +def shlibdeps_emitter(target, source, env): + """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. + """ + + libdep_files = [] + lib_suffix = env.subst('$SHLIBSUFFIX', target=target, source=source) + lib_prefix = env.subst('$SHLIBPREFIX', target=target, source=source) + for prereq in env.Flatten([env.get(libdeps_env_var, [])]): + full_path = env.subst(str(prereq), target=target, source=source) + dir_name = os.path.dirname(full_path) + file_name = os.path.basename(full_path) + if not file_name.startswith(lib_prefix): + file_name = '${SHLIBPREFIX}' + file_name + if not file_name.endswith(lib_suffix): + file_name += '${SHLIBSUFFIX}' + libdep_files.append(env.File(os.path.join(dir_name, file_name))) + + 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, libdep_files) + + for dependent in env.Flatten([env.get('LIBDEPS_DEPENDENTS', [])]): + __append_direct_libdeps(env.File(dependent), [target[0]]) + + return target, source + +def setup_environment(env, emitting_shared=False): """Set up the given build environment to do LIBDEPS tracking.""" try: @@ -269,10 +310,16 @@ def setup_environment(env): env[libdeps_env_var] = SCons.Util.CLVar() env[syslibdeps_env_var] = SCons.Util.CLVar() - env.Append(LIBEMITTER=libdeps_emitter, - PROGEMITTER=libdeps_emitter, - SHLIBEMITTER=libdeps_emitter) - env.Prepend(_LIBFLAGS=' $LINK_LIBGROUP_START $_LIBDEPS $LINK_LIBGROUP_END $_SYSLIBDEPS ') + env.Append(LIBEMITTER=libdeps_emitter) + if emitting_shared: + env.Append( + PROGEMITTER=shlibdeps_emitter, + SHLIBEMITTER=shlibdeps_emitter) + else: + env.Append( + PROGEMITTER=libdeps_emitter, + SHLIBEMITTER=libdeps_emitter) + env.Prepend(_LIBFLAGS=' $LINK_WHOLE_ARCHIVE_START $LINK_LIBGROUP_START $_LIBDEPS $LINK_LIBGROUP_END $LINK_WHOLE_ARCHIVE_END $_SYSLIBDEPS ') for builder_name in ('Program', 'SharedLibrary', 'LoadableModule'): try: update_scanner(env['BUILDERS'][builder_name]) |