# -*- mode: python; -*- # This SConscript describes build rules for the "mongo" project. import itertools import os import re import sys from buildscripts import utils Import("env") Import("has_option") Import("get_option") Import("usemozjs") Import("use_system_version_of_library") # Boost we need everywhere. 's2' is spammed in all over the place by # db/geo unfortunately. pcre is also used many places. env.InjectThirdPartyIncludePaths(libraries=['boost', 's2', 'pcre']) env.InjectMongoIncludePaths() env.SConscript( dirs=[ 'base', 'bson', 'client', 'crypto', 'db', 'dbtests', 'executor', 'installer', 'logger', 'platform', 'rpc', 's', 'scripting', 'shell', 'tools', 'unittest', 'util', ], ) # NOTE: This library does not really belong here. Its presence here is # temporary. Do not add to this library, do not remove from it, and do # not declare other libraries in this file. baseSource=[ 'base/data_range.cpp', 'base/data_range_cursor.cpp', 'base/data_type.cpp', 'base/data_type_string_data.cpp', 'base/data_type_terminated.cpp', 'base/error_codes.cpp', 'base/global_initializer.cpp', 'base/global_initializer_registerer.cpp', 'base/init.cpp', 'base/initializer.cpp', 'base/initializer_context.cpp', 'base/initializer_dependency_graph.cpp', 'base/make_string_vector.cpp', 'base/parse_number.cpp', 'base/status.cpp', 'base/string_data.cpp', 'base/validate_locale.cpp', 'bson/bson_validate.cpp', 'bson/bsonelement.cpp', 'bson/bsonmisc.cpp', 'bson/bsonobj.cpp', 'bson/bsonobjbuilder.cpp', 'bson/bsontypes.cpp', 'bson/json.cpp', 'bson/oid.cpp', 'bson/timestamp.cpp', 'logger/component_message_log_domain.cpp', 'logger/console.cpp', 'logger/log_component.cpp', 'logger/log_component_settings.cpp', 'logger/log_manager.cpp', 'logger/logger.cpp', 'logger/log_severity.cpp', 'logger/logstream_builder.cpp', 'logger/message_event_utf8_encoder.cpp', 'logger/message_log_domain.cpp', 'logger/ramlog.cpp', 'logger/rotatable_file_manager.cpp', 'logger/rotatable_file_writer.cpp', 'platform/random.cpp', 'platform/strnlen.cpp', 'util/allocator.cpp', 'util/assert_util.cpp', 'util/base64.cpp', 'util/concurrency/thread_name.cpp', 'util/exception_filter_win32.cpp', 'util/hex.cpp', 'util/log.cpp', 'util/signal_handlers_synchronous.cpp', 'util/stacktrace_${TARGET_OS_FAMILY}.cpp', 'util/stacktrace.cpp', 'util/static_observer.cpp', 'util/stringutils.cpp', 'util/system_clock_source.cpp', 'util/system_tick_source.cpp', 'util/text.cpp', 'util/time_support.cpp', 'util/version.cpp', ] baseLibDeps=[ # NOTE: This library *must not* depend on any libraries than # the ones declared here. Do not add to this list. '$BUILD_DIR/third_party/murmurhash3/murmurhash3', '$BUILD_DIR/third_party/shim_boost', '$BUILD_DIR/third_party/shim_pcrecpp', 'util/debugger', 'util/quick_exit', ] if GetOption('experimental-decimal-support') == 'on': baseSource.append('platform/decimal128.cpp') baseLibDeps.append('$BUILD_DIR/third_party/shim_intel_decimal128') else: baseSource.append('platform/decimal128_dummy.cpp') env.Library( target='base', source=baseSource, LIBDEPS=baseLibDeps, ) js_engine_ver = get_option("js-engine") if get_option("server-js") == "on" else "none" # On windows, we need to escape the backslashes in the command-line # so that windows paths look okay. cmd_line = " ".join(sys.argv).encode('string-escape') if env.TargetOSIs('windows'): cmd_line = cmd_line.replace('\\', r'\\') module_list = '{ %s }' % ', '.join([ '"{0}"'.format(x) for x in env['MONGO_MODULES'] ]) # This generates a numeric representation of the version string so that # you can easily compare versions of MongoDB without having to parse # the version string. # # The rules for this are # {major}{minor}{release}{pre/rc/final} # If the version is pre-release and not an rc, the final number is 0 # If the version is an RC, the final number of 1 + rc number # If the version is pre-release between RC's, the final number is 1 + rc number # If the version is a final release, the final number is 99 # # Examples: # 3.1.1-123 = 3010100 # 3.1.1-rc2 = 3010103 # 3.1.1-rc2-123 = 3010103 # 3.1.1 = 3010199 # version_parts = [ x for x in re.match(r'^(\d+)\.(\d+)\.(\d+)-?((?:(rc)(\d+))?.*)?', env['MONGO_VERSION']).groups() ] version_extra = version_parts[3] if version_parts[3] else "" if version_parts[4] == 'rc': version_parts[3] = int(version_parts[5]) + -50 elif version_parts[3]: version_parts[3] = -100 else: version_parts[3] = 0 version_parts = [ int(x) for x in version_parts[:4]] # This turns the MONGO_BUILDINFO_ENVIRONMENT_DATA tuples into a std::vector of # std::tuple. buildInfoInitializer = [] for tup in env['MONGO_BUILDINFO_ENVIRONMENT_DATA']: def pyToCXXBool(val): return "true" if val else "false" def wrapInQuotes(val): return '"{0}"'.format(val) buildInfoInitializer.append( 'std::make_tuple({0})'.format(', '.join( ( wrapInQuotes(tup[0]), wrapInQuotes(env.subst(tup[1])), pyToCXXBool(tup[2]), pyToCXXBool(tup[3]), ) )) ) buildInfoInitializer = '{{ {0} }}'.format(', '.join(buildInfoInitializer)) generatedVersionFile = env.Substfile( 'util/version.cpp.in', SUBST_DICT=[ ('@mongo_version@', env['MONGO_VERSION']), ('@mongo_version_major@', version_parts[0]), ('@mongo_version_minor@', version_parts[1]), ('@mongo_version_patch@', version_parts[2]), ('@mongo_version_extra@', version_parts[3]), ('@mongo_version_extra_str@', version_extra), ('@mongo_git_hash@', env['MONGO_GIT_HASH']), ('@buildinfo_js_engine@', js_engine_ver), ('@buildinfo_allocator@', GetOption('allocator')), ('@buildinfo_modules@', module_list), ('@buildinfo_environment_data@', buildInfoInitializer), ]) env.Alias('generated-sources', generatedVersionFile) config_header_substs = ( ('@mongo_config_byte_order@', 'MONGO_CONFIG_BYTE_ORDER'), ('@mongo_config_debug_build@', 'MONGO_CONFIG_DEBUG_BUILD'), ('@mongo_config_experimental_decimal_support@', 'MONGO_CONFIG_EXPERIMENTAL_DECIMAL_SUPPORT'), ('@mongo_config_have_thread_local@', 'MONGO_CONFIG_HAVE_THREAD_LOCAL'), ('@mongo_config_have___declspec_thread@', 'MONGO_CONFIG_HAVE___DECLSPEC_THREAD'), ('@mongo_config_have___thread@', 'MONGO_CONFIG_HAVE___THREAD'), ('@mongo_config_have_execinfo_backtrace@', 'MONGO_CONFIG_HAVE_EXECINFO_BACKTRACE'), ('@mongo_config_have_fips_mode_set@', 'MONGO_CONFIG_HAVE_FIPS_MODE_SET'), ('@mongo_config_have_header_unistd_h@', 'MONGO_CONFIG_HAVE_HEADER_UNISTD_H'), ('@mongo_config_have_memset_s@', 'MONGO_CONFIG_HAVE_MEMSET_S'), ('@mongo_config_have_posix_monotonic_clock@', 'MONGO_CONFIG_HAVE_POSIX_MONOTONIC_CLOCK'), ('@mongo_config_have_std_is_trivially_copyable@', 'MONGO_CONFIG_HAVE_STD_IS_TRIVIALLY_COPYABLE'), ('@mongo_config_have_std_make_unique@', 'MONGO_CONFIG_HAVE_STD_MAKE_UNIQUE'), ('@mongo_config_have_strnlen@', 'MONGO_CONFIG_HAVE_STRNLEN'), ('@mongo_config_optimized_build@', 'MONGO_CONFIG_OPTIMIZED_BUILD'), ('@mongo_config_ssl@', 'MONGO_CONFIG_SSL'), ('@mongo_config_wiredtiger_enabled@', 'MONGO_CONFIG_WIREDTIGER_ENABLED'), ) def makeConfigHeaderDefine(self, key): val = "// #undef {0}".format(key) if key in self['CONFIG_HEADER_DEFINES']: val = "#define {0} {1}".format(key, self['CONFIG_HEADER_DEFINES'][key]) return val env.AddMethod(makeConfigHeaderDefine) generateConfigHeaderFile = env.Substfile( 'config.h.in', SUBST_DICT=[(k, env.makeConfigHeaderDefine(v)) for (k, v) in config_header_substs] ) env.Alias('generated-sources', generateConfigHeaderFile) mongodLibDeps = [ "db/coredb", "db/conn_pool_options", "db/mongod_options", "db/mongodandmongos", "db/mongodwebserver", "db/serveronly", "db/repl/storage_interface_impl", "executor/network_interface_factory", 's/commands/shared_cluster_commands', "util/ntservice", ] if has_option('use-cpu-profiler'): mongodLibDeps.append('db/cpuprofiler') mongod = env.Program( target="mongod", source=[ "db/db.cpp", "db/mongod_options_init.cpp", ], LIBDEPS=mongodLibDeps, ) env.Default(env.Install('#/', mongod)) # tools rewrittenTools = [ "mongodump", "mongorestore", "mongoexport", "mongoimport", "mongostat", "mongotop", "bsondump", "mongofiles", "mongooplog" ] # mongobridge and mongoperf env.Install( '#/', [ env.Program("mongoperf", [ "client/examples/mongoperf.cpp", ], LIBDEPS=[ "db/coredb", "db/serveronly", "util/ntservice_mock", ]), ]) # mongos env.Install( '#/', env.Program( "mongos", [ "s/server.cpp", "s/mongos_options.cpp", "s/mongos_options_init.cpp", ], LIBDEPS=[ 'db/conn_pool_options', 'db/coredb', 'db/mongodandmongos', 's/client/sharding_connection_hook', 's/commands/cluster_commands', 's/commands/shared_cluster_commands', 's/coreshard', 's/mongoscore', 's/sharding_initialization', 'util/ntservice', 'util/options_parser/options_parser_init', ])) env.Library("linenoise_utf8", source=[ "shell/linenoise_utf8.cpp", ]) # --- sniffer --- mongosniff_built = False if env.TargetOSIs('osx') or env["_HAVEPCAP"]: mongosniff_built = True sniffEnv = env.Clone() sniffEnv.Append( CPPDEFINES="MONGO_EXPOSE_MACROS" ) if not env.TargetOSIs('windows'): sniffEnv.Append( LIBS=[ "pcap" ] ) else: sniffEnv.Append( LIBS=[ "wpcap" ] ) sniffEnv.Install( '#/', sniffEnv.Program( "mongosniff", "tools/sniffer.cpp", LIBDEPS = [ "db/serveronly", "db/coredb", ] ) ) # --- shell --- if not has_option('noshell') and usemozjs: shell_core_env = env.Clone() if has_option("safeshell"): shell_core_env.Append(CPPDEFINES=["MONGO_SAFE_SHELL"]) shell_core_env.Library("shell_core", source=[ "shell/bench.cpp", "shell/clientAndShell.cpp", "shell/linenoise.cpp", "shell/mk_wcwidth.cpp", "shell/mongo-server.cpp", "shell/shell_utils.cpp", "shell/shell_utils_extended.cpp", "shell/shell_utils_launcher.cpp", "shell/shell_options_init.cpp" ], LIBDEPS=[ 'db/index/external_key_generator', 'db/catalog/index_key_validate', 'scripting/scripting', 'util/processinfo', 'util/signal_handlers', 'linenoise_utf8', 'shell/mongojs', ], LIBDEPS_TAGS=[ # Circular with shell_options below. It is unclear # why this is split into two libraries, or even # why these libraries exist at all, since it seems # that the only thing that links them is the shell # itself. 'incomplete', ], ) # mongo shell options shell_core_env.Library("shell_options", ["shell/shell_options.cpp"], LIBDEPS=[ '$BUILD_DIR/mongo/db/server_options_core', '$BUILD_DIR/mongo/rpc/protocol', '$BUILD_DIR/mongo/util/net/network', '$BUILD_DIR/mongo/util/options_parser/options_parser_init', 'shell_core', ]) shellEnv = env.Clone() if env.TargetOSIs('windows'): shellEnv.Append(LIBS=["winmm.lib"]) mongo_shell = shellEnv.Program( "mongo", "shell/dbshell.cpp", LIBDEPS=["$BUILD_DIR/third_party/shim_pcrecpp", "shell_options", "shell_core", "db/server_options_core", "client/clientdriver", "$BUILD_DIR/mongo/util/password", ]) shellEnv.Install( '#/', mongo_shell ) else: shellEnv = None # ---- INSTALL ------- # binaries distBinaries = [] distDebugSymbols = [] def add_exe( v ): return "${PROGPREFIX}%s${PROGSUFFIX}" % v def failMissingObjCopy(env, target, source): env.FatalError("Generating debug symbols requires objcopy, please set the OBJCOPY variable.") def installBinary( e, name ): debug_sym_name = name name = add_exe( name ) debug_sym_cmd = None if e.TargetOSIs('linux', 'solaris'): if 'OBJCOPY' not in e: debug_sym_cmd = failMissingObjCopy else: debug_sym_cmd = '${OBJCOPY} --only-keep-debug ${SOURCE} ${TARGET}' debug_sym_name += '.debug' elif e.TargetOSIs('osx'): debug_sym_name += '.dSYM' debug_sym_cmd = 'dsymutil -o ${TARGET} ${SOURCE}' elif e.ToolchainIs('msvc'): debug_sym_name += '.pdb' distBinaries.append(debug_sym_name) distDebugSymbols.append(debug_sym_name) if debug_sym_cmd: debug_sym = e.Command( debug_sym_name, name, debug_sym_cmd ) e.Install("#/", debug_sym) e.Alias('debugsymbols', debug_sym) distDebugSymbols.append(debug_sym) if env.TargetOSIs('linux', 'solaris') and (not has_option("nostrip")): strip_cmd = e.Command( 'stripped/%s' % name, [name, debug_sym], '${OBJCOPY} --strip-debug --add-gnu-debuglink ${SOURCES[1]} ${SOURCES[0]} $TARGET' ) distBinaries.append('stripped/%s' % name) else: distBinaries.append(name) inst = e.Install( "$INSTALL_DIR/bin", name ) if env.TargetOSIs('posix'): e.AddPostAction( inst, 'chmod 755 $TARGET' ) def installExternalBinary( e, name_str ): name = env.File("#/%s" % add_exe(name_str)) if not name.isfile(): env.FatalError("ERROR: external binary not found: {0}", name) distBinaries.append(name) inst = e.Install( "$INSTALL_DIR/bin", name ) if env.TargetOSIs('posix'): e.AddPostAction( inst, 'chmod 755 $TARGET' ) # "--use-new-tools" adds dependencies for rewritten (Go) tools # It is required for "dist" but optional for "install" if has_option("use-new-tools"): toolsRoot = "src/mongo-tools" for t in rewrittenTools: installExternalBinary(env, "%s/%s" % (toolsRoot, t)) # legacy tools installBinary(env, "mongoperf") env.Alias("tools", '#/' + add_exe("mongoperf")) env.Alias("tools", "#/" + add_exe("mongobridge")) if mongosniff_built: installBinary(env, "mongosniff") env.Alias("tools", '#/' + add_exe("mongosniff")) installBinary( env, "mongod" ) installBinary( env, "mongos" ) if shellEnv is not None: installBinary( shellEnv, "mongo" ) env.Alias( "core", [ '#/%s' % b for b in [ add_exe( "mongo" ), add_exe( "mongod" ), add_exe( "mongos" ) ] ] ) # Stage the top-level mongodb banners distsrc = env.Dir('#distsrc') env.Append(MODULE_BANNERS = [distsrc.File('README'), distsrc.File('THIRD-PARTY-NOTICES'), distsrc.File('MPL-2')]) # If no module has introduced a file named LICENSE.txt, then inject the AGPL. if sum(itertools.imap(lambda x: x.name == "LICENSE.txt", env['MODULE_BANNERS'])) == 0: env.Append(MODULE_BANNERS = [distsrc.File('GNU-AGPL-3.0')]) # All module banners get staged to the top level of the tarfile, so we # need to fail if we are going to have a name collision. module_banner_filenames = set([f.name for f in env['MODULE_BANNERS']]) if not len(module_banner_filenames) == len(env['MODULE_BANNERS']): # TODO: Be nice and identify conflicts in error. env.FatalError("ERROR: Filename conflicts exist in module banners.") # Build a set of directories containing module banners, and use that # to build a --transform option for each directory so that the files # are tar'ed up to the proper location. module_banner_dirs = set([Dir('#').rel_path(f.get_dir()) for f in env['MODULE_BANNERS']]) module_banner_transforms = ["--transform %s=$SERVER_DIST_BASENAME" % d for d in module_banner_dirs] # Allow modules to map original file name directories to subdirectories # within the archive (e.g. { "src/mongo/db/modules/enterprise/docs": "snmp"}) archive_addition_transforms = [] for full_dir, archive_dir in env["ARCHIVE_ADDITION_DIR_MAP"].items(): archive_addition_transforms.append("--transform \"%s=$SERVER_DIST_BASENAME/%s\"" % (full_dir, archive_dir)) # "dist" target is valid only when --use-new-tools is specified # Attempts to build release artifacts without tools must fail if has_option("use-new-tools"): env.Command( target='#/${SERVER_ARCHIVE}', source=['#buildscripts/make_archive.py'] + env["MODULE_BANNERS"] + env["ARCHIVE_ADDITIONS"] + distBinaries, action=' '.join( ['$PYTHON ${SOURCES[0]} -o $TARGET'] + archive_addition_transforms + module_banner_transforms + [ '--transform $BUILD_DIR/mongo/stripped=$SERVER_DIST_BASENAME/bin', '--transform $BUILD_DIR/mongo=$SERVER_DIST_BASENAME/bin', '--transform $BUILD_DIR/mongo/stripped/src/mongo-tools=$SERVER_DIST_BASENAME/bin', '--transform src/mongo-tools=$SERVER_DIST_BASENAME/bin', '${TEMPFILE(SOURCES[1:])}' ], ), BUILD_DIR=env.Dir('$BUILD_DIR').path ) env.Alias("dist", source='#/${SERVER_ARCHIVE}') else: def failDist(env, target, source): env.FatalError("ERROR: 'dist' target only valid with --use-new-tools.") env.Alias("dist", [], [ failDist ] ) env.AlwaysBuild("dist") debug_symbols_dist = env.Command( target='#/${SERVER_DIST_BASENAME}-debugsymbols${DIST_ARCHIVE_SUFFIX}', source=['#buildscripts/make_archive.py'] + distDebugSymbols, action='$PYTHON ${SOURCES[0]} -o $TARGET --transform $BUILD_DIR/mongo=$SERVER_DIST_BASENAME ${TEMPFILE(SOURCES[1:])}', BUILD_DIR=env.Dir('$BUILD_DIR').path ) env.Alias('dist-debugsymbols', debug_symbols_dist) #final alias env.Alias( "install", "$INSTALL_DIR" )