internal List <Service> ResolveServices(List <Service> services)
        {
            var pass = 1;

            if (this.m_ShowDebugInformation)
            {
                RedirectableConsole.WriteLine("Performing service resolution pass " + pass + ":");
            }

            while (this.PerformResolutionPass(services))
            {
                // Returns true if it made any modifications.

                ++pass;

                if (this.m_ShowDebugInformation)
                {
                    RedirectableConsole.WriteLine("Performing service resolution pass " + pass + ":");
                }
            }

            if (this.m_ShowDebugInformation)
            {
                RedirectableConsole.WriteLine("Service resolution passes complete.");
            }

            return(services.Where(x => x.DesiredLevel != ServiceDesiredLevel.Disabled && x.DesiredLevel != ServiceDesiredLevel.Unused).ToList());
        }
        internal void EnableDefaultAndExplicitServices(List <Service> services)
        {
            if (this.m_ShowDebugInformation)
            {
                RedirectableConsole.WriteLine("Enabling default services, and services enabled / disabled by the user:"******" set to Default, because it is marked DefaultForRoot");
                        }

                        service.DesiredLevel = ServiceDesiredLevel.Default;
                    }
                }

                if (this.m_EnabledServices.Any(x => x == service.ServiceName || x == service.FullName))
                {
                    if (this.m_ShowDebugInformation)
                    {
                        RedirectableConsole.WriteLine(service.FullName + " set to Required, because it is explicitly enabled by the user");
                    }

                    service.DesiredLevel = ServiceDesiredLevel.Required;
                }

                if (this.m_DisabledServices.Any(x => x == service.ServiceName || x == service.FullName))
                {
                    if (this.m_ShowDebugInformation)
                    {
                        RedirectableConsole.WriteLine(service.FullName + " set to Disabled, because it is explicitly disabled by the user");
                    }

                    service.DesiredLevel = ServiceDesiredLevel.Disabled;
                }
            }
        }
        private void EnableReferencedProjects(List <XmlDocument> definitions, List <Service> services)
        {
            if (this.m_ShowDebugInformation)
            {
                RedirectableConsole.WriteLine("Enabling services in explicitly referenced projects:");
            }

            var lookup = services.ToDictionarySafe(
                k => k.FullName,
                v => v,
                (dict, d) => RedirectableConsole.WriteLine("WARNING: There is more than one service with the full name " + d.FullName));

            var references = from definition in definitions
                             where definition.DocumentElement.Name == "Project"
                             select
                             definition.DocumentElement.ChildNodes.OfType <XmlElement>().FirstOrDefault(x => x.Name == "References")
                             into referencesElement
                                 where referencesElement != null
                             select
                             referencesElement.ChildNodes.OfType <XmlElement>()
                             .Where(x => x.Name == "Reference")
                             .Select(x => x.GetAttribute("Include"))
                             .ToList()
                             into referencesList
                             from reference in referencesList.Where(lookup.ContainsKey)
                             select reference;

            foreach (var reference in references)
            {
                if (this.m_ShowDebugInformation)
                {
                    RedirectableConsole.WriteLine(lookup[reference].FullName + " set to Required, because there is an explicit reference to the project");
                }

                lookup[reference].DesiredLevel = ServiceDesiredLevel.Required;
            }
        }
        private void EnableRootProjects(List <Service> services)
        {
            if (this.m_ShowDebugInformation)
            {
                RedirectableConsole.WriteLine("Enabling project root services:");
            }

            foreach (var definition in this.m_RootDefinitions)
            {
                var defaultService =
                    services.FirstOrDefault(
                        x => x.ProjectName == definition.Name && string.IsNullOrEmpty(x.ServiceName));

                if (defaultService != null)
                {
                    if (this.m_ShowDebugInformation)
                    {
                        RedirectableConsole.WriteLine(defaultService.FullName + " set to Default, because it is a project root service");
                    }

                    defaultService.DesiredLevel = ServiceDesiredLevel.Default;
                }
            }
        }
        public IPackageMetadata ResolveSource(string workingDirectory, PackageRequestRef request)
        {
            var baseUri = new Uri(request.Uri);

            var     apiUri  = new Uri(baseUri.ToString().TrimEnd('/') + "/api");
            dynamic apiData = null;

            var performOnlineLookup = true;

            if (!request.ForceUpgrade && request.IsStaticReference)
            {
                performOnlineLookup = false;
                if (_packageRequestCache.IsCached(request.Uri))
                {
                    try
                    {
                        apiData = _packageRequestCache.GetCachedJsonObject(request.Uri);
                    }
                    catch (ExecEnvironment.SelfInvokeExitException)
                    {
                        throw;
                    }
                    catch
                    {
                        performOnlineLookup = true;
                    }
                }
                else
                {
                    performOnlineLookup = true;
                }
            }

            if (performOnlineLookup)
            {
                try
                {
                    string jsonString;
                    apiData = this.GetJSON(apiUri, out jsonString);
                    if (apiData.has_error)
                    {
                        throw new InvalidOperationException((string)apiData.error);
                    }
                    try
                    {
                        _packageRequestCache.StoreCachedData(request.Uri, jsonString);
                    }
                    catch (IOException)
                    {
                        RedirectableConsole.WriteLine("WARNING: Unable to save cached result of request.");
                    }
                }
                catch (Exception)
                {
                    // Attempt to retrieve it from the lookup cache.
                    if (_packageRequestCache.IsCached(request.Uri))
                    {
                        var shouldThrow = false;
                        try
                        {
                            apiData = _packageRequestCache.GetCachedJsonObject(request.Uri);
                        }
                        catch (ExecEnvironment.SelfInvokeExitException)
                        {
                            throw;
                        }
                        catch
                        {
                            shouldThrow = true;
                        }
                        if (shouldThrow)
                        {
                            throw;
                        }
                    }
                    else
                    {
                        throw;
                    }
                }
            }

            if (apiData == null)
            {
                throw new InvalidOperationException("apiData is null");
            }

            var sourceUri = (string)apiData.result.package.gitUrl;
            var type      = (string)apiData.result.package.type;

            if (!string.IsNullOrWhiteSpace(sourceUri))
            {
                try
                {
                    new Uri(sourceUri);
                }
                catch (ExecEnvironment.SelfInvokeExitException)
                {
                    throw;
                }
                catch
                {
                    throw new InvalidOperationException(
                              "Received invalid Git URL when loading package from " + apiUri);
                }
            }
            else
            {
                RedirectableConsole.WriteLine("WARNING: This package does not have a source repository set.");
            }

            var downloadMap    = new Dictionary <string, string>();
            var archiveTypeMap = new Dictionary <string, string>();
            var resolvedHash   = new Dictionary <string, string>();

            foreach (var ver in apiData.result.versions)
            {
                if (ver.platformName != request.Platform)
                {
                    continue;
                }
                if (!downloadMap.ContainsKey(ver.versionName))
                {
                    downloadMap.Add(ver.versionName, ver.downloadUrl);
                    archiveTypeMap.Add(ver.versionName, ver.archiveType);
                    resolvedHash.Add(ver.versionName, ver.versionName);
                }
            }
            foreach (var branch in apiData.result.branches)
            {
                if (!downloadMap.ContainsKey(branch.versionName))
                {
                    continue;
                }
                if (!downloadMap.ContainsKey(branch.branchName))
                {
                    downloadMap.Add(branch.branchName, downloadMap[branch.versionName]);
                    archiveTypeMap.Add(branch.branchName, archiveTypeMap[branch.versionName]);
                    resolvedHash.Add(branch.branchName, branch.versionName);
                }
            }

            // Resolve Git reference to Git commit hash.
            var gitCommit = resolvedHash.ContainsKey(request.GitRef) ? resolvedHash[request.GitRef] : request.GitRef;

            if (string.IsNullOrWhiteSpace(sourceUri))
            {
                // Normalize source URI value.
                sourceUri = null;
            }

            string fileUri, archiveType;

            if (!downloadMap.ContainsKey(gitCommit))
            {
                if (string.IsNullOrWhiteSpace(sourceUri))
                {
                    throw new InvalidOperationException("Unable to resolve binary package for version \"" +
                                                        request.GitRef + "\" and platform \"" + request.Platform +
                                                        "\" and this package does not have a source repository");
                }
                else
                {
                    RedirectableConsole.WriteLine("Unable to resolve binary package for version \"" + request.GitRef +
                                                  "\" and platform \"" + request.Platform + "\", falling back to source version");
                    fileUri     = null;
                    archiveType = null;
                }
            }
            else
            {
                fileUri     = downloadMap[gitCommit];
                archiveType = archiveTypeMap[gitCommit];
            }

            return(new ProtobuildPackageMetadata(
                       request.Uri,
                       type,
                       sourceUri,
                       request.Platform,
                       gitCommit,
                       archiveType,
                       fileUri,
                       (workingDirectoryAlt, metadata, folder, name, upgrade, source) =>
            {
                if (source == true)
                {
                    _sourcePackageResolve.Resolve(workingDirectoryAlt, metadata, folder, name, upgrade);
                }
                else
                {
                    _binaryPackageResolve.Resolve(workingDirectoryAlt, metadata, folder, name, upgrade);
                }
            },
                       _binaryPackageResolve.GetProtobuildPackageBinary));
        }
        public override bool Execute()
        {
            if (string.Compare(Platform, "Web", StringComparison.InvariantCultureIgnoreCase) == 0)
            {
                // Trigger JSIL provider download if needed.
                string jsilDirectory, jsilCompilerFile;
                if (!m_JSILProvider.GetJSIL(out jsilDirectory, out jsilCompilerFile))
                {
                    return(false);
                }
            }

            var module = ModuleInfo.Load(Path.Combine(RootPath, "Build", "Module.xml"));

            LogMessage(
                "Starting generation of projects for " + Platform);

            var definitions    = module.GetDefinitionsRecursively(Platform).ToArray();
            var loadedProjects = new List <LoadedDefinitionInfo>();

            if (_hostPlatformDetector.DetectPlatform() == "Windows")
            {
                var concurrentProjects = new ConcurrentBag <LoadedDefinitionInfo>();

                Parallel.ForEach(definitions, definition =>
                {
                    LogMessage("Loading: " + definition.Name);
                    concurrentProjects.Add(
                        m_ProjectLoader.Load(
                            Platform,
                            module,
                            definition));
                });

                // Do this so we maintain order with the original definitions list.
                foreach (var definition in definitions)
                {
                    var loadedProject = concurrentProjects.FirstOrDefault(x => x.Definition == definition);
                    if (loadedProject != null)
                    {
                        loadedProjects.Add(loadedProject);
                    }
                }
            }
            else
            {
                foreach (var definition in definitions)
                {
                    LogMessage("Loading: " + definition.Name);
                    loadedProjects.Add(
                        m_ProjectLoader.Load(
                            Platform,
                            module,
                            definition));
                }
            }

            var                  serviceManager = new ServiceManager(Platform);
            List <Service>       services;
            TemporaryServiceSpec serviceSpecPath;

            if (ServiceSpecPath == null)
            {
                serviceManager.SetRootDefinitions(module.GetDefinitions());

                if (EnableServices == null)
                {
                    EnableServices = new string[0];
                }

                if (DisableServices == null)
                {
                    DisableServices = new string[0];
                }

                foreach (var service in EnableServices)
                {
                    serviceManager.EnableService(service);
                }

                foreach (var service in DisableServices)
                {
                    serviceManager.DisableService(service);
                }

                if (DebugServiceResolution)
                {
                    serviceManager.EnableDebugInformation();
                }

                try
                {
                    services = serviceManager.CalculateDependencyGraph(loadedProjects.Select(x => x.Project).ToList());
                }
                catch (InvalidOperationException ex)
                {
                    RedirectableConsole.WriteLine("Error during service resolution: " + ex.Message);
                    return(false);
                }

                serviceSpecPath = serviceManager.SaveServiceSpec(services);

                foreach (var service in services)
                {
                    if (service.ServiceName != null)
                    {
                        LogMessage("Enabled service: " + service.FullName);
                    }
                }
            }
            else
            {
                services        = serviceManager.LoadServiceSpec(ServiceSpecPath);
                serviceSpecPath = new TemporaryServiceSpec(ServiceSpecPath, true);
            }

            using (serviceSpecPath)
            {
                var submodulesToProcess = new List <ModuleInfo>();
                var allSubmodules       = module.GetSubmodules(Platform);

                if (_hostPlatformDetector.DetectPlatform() == "Windows")
                {
                    var concurrentSubmodulesToProcess = new ConcurrentBag <ModuleInfo>();

                    Parallel.ForEach(allSubmodules, submodule =>
                    {
                        if (_featureManager.IsFeatureEnabledInSubmodule(module, submodule,
                                                                        Feature.OptimizationSkipInvocationOnNoStandardProjects))
                        {
                            if (submodule.GetDefinitionsRecursively(Platform).All(x => !x.IsStandardProject))
                            {
                                // Do not invoke this submodule.
                                LogMessage(
                                    "Skipping submodule generation for " + submodule.Name +
                                    " (there are no projects to generate)");
                            }
                            else
                            {
                                concurrentSubmodulesToProcess.Add(submodule);
                            }
                        }
                    });

                    // Do this so we maintain order with the original GetSubmodules list.
                    foreach (var submodule in allSubmodules)
                    {
                        if (concurrentSubmodulesToProcess.Contains(submodule))
                        {
                            submodulesToProcess.Add(submodule);
                        }
                    }
                }
                else
                {
                    foreach (var submodule in module.GetSubmodules(Platform))
                    {
                        if (_featureManager.IsFeatureEnabledInSubmodule(module, submodule,
                                                                        Feature.OptimizationSkipInvocationOnNoStandardProjects))
                        {
                            if (submodule.GetDefinitionsRecursively(Platform).All(x => !x.IsStandardProject))
                            {
                                // Do not invoke this submodule.
                                LogMessage(
                                    "Skipping submodule generation for " + submodule.Name +
                                    " (there are no projects to generate)");
                            }
                            else
                            {
                                submodulesToProcess.Add(submodule);
                            }
                        }
                    }
                }

                // Run Protobuild in batch mode in each of the submodules
                // where it is present.
                foreach (var submodule in submodulesToProcess)
                {
                    LogMessage(
                        "Invoking submodule generation for " + submodule.Name);
                    var noResolve = _featureManager.IsFeatureEnabledInSubmodule(module, submodule,
                                                                                Feature.PackageManagementNoResolve)
                        ? " -no-resolve"
                        : string.Empty;
                    var noHostPlatform = DisableHostPlatformGeneration &&
                                         _featureManager.IsFeatureEnabledInSubmodule(module, submodule,
                                                                                     Feature.NoHostGenerate)
                        ? " -no-host-generate"
                        : string.Empty;
                    _moduleExecution.RunProtobuild(
                        submodule,
                        _featureManager.GetFeatureArgumentToPassToSubmodule(module, submodule) +
                        "-generate " + Platform +
                        " -spec " + serviceSpecPath +
                        " " + m_PackageRedirector.GetRedirectionArguments() +
                        noResolve + noHostPlatform);
                    LogMessage(
                        "Finished submodule generation for " + submodule.Name);
                }

                var repositoryPaths = new List <string>();

                if (_hostPlatformDetector.DetectPlatform() == "Windows")
                {
                    var concurrentRepositoryPaths = new ConcurrentBag <string>();

                    Parallel.ForEach(definitions.Where(x => x.ModulePath == module.Path), definition =>
                    {
                        if (definition.PostBuildHook && RequiresHostPlatform != null)
                        {
                            // We require the host platform projects at this point.
                            RequiresHostPlatform();
                        }

                        string repositoryPath;
                        var definitionCopy = definition;
                        m_ProjectGenerator.Generate(
                            definition,
                            loadedProjects,
                            WorkingDirectory,
                            RootPath,
                            definition.Name,
                            Platform,
                            services,
                            out repositoryPath,
                            () => LogMessage("Generating: " + definitionCopy.Name),
                            DebugProjectGeneration);

                        // Only add repository paths if they should be generated.
                        if (module.GenerateNuGetRepositories && !string.IsNullOrEmpty(repositoryPath))
                        {
                            concurrentRepositoryPaths.Add(repositoryPath);
                        }
                    });

                    repositoryPaths = concurrentRepositoryPaths.ToList();
                }
                else
                {
                    foreach (var definition in definitions.Where(x => x.ModulePath == module.Path))
                    {
                        if (definition.PostBuildHook && RequiresHostPlatform != null)
                        {
                            // We require the host platform projects at this point.
                            RequiresHostPlatform();
                        }

                        string repositoryPath;
                        var    definitionCopy = definition;
                        m_ProjectGenerator.Generate(
                            definition,
                            loadedProjects,
                            WorkingDirectory,
                            RootPath,
                            definition.Name,
                            Platform,
                            services,
                            out repositoryPath,
                            () => LogMessage("Generating: " + definitionCopy.Name),
                            DebugProjectGeneration);

                        // Only add repository paths if they should be generated.
                        if (module.GenerateNuGetRepositories && !string.IsNullOrEmpty(repositoryPath))
                        {
                            repositoryPaths.Add(repositoryPath);
                        }
                    }
                }

                var solution = Path.Combine(
                    RootPath,
                    ModuleName + "." + Platform + ".sln");
                LogMessage("Generating: (solution)");
                m_SolutionGenerator.Generate(
                    WorkingDirectory,
                    module,
                    loadedProjects.Select(x => x.Project).ToList(),
                    Platform,
                    solution,
                    services,
                    repositoryPaths,
                    DebugProjectGeneration);

                // Only save the specification cache if we allow synchronisation
                if (module.DisableSynchronisation == null || !module.DisableSynchronisation.Value)
                {
                    var serviceCache = Path.Combine(RootPath, ModuleName + "." + Platform + ".speccache");
                    LogMessage("Saving service specification");
                    File.Copy(serviceSpecPath.Path, serviceCache, true);
                }

                LogMessage(
                    "Generation complete.");
            }

            return(true);
        }
