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
Exemple #2
0
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
Exemple #6
0
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
Exemple #7
0
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)