def CallCustomMethod( customization_filename, method_name, kwargs, as_list=True, ): """ Calls the specified method if it exists with the args that it expects. Ensure that the return value is None or a list of items. """ with Utilities.CustomMethodManager(customization_filename, method_name) as method: if method is None: return None method = CommonEnvironmentImports.Interface.CreateCulledCallable( method) result = method(kwargs) if as_list and not isinstance(result, list) and result is not None: result = [ result, ] return result
def EntryPoint( output_filename_or_stdout, repository_root, debug=False, verbose=False, configuration=None, output_stream=sys.stdout, ): configurations = configuration or []; del configuration if debug: verbose = True # Get the setup customization module potential_customization_filename = os.path.join(repository_root, Constants.SETUP_ENVIRONMENT_CUSTOMIZATION_FILENAME) if os.path.isfile(potential_customization_filename): sys.path.insert(0, repository_root) with CommonEnvironmentImports.CallOnExit(lambda: sys.path.pop(0)): customization_mod = __import__(os.path.splitext(Constants.SETUP_ENVIRONMENT_CUSTOMIZATION_FILENAME)[0]) else: customization_mod = None # ---------------------------------------------------------------------- def Execute(): commands = [] for func in [ _SetupBootstrap, _SetupCustom, _SetupShortcuts, _SetupGeneratedPermissions, _SetupScmHooks, ]: these_commands = func( repository_root, customization_mod, debug, verbose, configurations, ) if these_commands: commands += these_commands return commands # ---------------------------------------------------------------------- result, commands = Utilities.GenerateCommands(Execute, debug) if output_filename_or_stdout == "stdout": output_stream = sys.stdout close_stream_func = lambda: None else: output_stream = open(output_filename_or_stdout, 'w') close_stream_func = output_stream.close with CommonEnvironmentImports.CallOnExit(close_stream_func): output_stream.write(CommonEnvironmentImports.CurrentShell.GenerateCommands(commands)) return result
def Create(cls, repo_root, configuration=None): if CommonEnvironmentImports.CurrentShell.IsSymLink(repo_root): repo_root = CommonEnvironmentImports.CurrentShell.ResolveSymLink( repo_root) repo_name, repo_guid = Utilities.GetRepositoryUniqueId(repo_root) return cls( repo_guid, repo_name, repo_root, configuration=configuration, )
def Load( cls, repository_root, configuration, is_fast_environment, force=False, ): if not force and os.getenv(Constants.DE_REPO_ROOT_NAME): repository_root = os.getenv(Constants.DE_REPO_ROOT_NAME) configuration = os.getenv(Constants.DE_REPO_CONFIGURATION_NAME) filename = cls._GetFilename(repository_root, configuration, is_fast_environment) if not force and os.path.isfile(filename): try: # Load the json with open(filename, 'r') as f: data = json.load(f) # Convert the json structure into concrete types tool_version_specs = [ Configuration.VersionInfo( vi_data["Name"], vi_data["Version"], ) for vi_data in data["VersionSpecs"]["Tools"] ] library_version_specs = { language: [ Configuration.VersionInfo( vi_data["Name"], vi_data["Version"], ) for vi_data in language_version_infos ] for language, language_version_infos in six.iteritems( data["VersionSpecs"]["Libraries"]) } return cls( data["Id"], data["Root"], data["IsMixinRepo"], data["Configuration"], [ Repository( repo_data["Id"], repo_data["Name"], repo_data["Root"], repo_data["Configuration"], repo_data["IsMixinRepo"], ) for repo_data in data["PrioritizedRepositories"] ], Configuration.VersionSpecs( tool_version_specs, library_version_specs, ), ) except: pass # Generate the data repository_root = CommonEnvironmentImports.FileSystem.Normalize( repository_root) assert os.path.isdir(repository_root), repository_root repositories = OrderedDict() tool_version_info = [] library_version_info = {} version_info_lookup = {} # ---------------------------------------------------------------------- def Walk( referencing_repo, repo, priority_modifier, ): if repo.Id not in repositories: bootstrap_info = EnvironmentBootstrap.Load(repo.Root) bootstrap_info.Repo = repo bootstrap_info.ReferencingRepo = referencing_repo bootstrap_info.priority_modifier = 0 repositories[repo.Id] = bootstrap_info recurse = True else: recurse = False bootstrap_info = repositories[repo.Id] bootstrap_info.priority_modifier += priority_modifier # Ensure that the configuration name is valid if bootstrap_info.IsConfigurable and not repo.Configuration: raise Exception( "The repository at '{}' is configurable, but no configuration was provided." .format(repo.Root)) if not bootstrap_info.IsConfigurable and repo.Configuration: raise Exception( "The repository at '{}' is not configurable, but a configuration was provided ({})." .format(repo.Root, repo.Configuration)) if repo.Configuration not in bootstrap_info.Configurations: raise Exception( textwrap.dedent("""\ The configuration '{config}' is not a valid configuration for the repository at '{root}'. Valid configuration values are: {configs} """).format( config=repo.Configuration, root=repo.Root, configs='\n'.join([ " - {}".format(config or "<None>") for config in six.iterkeys(bootstrap_info.Configurations) ]), )) # Check for consistent repo locations if repo.Root != bootstrap_info.Repo.Root: raise Exception( textwrap.dedent("""\ There is a mismatch in repository locations. Repository: {name} <{id}> New Location: {new_value} Referenced By: {new_name} <{new_id}> [{new_root}] Original Location: {original_value} Referenced By: {original_name} <{original_id}> [{original_root}] """).format( name=repo.Name, id=repo.Id, new_value=repo.Root, new_name=referencing_repo.Name, new_id=referencing_repo.Id, new_root=referencing_repo.Root, original_value=bootstrap_info.Repo.Root, original_name=bootstrap_info.Repo.Name, original_id=bootstrap_info.Repo.Id, original_root=bootstrap_info.Repo.Root, )) # Check for consistent configurations if repo.Configuration != bootstrap_info.Repo.Configuration: raise Exception( textwrap.dedent("""\ There is a mismatch in repository configurations: Repository: {name} <{id}> New Configuration: {new_value} Referenced By: {new_name} <{new_id}> [{new_root}] Original Configuration: {original_value} Referenced By: {original_name} <{original_id}> [{original_root}] """).format( name=repo.Name, id=repo.Id, new_value=repo.Configuration, new_name=referencing_repo.Name, new_id=referencing_repo.Id, new_root=referencing_repo.Root, original_value=bootstrap_info.Repo.Configuration, original_name=bootstrap_info.Repo.Name, original_id=bootstrap_info.Repo.Id, original_root=bootstrap_info.Repo.Root, )) # Process the version info # ---------------------------------------------------------------------- def OnVersionMismatch(type_, version_info, existing_version_info): original_repo = version_info_lookup[existing_version_info] raise Exception( textwrap.dedent("""\ There was a mismatch in version information. Item: {name} <{type_}> New Version: {new_value} Specified By: {new_name} ({new_config}) <{new_id}> [{new_root}] Original Version: {original_value} Specified By: {original_name} ({original_config}) <{original_id}> [{original_root}] """).format( name=version_info.Name, type_=type_, new_value=version_info.Version, new_name=repo.Name, new_config=repo.Configuration, new_id=repo.Id, new_root=repo.Root, original_value=existing_version_info.Version, original_name=original_repo.Name, original_config=original_repo.Configuration, original_id=original_repo.Id, original_root=original_repo.Root, )) # ---------------------------------------------------------------------- for version_info in bootstrap_info.Configurations[ repo.Configuration].VersionSpecs.Tools: existing_version_info = next( (tvi for tvi in tool_version_info if tvi.Name == version_info.Name), None) if existing_version_info is None: tool_version_info.append(version_info) version_info_lookup[version_info] = repo elif version_info.Version != existing_version_info.Version: OnVersionMismatch("Tools", version_info, existing_version_info) for library_language, version_info_items in six.iteritems( bootstrap_info.Configurations[ repo.Configuration].VersionSpecs.Libraries): for version_info in version_info_items: existing_version_info = next( (lvi for lvi in library_version_info.get( library_language, []) if lvi.Name == version_info.Name), None) if existing_version_info is None: library_version_info.setdefault( library_language, []).append(version_info) version_info_lookup[version_info] = repo elif version_info.Version != existing_version_info.Version: OnVersionMismatch( "{} Libraries".format(library_language), version_info, existing_version_info) # Process this repository's dependencies if recurse: for dependency_info in bootstrap_info.Configurations[ repo.Configuration].Dependencies: Walk( repo, Repository.Create(dependency_info.RepositoryRoot, dependency_info.Configuration), priority_modifier + 1, ) # ---------------------------------------------------------------------- this_repository = Repository.Create(repository_root, configuration) Walk(None, this_repository, 1) # Order the results from the most- to least-frequently requested priority_values = [(id, info.priority_modifier) for id, info in six.iteritems(repositories)] priority_values.sort(key=lambda x: x[1], reverse=True) this_bootstrap_info = repositories[priority_values[-1][0]] this_configuration = this_bootstrap_info.Configurations[configuration] # Check the fingerprints calculated_fingerprint = Utilities.CalculateFingerprint( [ repository_root, ] + [ dependency.RepositoryRoot for dependency in this_configuration.Dependencies ], repository_root, ) if this_configuration.Fingerprint != calculated_fingerprint: lines = [] line_template = "{0:<80} : {1}" for k, v in six.iteritems(calculated_fingerprint): if k not in this_configuration.Fingerprint: lines.append(line_template.format(k[0], "Added")) else: lines.append( line_template.format( k, "Identical" if v == this_configuration.Fingerprint[k] else "Modified")) for k in six.iteritems(this_configuration.Fingerprint): if k not in calculated_fingerprint: lines.append(line_template.format(k[0], "Removed")) assert lines raise Exception( textwrap.dedent("""\ ******************************************************************************** ******************************************************************************** It appears that one or more of the repositories that this repository depends on have changed. Please run '{setup}' again. {status} ******************************************************************************** ******************************************************************************** """).format( setup=CommonEnvironmentImports.CurrentShell. CreateScriptName(Constants.SETUP_ENVIRONMENT_NAME), status=CommonEnvironmentImports.StringHelpers.LeftJustify( '\n'.join(lines), 4), )) # Create the object return cls( this_repository.Id, repository_root, this_bootstrap_info.IsMixinRepo, configuration, is_fast_environment, [repositories[id].Repo for id, _ in priority_values], Configuration.VersionSpecs(tool_version_info, library_version_info), )
def _Impl( display_sentinel, json_filename, result_filename, first, output_stream, method_name, parser, ): output_stream = StreamDecorator( output_stream, line_prefix=display_sentinel, ) with open(json_filename) as f: try: data = parser(f.read(), is_root=True) except Exception as ex: output_stream.write("ERROR: {} ({})\n".format(str(ex), ex.stack)) return -1 output_stream.write("Parsing dependencies...") with output_stream.DoneManager(): dependencies = ActivationData.Load(None, None, None).PrioritizedRepositories has_config_specific = False output_stream.write("Validating...") with output_stream.DoneManager() as dm: for index, repository_info in enumerate(dependencies): dm.stream.write("Processing '{}' ({} of {})...".format( repository_info.Name, index + 1, len(dependencies), )) with dm.stream.DoneManager() as this_dm: with Utilities.CustomMethodManager(os.path.join(repository_info.Root, Constants.HOOK_ENVIRONMENT_CUSTOMIZATION_FILENAME), method_name) as method: if not method: continue args = OrderedDict([ ( "data", data ), ( "output_stream", this_dm.stream ), ]) # Get the method args to see if a configuration is requried func_code = six.get_function_code(method) if "configuration" in func_code.co_varnames[:func_code.co_argcount]: args["configuration"] = repository_info.Configuration has_config_specific = True elif not first: # Don't call a config-agnostic method more than once continue try: this_dm.result = Interface.CreateCulledCallable(method)(args) or 0 except Exception as ex: this_dm.stream.write(StringHelpers.LeftJustify( "ERROR: {}\n".format(str(ex).rstrip()), len("ERROR: "), )) this_dm.result = -1 with open(result_filename, 'w') as f: f.write('-1' if dm.result != 0 else '1' if has_config_specific else '0') return dm.result
def Activate( output_filename_or_stdout, repository_root, configuration, debug=False, verbose=False, version_spec=None, no_python_libraries=False, force=False, fast=False, mixin=None, output_stream=sys.stdout, ): """Activates a respository for development activities.""" configuration = configuration if configuration.lower() != "none" else None version_specs = version_spec or {} del version_spec mixins = mixin or [] del mixin if debug: verbose = True output_stream = CommonEnvironmentImports.StreamDecorator(output_stream) # ---------------------------------------------------------------------- def Execute(): commands = [] # Load the activation data output_stream.write("\nLoading data...") with output_stream.DoneManager(suffix='\n', ): is_activated = bool(os.getenv(Constants.DE_REPO_ACTIVATED_FLAG)) activation_data = ActivationData.Load( repository_root, configuration, fast, force=force or not is_activated, ) # Augment the version specs with those provided on the command line for k, v in six.iteritems(version_specs): keys = k.split('/') if keys[0] == Constants.TOOLS_SUBDIR: if len(keys) != 2: raise Exception( "'{}' is not a valid version spec; expected '{}/<Tool Name>'." .format(k, Constants.TOOLS_SUBDIR)) name = keys[1] version_infos = activation_data.VersionSpecs.Tools elif keys[0] == Constants.LIBRARIES_SUBDIR: if len(keys) != 3: raise Exception( "'{}' is not a valid version spec; expected '{}/<Language>/<Library Name>'." .format(k, Constants.LIBRARIES_SUBDIR)) name = keys[2] version_infos = activation_data.VersionSpecs.Libraries.setdefault( keys[1], []) else: raise Exception( "'{}' is not a valid version spec prefix".format(keys[0])) found = False for version_info in version_infos: if version_info.Name == name: version_info.Version = v found = True break if not found: version_infos.append(Configuration.VersionInfo(name, v)) # ---------------------------------------------------------------------- def LoadMixinLibrary(mixin_path): mixin_activation_data = ActivationData.Load( mixin_path, configuration=None, force=True, ) if not mixin_activation_data.IsMixinRepo: raise Exception( "The repository at '{}' is not a mixin repository".format( mixin_path)) assert not mixin_activation_data.VersionSpecs.Tool assert not mixin_activation_data.VersionSpecs.Libraries assert len(mixin_activation_data.PrioritizedRepositories) == 1 mixin_repo = mixin_activation_data.PrioritizedRepositories[0] mixin_repo.IsMixinRepo = True # Add this repo as a repo to be activated if it isn't already in the list if not any(r.Id == mixin_repo.Id for r in activation_data.PrioritizedRepositories): activation_data.PrioritizedRepositories.append(mixin_repo) # ---------------------------------------------------------------------- # Are we activating a mixin repository? is_mixin_repo = EnvironmentBootstrap.Load(repository_root).IsMixinRepo if is_mixin_repo: if force: raise Exception( "'force' cannot be used with mixin repositories") LoadMixinLibrary(repository_root) for mixin in mixins: LoadMixinLibrary(mixin) # Ensure that the generated dir exists generated_dir = activation_data.GetActivationDir() CommonEnvironmentImports.FileSystem.MakeDirs(generated_dir) methods = [ _ActivateActivationData, _ActivateNames, _ActivateTools, _ActivatePython, _ActivateScripts, _ActivateCustom, _ActivatePrompt, ] if not is_mixin_repo: methods = [ _ActivateOriginalEnvironment, _ActivateRepoEnvironmentVars, ] + methods args = OrderedDict([ ("output_stream", output_stream), ("configuration", configuration), ("activation_data", activation_data), ("version_specs", activation_data.VersionSpecs), ("generated_dir", generated_dir), ("debug", debug), ("verbose", verbose), ("no_python_libraries", no_python_libraries), ("fast", fast), ("repositories", activation_data.PrioritizedRepositories), ("is_mixin_repo", is_mixin_repo), ]) # Invoke the methods for original_method in methods: method = CommonEnvironmentImports.Interface.CreateCulledCallable( original_method) result = method(args) if isinstance(result, list): commands += result elif result is not None: commands.append(result) return commands # ---------------------------------------------------------------------- result, commands = Utilities.GenerateCommands(Execute, debug) if output_filename_or_stdout == "stdout": output_stream = sys.stdout close_stream_func = lambda: None else: output_stream = open(output_filename_or_stdout, 'w') close_stream_func = output_stream.close with CommonEnvironmentImports.CallOnExit(close_stream_func): output_stream.write( CommonEnvironmentImports.CurrentShell.GenerateCommands(commands)) return result
def _SetupBootstrap( repository_root, customization_mod, debug, verbose, explict_configurations, search_depth=5, ): # Look for all dependencies by intelligently enumerating through the file system search_depth += repository_root.count(os.path.sep) if CommonEnvironmentImports.CurrentShell.CategoryName == "Windows": # Remove the slash assocaited with the drive name assert search_depth search_depth -= 1 fundamental_repo = RepositoryBootstrap.GetFundamentalRepository() repository_root_dirname = os.path.dirname(fundamental_repo) len_repository_root_dirname = len(repository_root_dirname) # ---------------------------------------------------------------------- class LookupObject(object): def __init__(self, name): self.Name = name self.repository_root = None self.dependent_configurations = [] def __str__(self): return CommonEnvironmentImports.CommonEnvironment.ObjectStrImpl(self) # ---------------------------------------------------------------------- def EnumerateDirectories(): search_items = [] searched_items = set() # ---------------------------------------------------------------------- def FirstNonmatchingChar(s): for index, c in enumerate(s): if ( index == len_repository_root_dirname or c != repository_root_dirname[index] ): break return index # ---------------------------------------------------------------------- def PushSearchItem(item): item = os.path.realpath(os.path.normpath(item)) parts = item.split(os.path.sep) if len(parts) > search_depth: return parts_lower = set([ part.lower() for part in parts ]) priority = 1 for bump_name in CODE_DIRECTORY_NAMES: if bump_name in parts_lower: priority = 0 break # Every item except the last is used for sorting search_items.append(( -FirstNonmatchingChar(item), # Favor parents over other locations priority, # Favor names that look like they could contain source doe len(parts), # Favor dirs closer to the root item.lower(), # Case insensitive sort item, )) search_items.sort() # ---------------------------------------------------------------------- def PopSearchItem(): return search_items.pop(0)[-1] # ---------------------------------------------------------------------- def Impl( skip_root, preprocess_item_func=None, # def Func(item) -> item ): preprocess_item_func = preprocess_item_func or (lambda item: item) while search_items: search_item = PopSearchItem() # Don't process if the dir has already been processed if search_item in searched_items: continue searched_items.add(search_item) # Don't process if the dir doesn't exist anymore (these searched can # take a while and dirs come and go) if not os.path.isdir(search_item): continue # Don't process if the dir has been explicitly ignored if os.path.exists(os.path.join(search_item, Constants.IGNORE_DIRECTORY_AS_BOOTSTRAP_DEPENDENCY_SENTINEL_FILENAME)): continue yield search_item try: # Add the parent to the queue potential_parent = os.path.dirname(search_item) if potential_parent != search_item: if not skip_root or os.path.dirname(potential_parent) != potential_parent: PushSearchItem(preprocess_item_func(potential_parent)) # Add the children to the queue for item in os.listdir(search_item): fullpath = os.path.join(search_item, item) if not os.path.isdir(fullpath): continue if item.lower() in ENUMERATE_EXCLUDE_DIRS: continue PushSearchItem(preprocess_item_func(fullpath)) except PermissionError: pass # ---------------------------------------------------------------------- PushSearchItem(repository_root) if CommonEnvironmentImports.CurrentShell.CategoryName == "Windows": # ---------------------------------------------------------------------- def ItemPreprocessor(item): drive, suffix = os.path.splitdrive(item) if drive[-1] == ':': drive = drive[:-1] return "{}:{}".format(drive.upper(), suffix) # ---------------------------------------------------------------------- for item in Impl(True, ItemPreprocessor): yield item # If here, look at other drive locations import win32api import win32file # <Module 'win32api' has not 'GetLogicalDriveStrings' member, but source is unavailable. Consider adding this module to extension-pkg-whitelist if you want to perform analysis based on run-time introspection of living objects.> pylint: disable = I1101 for drive in [ drive for drive in win32api.GetLogicalDriveStrings().split('\000') if drive and win32file.GetDriveType(drive) == win32file.DRIVE_FIXED ]: PushSearchItem(drive) for item in Impl(False, ItemPreprocessor): yield item else: for item in Impl(False): yield item # ---------------------------------------------------------------------- # Get the configurations from the setup script configurations = None is_mixin_repository = False if customization_mod: dependencies_func = getattr(customization_mod, Constants.SETUP_ENVIRONMENT_DEPENDENCIES_METHOD_NAME, None) if dependencies_func is not None: configurations = dependencies_func() if configurations and not isinstance(configurations, dict): configurations = { None : configurations, } # Is this a mixin repo? Mixin repos are specified via the MixinRepository # decorator. is_mixin_repository = ( hasattr(dependencies_func, "_self_wrapper") and dependencies_func._self_wrapper.__name__ == "MixinRepository" ) if not configurations: configurations = { None : Configuration("Default Configuration"), } has_configurations = len(configurations) > 1 or next(six.iterkeys(configurations)) is not None # A mixin repository cannot have configurations, dependencies, or version specs if ( is_mixin_repository and ( has_configurations or next(six.itervalues(configurations)).Dependencies or next(six.itervalues(configurations)).VersionSpecs.Tools or next(six.itervalues(configurations)).VersionSpecs.Libraries ) ): raise Exception("A mixin repository cannot have any configurations, dependencies, or version specs.") # Remove any configurations that shouldn't be setup if explict_configurations: for config_name in list(six.iterkeys(configurations)): if config_name not in explict_configurations: del configurations[config_name] # Create a repo lookup list fundamental_name, fundamental_guid = Utilities.GetRepositoryUniqueId(fundamental_repo) id_lookup = OrderedDict([ ( fundamental_guid, LookupObject(fundamental_name), ), ]) for config_name, config_info in six.iteritems(configurations): for dependency_info in config_info.Dependencies: if dependency_info.RepositoryId not in id_lookup: id_lookup[dependency_info.RepositoryId] = LookupObject(dependency_info.FriendlyName) id_lookup[dependency_info.RepositoryId].dependent_configurations.append(config_name) # Display status col_sizes = [ 54, 32, 100, ] display_template = "{{name:<{0}}} {{guid:<{1}}} {{data:<{2}}}".format(*col_sizes) max_config_name_length = int(col_sizes[0] * 0.75) config_display_info = [] for config_name, config_info in six.iteritems(configurations): if config_name is None: continue max_config_name_length = max(max_config_name_length, len(config_name)) config_display_info.append(( config_name, config_info.Description )) sys.stdout.write(textwrap.dedent( """\ Your system will be scanned for these repositories: {header} {sep} {values} {configurations} """).format( header=display_template.format( name="Repository Name", guid="Id", data="Dependent Configurations", ), sep=display_template.format(**{ k : v for k, v in six.moves.zip( [ "name", "guid", "data", ], [ '-' * col_size for col_size in col_sizes ], ) }), values='\n '.join([ display_template.format( name=v.Name, guid=k, data=', '.join(sorted([ dc for dc in v.dependent_configurations if dc ], key=str.lower)), ) for k, v in six.iteritems(id_lookup) ]), configurations='' if not has_configurations else CommonEnvironmentImports.StringHelpers.LeftJustify( textwrap.dedent( # <Wrong hanging indentation> pylint: disable = C0330 """\ Based on these configurations: {} {} """).format( CommonEnvironmentImports.StringHelpers.LeftJustify( '\n'.join([ "- {0:<{1}}{2}".format( config_name, max_config_name_length, " : {}".format(description), ) for config_name, description in config_display_info ]), 4, ), '' if explict_configurations else textwrap.dedent( # <Wrong hanging indentation> pylint: disable = C0330 """\ To setup specific configurations, specify this argument one or more times on the command line: /configuration=<configuration name> """).rstrip(), ), 4, ), )) # Find them all remaining_repos = len(id_lookup) verbose_stream = CommonEnvironmentImports.StreamDecorator(sys.stdout if debug or verbose else None) for directory in EnumerateDirectories(): verbose_stream.write("Searching in '{}'...\n".format(directory)) result = Utilities.GetRepositoryUniqueId( directory, raise_on_error=False, ) if result is None: continue repo_guid = result[1] if repo_guid in id_lookup: # Note that we may already have a repository associated with this guid. # This can happen when the repo has already been found near the # originating repo and the search has continued into directories further # away. if id_lookup[repo_guid].repository_root is None: id_lookup[repo_guid].repository_root = directory remaining_repos -= 1 if not remaining_repos: break verbose_stream.write('\n') if remaining_repos: unknown_repos = [] for repo_guid, lookup_info in six.iteritems(id_lookup): if lookup_info.repository_root is None: unknown_repos.append(( lookup_info.Name, repo_guid )) assert unknown_repos raise Exception(textwrap.dedent( """\ Unable to find {repository} {repos} """).format( repository=inflect.no("repository", len(unknown_repos)), repos='\n'.join([ " - {} ({})".format(repo_name, repo_guid) for repo_name, repo_guid in unknown_repos ]), )) sys.stdout.write(textwrap.dedent( """\ {repository} {was} found at {this} {location}: {header} {sep} {values} """).format( repository=inflect.no("repository", len(id_lookup)), was=inflect.plural_verb("was", len(id_lookup)), this=inflect.plural_adj("this", len(id_lookup)), location=inflect.plural("location", len(id_lookup)), header=display_template.format( name="Repository Name", guid="Id", data="Location", ), sep=display_template.format(**{ k : v for k, v in six.moves.zip( [ "name", "guid", "data", ], [ '-' * col_size for col_size in col_sizes ], ) }), values=CommonEnvironmentImports.StringHelpers.LeftJustify( '\n'.join([ display_template.format( name=lookup_info.Name, guid=repo_guid, data=lookup_info.repository_root, ) for repo_guid, lookup_info in six.iteritems(id_lookup) ]), 4, ), )) # Populate the configuration locations and Calcualte fingerprints for config_name, config_info in six.iteritems(configurations): repository_roots = [] for dependency in config_info.Dependencies: assert dependency.RepositoryRoot is None, dependency.RepositoryRoot assert dependency.RepositoryId in id_lookup, dependency dependency.RepositoryRoot = id_lookup[dependency.RepositoryId].repository_root repository_roots.append(dependency.RepositoryRoot) config_info.Fingerprint = Utilities.CalculateFingerprint( [ repository_root, ] + repository_roots, repository_root, ) EnvironmentBootstrap( fundamental_repo, is_mixin_repository, has_configurations, configurations, ).Save(repository_root)