Beispiel #7
0
        public IPackageMetadata ResolveSource(string workingDirectory, PackageRequestRef request)
        {
            var components = request.Uri.Split(new[] { '|' }, 2);

            var repository  = NormalizeScheme(components[0]);
            var packageName = components[1];
            var version     = request.GitRef;

            var semVerRegex  = new Regex("^[0-9]\\.[0-9]\\.[0-9](\\-.*)?$");
            var gitHashRegex = new Regex("^[0-9a-fA-F]{40}$");

            var shouldQuerySourceRepository = false;

            if (semVerRegex.IsMatch(version))
            {
                // This is a semantic version; leave as-is.
            }
            else if (gitHashRegex.IsMatch(version))
            {
                // This is a Git hash, convert it to NuGet's semantic version.
                version = NuGetVersionHelper.CreateNuGetPackageVersion(version, request.Platform);
            }
            else
            {
                // This is a branch, or other kind of source reference.  We need
                // to query the source repository to resolve it to a Git hash.
                shouldQuerySourceRepository = true;
            }

            string serviceJson;
            var    serviceIndex = _packageRequestCache.TryGetOptionallyCachedJsonObject(
                repository,
                !request.ForceUpgrade,
                out serviceJson,
                id => GetData(new Uri(id)));

            string registrationsBaseUrl = null;

            foreach (var entry in serviceIndex.resources)
            {
                var entryType = (string)entry["@type"];
                if (entryType == "RegistrationsBaseUrl")
                {
                    registrationsBaseUrl = (string)entry["@id"];
                }
            }

            if (registrationsBaseUrl == null)
            {
                throw new InvalidOperationException("Unable to locate RegistrationsBaseUrl service.");
            }

            var packageMetadataUrl =
                $"{registrationsBaseUrl.TrimEnd(new[] {'/'})}/{packageName.ToLowerInvariant()}/index.json";

            string packageMetadataJson;
            var    packageMetadata = _packageRequestCache.TryGetOptionallyCachedJsonObject(
                packageMetadataUrl,
                !request.ForceUpgrade && request.IsStaticReference,
                out packageMetadataJson,
                id => GetData(new Uri(id)));

            string latestPackageVersion = null;

            foreach (var item in packageMetadata.items)
            {
                if (latestPackageVersion == null || string.CompareOrdinal((string)item.upper, latestPackageVersion) > 0)
                {
                    latestPackageVersion = item.upper;
                }
            }

            var packagesByVersionLock = new object();
            var packagesByVersion     = new Dictionary <string, dynamic>();

            if (_hostPlatformDetector.DetectPlatform() == "Windows")
            {
                // Do this in parallel as we may need to make multiple HTTPS requests.
                Parallel.ForEach((IEnumerable <object>)packageMetadata.items,
                                 item => PopulatePackagesByVersion(packagesByVersionLock, packagesByVersion, (dynamic)item, request));
            }
            else
            {
                // Parallelisation is not safe on this platform, do it sequentually.
                foreach (var item in packageMetadata.items)
                {
                    PopulatePackagesByVersion(packagesByVersionLock, packagesByVersion, item, request);
                }
            }

            string packageType = PackageManager.PACKAGE_TYPE_LIBRARY;

            string sourceCodeUrl = null;
            string commitHashForSourceResolve = null;

            if (shouldQuerySourceRepository)
            {
                var lookupVersion = latestPackageVersion;
                if (!packagesByVersion.ContainsKey(lookupVersion) && packagesByVersion.ContainsKey(lookupVersion + "+git.unspecified"))
                {
                    lookupVersion += "+git.unspecified";
                }
                sourceCodeUrl = ExtractSourceRepository(packagesByVersion[lookupVersion]);
                packageType   = ExtractPackageType(packagesByVersion[lookupVersion]);

                if (!string.IsNullOrWhiteSpace(sourceCodeUrl))
                {
                    if (sourceCodeUrl.StartsWith("git="))
                    {
                        sourceCodeUrl = sourceCodeUrl.Substring("git=".Length);

                        var performGitLsRemote = true;
                        if (sourceCodeUrl.StartsWith("https://github.com/"))
                        {
                            try
                            {
                                // This is a GitHub repository.  During the installation of Protobuild Manager (hosted on GitHub), we need
                                // to resolve the latest version on NuGet, but we can't do this because Git may not be in the PATH or may
                                // not be available.  We still want developers to be able to install Protobuild Manager without Git on
                                // their PATH (as they may have dedicated shells to use it), so we attempt to use the GitHub API to resolve
                                // the commit hash first.
                                string gitHubJsonInfo;
                                var    gitHubComponents =
                                    sourceCodeUrl.Substring("https://github.com/".Length).Split('/');
                                var gitHubOwner  = gitHubComponents[0];
                                var gitHubRepo   = gitHubComponents[1];
                                var gitHubApiUrl = "https://api.github.com/repos/" + gitHubOwner +
                                                   "/" +
                                                   gitHubRepo + "/branches/" + version;
                                var gitHubJson = _packageRequestCache.TryGetOptionallyCachedJsonObject(
                                    gitHubApiUrl,
                                    !request.ForceUpgrade && request.IsStaticReference,
                                    out packageMetadataJson,
                                    id =>
                                {
                                    using (var client = new RetryableWebClient())
                                    {
                                        client.SilentOnError = true;
                                        client.SetHeader("User-Agent", "Protobuild NuGet Lookup/v1.0");
                                        client.SetHeader("Accept", "application/vnd.github.v3+json");

                                        return(client.DownloadString(gitHubApiUrl));
                                    }
                                });
                                var commitHash = gitHubJson.commit.sha;

                                if (!string.IsNullOrWhiteSpace(commitHash))
                                {
                                    // This is a match and we've found our Git hash to use.
                                    version =
                                        NuGetVersionHelper.CreateNuGetPackageVersion(commitHash.Trim(),
                                                                                     request.Platform);
                                    commitHashForSourceResolve = commitHash.Trim();
                                    performGitLsRemote         = false;
                                }
                            }
                            catch (Exception)
                            {
                                Console.WriteLine("NOTICE: Unable to lookup version information via GitHub API; falling back to 'git ls-remote'");
                            }
                        }

                        if (performGitLsRemote)
                        {
                            var heads = _packageRequestCache.TryGetOptionallyCachedData(
                                "git:" + sourceCodeUrl,
                                !request.ForceUpgrade && request.IsStaticReference,
                                id => GitUtils.RunGitAndCapture(
                                    workingDirectory,
                                    null,
                                    "ls-remote --heads " + new Uri(sourceCodeUrl)));

                            var lines = heads.Split(new string[] { "\r\n", "\n", "\r" },
                                                    StringSplitOptions.RemoveEmptyEntries);

                            foreach (var line in lines)
                            {
                                var sourceEntryComponents = line.Split('\t');
                                if (sourceEntryComponents.Length >= 2)
                                {
                                    var branchName = sourceEntryComponents[1].Trim();

                                    if (branchName.StartsWith("refs/heads/"))
                                    {
                                        branchName = branchName.Substring("refs/heads/".Length);
                                    }
                                    else
                                    {
                                        continue;
                                    }

                                    if (string.Equals(version, branchName, StringComparison.InvariantCulture))
                                    {
                                        // This is a match and we've found our Git hash to use.
                                        version =
                                            NuGetVersionHelper.CreateNuGetPackageVersion(
                                                sourceEntryComponents[0].Trim(),
                                                request.Platform);
                                        commitHashForSourceResolve = sourceEntryComponents[0].Trim();
                                        break;
                                    }
                                }
                            }
                        }
                    }
                    else
                    {
                        throw new InvalidOperationException("Unknown source code repository type '" + sourceCodeUrl + "'");
                    }

                    // If we fall out of this loop, we'll hit the next if statement and most likely fail.
                }
            }

            string binaryUri    = null;
            string binaryFormat = null;

            if (!packagesByVersion.ContainsKey(version))
            {
                if (string.IsNullOrWhiteSpace(sourceCodeUrl))
                {
                    throw new InvalidOperationException(
                              "Unable to resolve binary package for version \"" +
                              version + "\" and platform \"" + request.Platform +
                              "\" and this package does not have a source repository");
                }
                else
                {
                    RedirectableConsole.WriteLine("Unable to resolve binary package for version \"" + version +
                                                  "\" and platform \"" + request.Platform + "\", falling back to source version");
                }
            }
            else
            {
                sourceCodeUrl = ExtractSourceRepository(packagesByVersion[version]);
                packageType   = ExtractPackageType(packagesByVersion[version]);

                if (!string.IsNullOrWhiteSpace(sourceCodeUrl))
                {
                    if (sourceCodeUrl.StartsWith("git="))
                    {
                        sourceCodeUrl = sourceCodeUrl.Substring("git=".Length);
                    }
                    else
                    {
                        throw new InvalidOperationException("Unknown source code repository type '" + sourceCodeUrl + "'");
                    }
                }

                if (commitHashForSourceResolve == null)
                {
                    commitHashForSourceResolve = ExtractCommitHash(packagesByVersion[version]);
                }

                // packageContent may not be under catalogEntry; our best guess is that NuGet
                // moved it from the root to catalogEntry at some point in the past, but not
                // all of the registrations are updated with it under catalogEntry, e.g. RestSharp
                try
                {
                    binaryUri = packagesByVersion[version].catalogEntry.packageContent;
                }
                catch
                {
                    binaryUri = packagesByVersion[version].packageContent;
                }

                binaryFormat = PackageManager.ARCHIVE_FORMAT_NUGET_ZIP;
            }

            return(new NuGet3PackageMetadata(
                       repository,
                       packageName,
                       packageType,
                       sourceCodeUrl,
                       request.Platform,
                       version,
                       binaryFormat,
                       binaryUri,
                       commitHashForSourceResolve,
                       (workingDirectoryAlt, metadata, folder, name, upgrade, source) =>
            {
                if (source == true)
                {
                    _sourcePackageResolve.Resolve(workingDirectoryAlt, metadata, folder, name, upgrade);
                }
                else
                {
                    _binaryPackageResolve.Resolve(workingDirectoryAlt, metadata, folder, name, upgrade);
                }
            }));
        }
        private bool PerformResolutionPass(List <Service> services)
        {
            var lookup = services.ToDictionarySafe(
                k => k.FullName,
                v => v,
                (dict, d) => { /* We have already warned about this previously */ });
            var modified = false;

            foreach (var service in services)
            {
                if (service.DesiredLevel == ServiceDesiredLevel.Disabled || service.DesiredLevel == ServiceDesiredLevel.Unused)
                {
                    continue;
                }

                foreach (var require in service.Requires)
                {
                    if (!lookup.ContainsKey(require))
                    {
                        throw new InvalidOperationException(
                                  service.FullName + " requires " + require + ", but it does not exist.");
                    }

                    if (lookup[require].DesiredLevel == ServiceDesiredLevel.Disabled)
                    {
                        if (service.DesiredLevel == ServiceDesiredLevel.Required || service.ServiceName == null)
                        {
                            throw new InvalidOperationException(
                                      service.FullName + " requires " + require + ", but you have explicitly requested it be disabled.");
                        }
                        else if (service.DesiredLevel == ServiceDesiredLevel.Recommended ||
                                 service.DesiredLevel == ServiceDesiredLevel.Default)
                        {
                            if (this.m_ShowDebugInformation)
                            {
                                RedirectableConsole.WriteLine(service.FullName + " set to Disabled, because it's dependency " + lookup[require].FullName + " is set to Disabled");
                            }

                            service.DesiredLevel = ServiceDesiredLevel.Disabled;
                            modified             = true;
                        }
                    }
                    else if (service.DesiredLevel == ServiceDesiredLevel.Required)
                    {
                        if (lookup[require].DesiredLevel != ServiceDesiredLevel.Required)
                        {
                            if (this.m_ShowDebugInformation)
                            {
                                RedirectableConsole.WriteLine(lookup[require].FullName + " set to Required, because it's dependency " + service.FullName + " is set to Required");
                            }

                            lookup[require].DesiredLevel = ServiceDesiredLevel.Required;
                            modified = true;
                        }
                    }
                    else if (service.DesiredLevel == ServiceDesiredLevel.Recommended)
                    {
                        if (lookup[require].DesiredLevel != ServiceDesiredLevel.Required &&
                            lookup[require].DesiredLevel != ServiceDesiredLevel.Recommended)
                        {
                            if (this.m_ShowDebugInformation)
                            {
                                RedirectableConsole.WriteLine(lookup[require].FullName + " set to Recommended, because it's dependency " + service.FullName + " is set to Recommended");
                            }

                            lookup[require].DesiredLevel = ServiceDesiredLevel.Recommended;
                            modified = true;
                        }
                    }
                    else if (service.DesiredLevel == ServiceDesiredLevel.Default)
                    {
                        if (lookup[require].DesiredLevel != ServiceDesiredLevel.Required &&
                            lookup[require].DesiredLevel != ServiceDesiredLevel.Recommended &&
                            lookup[require].DesiredLevel != ServiceDesiredLevel.Default)
                        {
                            if (this.m_ShowDebugInformation)
                            {
                                RedirectableConsole.WriteLine(lookup[require].FullName + " set to Default, because it's dependency " + service.FullName + " is set to Default");
                            }

                            lookup[require].DesiredLevel = ServiceDesiredLevel.Default;
                            modified = true;
                        }
                    }
                }

                foreach (var recommend in service.Recommends)
                {
                    if (!lookup.ContainsKey(recommend))
                    {
                        throw new InvalidOperationException(
                                  service.FullName + " recommends " + recommend + ", but it does not exist.");
                    }

                    if (lookup[recommend].DesiredLevel != ServiceDesiredLevel.Disabled &&
                        lookup[recommend].DesiredLevel != ServiceDesiredLevel.Recommended &&
                        lookup[recommend].DesiredLevel != ServiceDesiredLevel.Required)
                    {
                        if (this.m_ShowDebugInformation)
                        {
                            RedirectableConsole.WriteLine(lookup[recommend].FullName + " set to Recommended, because it's dependency " + service.FullName + " recommends it");
                        }

                        lookup[recommend].DesiredLevel = ServiceDesiredLevel.Recommended;
                        modified = true;
                    }
                }

                foreach (var conflict in service.Conflicts)
                {
                    if (!lookup.ContainsKey(conflict))
                    {
                        // The service that the conflict is declared with may
                        // not exist because it's not available on the current
                        // platform, in which case it can't be enabled anyway.
                        continue;
                    }

                    if (lookup[conflict].DesiredLevel == ServiceDesiredLevel.Required &&
                        service.DesiredLevel == ServiceDesiredLevel.Required)
                    {
                        throw new InvalidOperationException(
                                  service.FullName + " conflicts with " + lookup[conflict].FullName + ", but both are enabled (and required).");
                    }

                    if (lookup[conflict].DesiredLevel == ServiceDesiredLevel.Recommended)
                    {
                        if (this.m_ShowDebugInformation)
                        {
                            RedirectableConsole.WriteLine(lookup[conflict].FullName + " set to Disabled, because " + service.FullName + " conflicts with it");
                        }

                        // The service this conflicts with is only recommended, so we can
                        // safely disable it.
                        lookup[conflict].DesiredLevel = ServiceDesiredLevel.Disabled;
                        modified = true;
                    }
                }
            }

            return(modified);
        }