summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Morrow <andrew.morrow@10gen.com>2019-09-16 19:21:02 +0000
committerevergreen <evergreen@mongodb.com>2019-09-16 19:21:02 +0000
commit54d1ab613046f06d0524aa9f7a244dfe3a076aa0 (patch)
treeaf8cc0576c03cdd0b6e08588304b55ae932ffbe5
parenteddda0424bd26a9784c373d510a61a49b38790e8 (diff)
downloadmongo-54d1ab613046f06d0524aa9f7a244dfe3a076aa0.tar.gz
SERVER-42887 Make roles injectible instead
-rw-r--r--SConstruct60
-rw-r--r--site_scons/site_tools/auto_install_binaries.py186
2 files changed, 166 insertions, 80 deletions
diff --git a/SConstruct b/SConstruct
index 4fd117320f0..e3e470619c3 100644
--- a/SConstruct
+++ b/SConstruct
@@ -3767,6 +3767,44 @@ if get_option('install-mode') == 'hygienic':
env["AIB_TARBALL_SUFFIX"] = "tgz"
env.Tool('auto_install_binaries')
+ env.DeclareRoles(
+ roles=[
+
+ env.Role(
+ name="base",
+ ),
+
+ env.Role(
+ name="debug",
+ ),
+
+ env.Role(
+ name="dev",
+ dependencies=[
+ "runtime"
+ ],
+ ),
+
+ env.Role(
+ name="meta",
+ ),
+
+ env.Role(
+ name="runtime",
+ dependencies=[
+ # On windows, we want the runtime role to depend
+ # on the debug role so that PDBs end in the
+ # runtime package.
+ "debug" if env.TargetOSIs('windows') else None,
+ ],
+ transitive=True,
+ silent=True,
+ ),
+ ],
+ base_role="base",
+ meta_role="meta",
+ )
+
env.AddSuffixMapping({
"$PROGSUFFIX": env.SuffixMap(
directory="$PREFIX_BINDIR",
@@ -3774,7 +3812,7 @@ if get_option('install-mode') == 'hygienic':
"runtime",
]
),
-
+
"$LIBSUFFIX": env.SuffixMap(
directory="$PREFIX_LIBDIR",
default_roles=[
@@ -3797,7 +3835,7 @@ if get_option('install-mode') == 'hygienic':
"debug",
]
),
-
+
".dSYM": env.SuffixMap(
directory="$PREFIX_DEBUGDIR",
default_roles=[
@@ -3812,26 +3850,8 @@ if get_option('install-mode') == 'hygienic':
]
),
- ".lib": env.SuffixMap(
- directory="$PREFIX_LIBDIR",
- default_roles=[
- "dev"
- ]
- ),
-
- ".h": env.SuffixMap(
- directory="$PREFIX_INCLUDEDIR",
- default_roles=[
- "dev",
- ]
- ),
})
- if env.TargetOSIs('windows'):
- # On windows, we want the runtime role to depend on the debug role so that PDBs
- # end in the runtime package.
- env.AddRoleDependencies(role="runtime", dependencies=["debug"])
-
env.AddPackageNameAlias(
component="dist",
role="runtime",
diff --git a/site_scons/site_tools/auto_install_binaries.py b/site_scons/site_tools/auto_install_binaries.py
index 4344949761d..a26838b8616 100644
--- a/site_scons/site_tools/auto_install_binaries.py
+++ b/site_scons/site_tools/auto_install_binaries.py
@@ -31,14 +31,16 @@ import SCons
from SCons.Tool import install
ALIAS_MAP = "AIB_ALIAS_MAP"
+BASE_ROLE = "AIB_BASE_ROLE"
COMPONENTS = "AIB_COMPONENTS_EXTRA"
INSTALL_ACTIONS = "AIB_INSTALL_ACTIONS"
+META_ROLE = "AIB_META_ROLE"
PACKAGE_ALIAS_MAP = "AIB_PACKAGE_ALIAS_MAP"
PACKAGE_PREFIX = "AIB_PACKAGE_PREFIX"
PRIMARY_COMPONENT = "AIB_COMPONENT"
PRIMARY_ROLE = "AIB_ROLE"
ROLES = "AIB_ROLES"
-ROLE_DEPENDENCIES = "AIB_ROLE_DEPENDENCIES"
+ROLE_DECLARATIONS = "AIB_ROLE_DECLARATIONS"
SUFFIX_MAP = "AIB_SUFFIX_MAP"
AIB_MAKE_ARCHIVE_CONTENT = """
@@ -100,14 +102,6 @@ if __name__ == "__main__":
archive.close()
"""
-AVAILABLE_ROLES = [
- "base",
- "debug",
- "dev",
- "meta",
- "runtime",
-]
-
RoleInfo = namedtuple(
'RoleInfo',
[
@@ -124,12 +118,111 @@ SuffixMap = namedtuple(
],
)
-def generate_alias(component, role, target="install"):
+class DeclaredRole():
+ def __init__(self, name, dependencies=None, transitive=False, silent=False):
+ self.name = name
+
+ if dependencies is None:
+ self.dependencies = set()
+ else:
+ self.dependencies = {dep for dep in dependencies if dep is not None}
+
+ self.transitive = transitive
+ self.silent = silent
+
+def declare_role(env, **kwargs):
+ """Construct a new role declaration"""
+ return DeclaredRole(**kwargs)
+
+def declare_roles(env, roles, base_role=None, meta_role=None):
+ """Given a list of role declarations, validate them and store them in the environment"""
+
+ role_names = [role.name for role in roles]
+ if len(role_names) != len(set(role_names)):
+ raise Exception(
+ "Cannot declare duplicate roles"
+ )
+
+ # Ensure that all roles named in dependency lists actually were
+ # passed in as a role.
+ for role in roles:
+ for d in role.dependencies:
+ if d not in role_names:
+ raise Exception(
+ "Role dependency '{}' does not name a declared role".format(d)
+ )
+
+ if isinstance(base_role, str):
+ if base_role not in role_names:
+ raise Exception(
+ "A base_role argument was provided but it does not name a declared role"
+ )
+ elif isinstance(base_role, DeclaredRole):
+ if base_role not in roles:
+ raise Exception(
+ "A base_role argument was provided but it is not a declared role"
+ )
+ elif base_role is not None:
+ raise Exception(
+ "The base_role argument must be a string name of a role or a role object"
+ )
+ else:
+ # Set it to something falsy
+ base_role = str()
+
+ if isinstance(meta_role, str):
+ if meta_role not in role_names:
+ raise Exception(
+ "A meta_role argument was provided but it does not name a declared role"
+ )
+ elif isinstance(meta_role, DeclaredRole):
+ if meta_role not in roles:
+ raise Exception(
+ "A meta_role argument was provided but it is not a declared role"
+ )
+ elif meta_role is not None:
+ raise Exception(
+ "The meta_role argument must be a string name of a role or a role object"
+ )
+ else:
+ # Set it to something falsy
+ meta_role = str()
+
+ silents = [role for role in roles if role.silent]
+ if len(silents) > 1:
+ raise Exception(
+ "No more than one role can be declared as silent"
+ )
+
+ # If a base role was given, then add it as a dependency of every
+ # role that isn't the base role (which would be circular).
+ if base_role:
+ for role in roles:
+ if role.name != base_role:
+ role.dependencies.add(base_role)
+
+ # Become a dictionary, so we can look up roles easily.
+ roles = { role.name : role for role in roles }
+
+ # If a meta role was given, then add every role which isn't the
+ # meta role as one of its dependencies.
+ if meta_role:
+ roles[meta_role].dependencies.update(r for r in roles.keys() if r != meta_role)
+
+ # TODO: Check for DAG
+
+ # TODO: What if base_role or meta_role is really None?
+ env[BASE_ROLE] = base_role
+ env[META_ROLE] = meta_role
+ env[ROLE_DECLARATIONS] = roles
+
+
+def generate_alias(env, component, role, target="install"):
"""Generate a scons alias for the component and role combination"""
return "{target}-{component}{role}".format(
target=target,
component=component,
- role="" if role == "runtime" else "-" + role,
+ role="" if env[ROLE_DECLARATIONS][role].silent else "-" + role,
)
@@ -143,6 +236,7 @@ def get_package_name(env, component, role):
def get_dependent_actions(
+ env,
components,
roles,
non_transitive_roles,
@@ -170,7 +264,7 @@ def get_dependent_actions(
#
# If they are overlapping then that means we can't transition to a
# new role during scanning.
- if "base" not in roles:
+ if env[BASE_ROLE] not in roles:
can_transfer = (
non_transitive_roles
and roles.isdisjoint(non_transitive_roles)
@@ -181,11 +275,11 @@ def get_dependent_actions(
node_roles = {
role for role
in getattr(node.attributes, ROLES, set())
- if role != "meta"
+ if role != env[META_ROLE]
}
if (
# TODO: make the "always transitive" roles configurable
- "base" not in node_roles
+ env[BASE_ROLE] not in node_roles
# If we are not transferrable
and not can_transfer
# Checks if we are actually crossing a boundry
@@ -218,10 +312,10 @@ def scan_for_transitive_install(node, env, cb=None):
roles = {
role for role
in getattr(node.sources[0].attributes, ROLES, set())
- if role != "meta"
+ if role != env[META_ROLE]
}
- # TODO: add fancy configurability
- non_transitive_roles = {role for role in roles if role == "runtime"}
+
+ non_transitive_roles = {role for role in roles if env[ROLE_DECLARATIONS][role].transitive}
for install_source in install_sources:
install_executor = install_source.get_executor()
if not install_executor:
@@ -234,6 +328,7 @@ def scan_for_transitive_install(node, env, cb=None):
for grandchild in grandchildren:
results.extend(
get_dependent_actions(
+ env,
components,
roles,
non_transitive_roles,
@@ -319,10 +414,11 @@ def auto_install(env, target, source, **kwargs):
source = [env.Entry(s) for s in env.Flatten([source])]
roles = {
kwargs.get(PRIMARY_ROLE),
- # The 'meta' tag is implicitly attached as a role.
- "meta",
}
+ if env[META_ROLE]:
+ roles.add(env[META_ROLE])
+
if kwargs.get(ROLES) is not None:
roles = roles.union(set(kwargs[ROLES]))
@@ -381,7 +477,7 @@ def auto_install(env, target, source, **kwargs):
actions = env.Flatten(actions)
for component, role in itertools.product(components, roles):
- alias_name = generate_alias(component, role)
+ alias_name = generate_alias(env, component, role)
alias = env.Alias(alias_name, actions)
setattr(alias[0].attributes, COMPONENTS, components)
setattr(alias[0].attributes, ROLES, roles)
@@ -390,7 +486,7 @@ def auto_install(env, target, source, **kwargs):
if component != "common":
# We have to call env.Alias just in case the
# generated_alias does not already exist.
- env.Depends(alias, env.Alias(generate_alias("common", role)))
+ env.Depends(alias, env.Alias(generate_alias(env, "common", role)))
env[ALIAS_MAP][component][role] = RoleInfo(
alias_name=alias_name,
@@ -424,7 +520,8 @@ def finalize_install_dependencies(env):
env.Depends(info.alias, common_rolemap[role].alias)
aliases.extend(common_rolemap[role].alias)
- for dependency in env[ROLE_DEPENDENCIES].get(role, []):
+ role_decl = env[ROLE_DECLARATIONS].get(role)
+ for dependency in role_decl.dependencies:
dependency_info = rolemap.get(dependency, [])
if dependency_info:
env.Depends(info.alias, dependency_info.alias)
@@ -450,7 +547,7 @@ def finalize_install_dependencies(env):
# configurable? It's possible someone would want to do it.
env.NoCache(archive)
- archive_alias = generate_alias(component, role, target=fmt)
+ archive_alias = generate_alias(env, component, role, target=fmt)
env.Alias(archive_alias, archive)
@@ -477,10 +574,10 @@ def auto_install_emitter(target, source, env):
def add_suffix_mapping(env, suffix, role=None):
"""Map suffix to role"""
if isinstance(suffix, str):
- if role not in AVAILABLE_ROLES:
+ if role not in env[ROLE_DECLARATIONS]:
raise Exception(
"target {} is not a known role. Available roles are {}".format(
- role, AVAILABLE_ROLES
+ role, env[ROLE_DECLARATIONS].keys()
)
)
env[SUFFIX_MAP][env.subst(suffix)] = role
@@ -490,10 +587,10 @@ def add_suffix_mapping(env, suffix, role=None):
for _, mapping in suffix.items():
for role in mapping.default_roles:
- if role not in AVAILABLE_ROLES:
+ if role not in env[ROLE_DECLARATIONS]:
raise Exception(
"target {} is not a known role. Available roles are {}".format(
- target, AVAILABLE_ROLES
+ target, env[ROLE_DECLARATIONS].keys()
)
)
@@ -511,13 +608,6 @@ def add_package_name_alias(env, component, role, name):
raise Exception("No role provided for package name alias")
env[PACKAGE_ALIAS_MAP][(component, role)] = name
-def add_role_dependencies(env, role, dependencies):
- current = env[ROLE_DEPENDENCIES].get(role, None)
- if not current:
- current = env[ROLE_DEPENDENCIES][role] = list()
- current.extend(dependencies)
- current = list(set(current))
-
def suffix_mapping(env, directory=False, default_roles=False):
"""Generate a SuffixMap object from source and target."""
return SuffixMap(
@@ -525,7 +615,6 @@ def suffix_mapping(env, directory=False, default_roles=False):
default_roles=default_roles,
)
-
def dest_dir_generator(initial_value=None):
"""Memoized dest_dir_generator"""
dd = (None, None)
@@ -613,37 +702,14 @@ def generate(env): # pylint: disable=too-many-statements
env[SUFFIX_MAP] = {}
env[PACKAGE_ALIAS_MAP] = {}
env[ALIAS_MAP] = defaultdict(dict)
- # TODO: make this configurable?
- env[ROLE_DEPENDENCIES] = {
- "debug": [
- "base",
-
- # TODO: Debug should depend on these when making packages, but shouldn't when building
- # the legacy tarball. Probably fuel for the above configurability fire. For now, make it not
- # depend so that we can get AIB in place for the dist builders.
- # "runtime",
- ],
- "dev": [
- "base",
- "runtime",
- ],
- "meta": [
- "base",
- "debug",
- "dev",
- "runtime",
- ],
- "runtime": [
- "base",
- ],
- }
env.AddMethod(suffix_mapping, "SuffixMap")
env.AddMethod(add_suffix_mapping, "AddSuffixMapping")
env.AddMethod(add_package_name_alias, "AddPackageNameAlias")
env.AddMethod(auto_install, "AutoInstall")
env.AddMethod(finalize_install_dependencies, "FinalizeInstallDependencies")
- env.AddMethod(add_role_dependencies, "AddRoleDependencies")
+ env.AddMethod(declare_role, "Role")
+ env.AddMethod(declare_roles, "DeclareRoles")
env.Tool("install")
# TODO: we should probably expose these as PseudoBuilders and let