Ejemplo n.º 1
0
        public override bool Execute()
        {
            var packageArtifacts = Artifacts.Select(ArtifactInfo.Parse)
                                   .OfType <ArtifactInfo.Package>()
                                   .Where(p => !p.IsSymbolsArtifact);

            var factory = new SolutionInfoFactory(Log, BuildEngine5);
            var props   = MSBuildListSplitter.GetNamedProperties(Properties);

            Log.LogMessage(MessageImportance.High, $"Beginning cross-repo analysis on {Solutions.Length} solutions. Hang tight...");

            if (!props.TryGetValue("Configuration", out var defaultConfig))
            {
                defaultConfig = "Debug";
            }

            var solutions = factory.Create(Solutions, props, defaultConfig, _cts.Token);

            Log.LogMessage($"Found {solutions.Count} and {solutions.Sum(p => p.Projects.Count)} projects");

            if (_cts.IsCancellationRequested)
            {
                return(false);
            }

            EnsureConsistentGraph(packageArtifacts, solutions);
            RepositoryBuildOrder = GetRepositoryBuildOrder(packageArtifacts, solutions.Where(s => s.ShouldBuild));

            return(!Log.HasLoggedErrors);
        }
Ejemplo n.º 2
0
        public static IEnumerable <string> GetProjects(ITaskItem projectOrSolution, IDictionary <string, string> solutionProperties)
        {
            var projectFilePath = projectOrSolution.ItemSpec.Replace('\\', '/');

            if (Path.GetExtension(projectFilePath).Equals(".sln", StringComparison.OrdinalIgnoreCase))
            {
                // prefer the AdditionalProperties metadata as this is what the MSBuild task will use when building solutions
                var props = MSBuildListSplitter.GetNamedProperties(projectOrSolution.GetMetadata("AdditionalProperties"));
                props.TryGetValue("Configuration", out var config);

                if (config == null)
                {
                    solutionProperties.TryGetValue("Configuration", out config);
                }

                var sln = Create(projectFilePath, config);

                foreach (var project in sln.Projects)
                {
                    yield return(project);
                }
            }
            else
            {
                yield return(Path.GetFullPath(projectFilePath));
            }
        }
Ejemplo n.º 3
0
        public override bool Execute()
        {
            var packageArtifacts = Artifacts.Select(ArtifactInfo.Parse)
                                   .OfType <ArtifactInfo.Package>()
                                   .Where(p => !p.IsSymbolsArtifact)
                                   .ToDictionary(p => p.PackageInfo.Id, p => p, StringComparer.OrdinalIgnoreCase);

            var factory = new SolutionInfoFactory(Log, BuildEngine5);
            var props   = MSBuildListSplitter.GetNamedProperties(Properties);

            Log.LogMessage(MessageImportance.High, $"Beginning cross-repo analysis on {Solutions.Length} solutions. Hang tight...");

            if (!props.TryGetValue("Configuration", out var defaultConfig))
            {
                defaultConfig = "Debug";
            }

            var solutions = factory.Create(Solutions, props, defaultConfig, _cts.Token).OrderBy(f => f.Directory).ToList();

            Log.LogMessage($"Found {solutions.Count} and {solutions.Sum(p => p.Projects.Count)} projects");

            if (_cts.IsCancellationRequested)
            {
                return(false);
            }

            return(GenerateGraph(packageArtifacts, solutions));
        }
Ejemplo n.º 4
0
        public override bool Execute()
        {
            if (Projects == null || Projects.Length == 0)
            {
                Log.LogMessage(MessageImportance.Low, "No projects or solutions were found. Skipping PackageReference validation.");
                return(true);
            }

            if (!File.Exists(DependenciesFile))
            {
                Log.LogKoreBuildError(KoreBuildErrors.DependenciesFileDoesNotExist, $"Expected the dependencies file to exist at {DependenciesFile}");
                return(false);
            }

            if (!DependencyVersionsFile.TryLoad(DependenciesFile, out var depsFile))
            {
                Log.LogError($"Could not load the dependencies file from {DependenciesFile}");
                return(false);
            }

            if (!depsFile.HasVersionsPropertyGroup())
            {
                Log.LogKoreBuildWarning(KoreBuildErrors.PackageRefPropertyGroupNotFound, $"No PropertyGroup with Label=\"{DependencyVersionsFile.PackageVersionsLabel}\" could be found in {DependenciesFile}");
            }

            foreach (var proj in Projects)
            {
                var ext = Path.GetExtension(proj.ItemSpec);
                if (ext == ".sln")
                {
                    var solutionProps = MSBuildListSplitter.GetNamedProperties(Properties);
                    var projectFiles  = Projects.SelectMany(p => SolutionInfoFactory.GetProjects(p, solutionProps)).Distinct();
                    foreach (var project in projectFiles)
                    {
                        VerifyPackageReferences(project, depsFile.VersionVariables);
                    }
                }
                else
                {
                    VerifyPackageReferences(proj.ItemSpec, depsFile.VersionVariables);
                }
            }

            return(!Log.HasLoggedErrors);
        }
Ejemplo n.º 5
0
        public override bool Execute()
        {
            var packageArtifacts = Artifacts.Select(ArtifactInfo.Parse)
                                   .OfType <ArtifactInfo.Package>()
                                   .Where(p => !p.IsSymbolsArtifact);

            var factory = new SolutionInfoFactory(Log, BuildEngine5);
            var props   = MSBuildListSplitter.GetNamedProperties(Properties);

            Log.LogMessage(MessageImportance.High, $"Beginning cross-repo analysis on {Solutions.Length} solutions. Hang tight...");

            if (!props.TryGetValue("Configuration", out var defaultConfig))
            {
                defaultConfig = "Debug";
            }

            var solutions = factory.Create(Solutions, props, defaultConfig, _cts.Token);

            Log.LogMessage($"Found {solutions.Count} and {solutions.Sum(p => p.Projects.Count)} projects");

            var policies = new Dictionary <string, PatchPolicy>();

            foreach (var repo in Repositories)
            {
                policies.Add(repo.ItemSpec, Enum.Parse <PatchPolicy>(repo.GetMetadata("PatchPolicy")));
            }

            foreach (var solution in solutions)
            {
                var repoName = Path.GetFileName(solution.Directory);
                solution.PatchPolicy = policies[repoName];
            }

            if (_cts.IsCancellationRequested)
            {
                return(false);
            }

            EnsureConsistentGraph(packageArtifacts, solutions);
            RepositoryBuildOrder = GetRepositoryBuildOrder(packageArtifacts, solutions.Where(s => s.ShouldBuild));

            return(!Log.HasLoggedErrors);
        }
        public IReadOnlyList <ProjectInfo> CreateMany(ITaskItem[] projectItems, string[] properties, bool policyDesignBuild, CancellationToken token)
        {
            if (projectItems == null)
            {
                return(Array.Empty <ProjectInfo>());
            }

            var cts = new CancellationTokenSource();

            token.Register(() => cts.Cancel());
            var solutionProps = MSBuildListSplitter.GetNamedProperties(properties);
            var projectFiles  = projectItems.SelectMany(p => SolutionInfoFactory.GetProjects(p, solutionProps)).Distinct();
            var projects      = new ConcurrentBag <ProjectInfo>();
            var stop          = Stopwatch.StartNew();

            Parallel.ForEach(projectFiles, projectFile =>
            {
                if (cts.Token.IsCancellationRequested)
                {
                    return;
                }

                try
                {
                    projects.Add(Create(projectFile, policyDesignBuild));
                }
                catch (Exception ex)
                {
                    _logger.LogErrorFromException(ex);
                    cts.Cancel();
                }
            });

            stop.Stop();
            _logger.LogMessage(MessageImportance.Low, $"Finished design-time build in {stop.ElapsedMilliseconds}ms");
            return(projects.ToArray());
        }
Ejemplo n.º 7
0
        private IReadOnlyDictionary <string, PackageReferenceInfo> GetDependencies(ProjectInstance project)
        {
            var references = new Dictionary <string, PackageReferenceInfo>(StringComparer.OrdinalIgnoreCase);

            foreach (var item in  project.GetItems("PackageReference"))
            {
                bool.TryParse(item.GetMetadataValue("IsImplicitlyDefined"), out var isImplicit);
                var noWarn = item.GetMetadataValue("NoWarn");
                IReadOnlyList <string> noWarnItems = string.IsNullOrEmpty(noWarn)
                    ? Array.Empty <string>()
                    : MSBuildListSplitter.SplitItemList(noWarn).ToArray();

                var info = new PackageReferenceInfo(item.EvaluatedInclude, item.GetMetadataValue("Version"), isImplicit, noWarnItems);

                if (references.ContainsKey(info.Id))
                {
                    _logger.LogKoreBuildWarning(project.ProjectFileLocation.File, KoreBuildErrors.DuplicatePackageReference, $"Found a duplicate PackageReference for {info.Id}. Restore results may be unpredictable.");
                }

                references[info.Id] = info;
            }

            return(references);
        }
Ejemplo n.º 8
0
        public override bool Execute()
        {
            if (!File.Exists(NuspecPath))
            {
                Log.LogError("Nuspec does not exist: " + NuspecPath);
                return(false);
            }

            var packageBasePath = string.IsNullOrEmpty(BasePath)
                ? Path.GetDirectoryName(NuspecPath)
                : BasePath;

            if (!Directory.Exists(packageBasePath))
            {
                Log.LogError("Base path does not exist: " + packageBasePath);
                return(false);
            }

            if (!(string.IsNullOrEmpty(DestinationFolder) ^ string.IsNullOrEmpty(OutputPath)))
            {
                Log.LogError("Either DestinationFolder and OutputPath must be specified, but only not both.");
                return(false);
            }

            var properties = MSBuildListSplitter.GetNamedProperties(Properties);

            string PropertyProvider(string name)
            {
                if (properties.TryGetValue(name, out var value))
                {
                    return(value);
                }
                Log.LogError("Undefined property: " + name);
                return(null);
            }

            PackageBuilder packageBuilder;

            try
            {
                Log.LogMessage($"Loading nuspec {NuspecPath}");

                using (var file = File.OpenRead(NuspecPath))
                {
                    var manifest = Manifest.ReadFrom(file, PropertyProvider, validateSchema: false);
                    if (!manifest.HasFilesNode)
                    {
                        // Warn about this overly permissive default in nuspec.
                        Log.LogKoreBuildWarning(KoreBuildErrors.NuspecMissingFilesNode,
                                                "The nuspec file is missing the <files> nodes. This causes all files in NuspecBase to be included in the package. " +
                                                @"Add an empty `<files />` node to prevent this behavior. Add `<files> <file src=""**\*\"" target=""\"" /> </files>` to the nuspec to suppress this warning.");
                    }
                }

                packageBuilder = new PackageBuilder(NuspecPath, packageBasePath, PropertyProvider, IncludeEmptyDirectories);
            }
            catch (InvalidDataException ex)
            {
                Log.LogKoreBuildError(NuspecPath, KoreBuildErrors.InvalidNuspecFile, ex.Message);
                return(false);
            }

            if (Dependencies != null)
            {
                AddDependencies(packageBuilder);
            }

            if (PackageFiles != null)
            {
                AddFiles(packageBuilder);
            }

            if (Log.HasLoggedErrors)
            {
                return(false);
            }

            var dest = !string.IsNullOrEmpty(OutputPath)
                ? OutputPath
                : Path.Combine(DestinationFolder, $"{packageBuilder.Id}.{packageBuilder.Version}.nupkg");

            // normalize path
            dest = Path.GetFullPath(dest);

            Directory.CreateDirectory(Path.GetDirectoryName(dest));

            if (!Overwrite && File.Exists(dest))
            {
                Log.LogError($"File path '{dest}' already exists. Set Overwrite=true to overwrite the destination nupkg file.");
                return(false);
            }

            if (packageBuilder.Files != null)
            {
                foreach (var file in packageBuilder.Files)
                {
                    if (file is PhysicalPackageFile p)
                    {
                        Log.LogMessage($"Packing {p.SourcePath} => {p.Path}");
                    }
                    else
                    {
                        Log.LogMessage($"Packing {file.Path}");
                    }
                }
            }

            using (var stream = File.Create(dest))
            {
                packageBuilder.Save(stream);
            }

            Log.LogMessage(MessageImportance.High, $"Created package {dest}");
            Packages = new[] { new TaskItem(dest) };

            return(true);
        }
Ejemplo n.º 9
0
        public override bool Execute()
        {
            var packageArtifacts = Artifacts.Select(ArtifactInfo.Parse)
                                   .OfType <ArtifactInfo.Package>()
                                   .Where(p => !p.IsSymbolsArtifact)
                                   .ToDictionary(p => p.PackageInfo.Id, p => p, StringComparer.OrdinalIgnoreCase);

            var factory = new SolutionInfoFactory(Log, BuildEngine5);
            var props   = MSBuildListSplitter.GetNamedProperties(Properties);

            if (!props.TryGetValue("Configuration", out var defaultConfig))
            {
                defaultConfig = "Debug";
            }

            var solutions = factory.Create(Solutions, props, defaultConfig, _cts.Token).OrderBy(f => f.Directory).ToList();

            Log.LogMessage($"Found {solutions.Count} and {solutions.Sum(p => p.Projects.Count)} projects");

            if (_cts.IsCancellationRequested)
            {
                return(false);
            }

            var repoGraph           = new AdjacencyMatrix(solutions.Count);
            var packageToProjectMap = new Dictionary <PackageIdentity, ProjectInfo>();

            for (var i = 0; i < solutions.Count; i++)
            {
                var sln = repoGraph[i] = solutions[i];

                foreach (var proj in sln.Projects)
                {
                    if (!proj.IsPackable ||
                        proj.FullPath.Contains("samples") ||
                        proj.FullPath.Contains("tools/Microsoft.VisualStudio.Web.CodeGeneration.Design"))
                    {
                        continue;
                    }

                    var id = new PackageIdentity(proj.PackageId, new NuGetVersion(proj.PackageVersion));

                    if (packageToProjectMap.TryGetValue(id, out var otherProj))
                    {
                        Log.LogError($"Both {proj.FullPath} and {otherProj.FullPath} produce {id}");
                        continue;
                    }

                    packageToProjectMap.Add(id, proj);
                }

                var sharedSrc = Path.Combine(sln.Directory, "shared");
                if (Directory.Exists(sharedSrc))
                {
                    foreach (var dir in Directory.GetDirectories(sharedSrc, "*.Sources"))
                    {
                        var id            = GetDirectoryName(dir);
                        var artifactInfo  = packageArtifacts[id];
                        var sharedSrcProj = new ProjectInfo(dir,
                                                            Array.Empty <ProjectFrameworkInfo>(),
                                                            Array.Empty <DotNetCliReferenceInfo>(),
                                                            true,
                                                            artifactInfo.PackageInfo.Id,
                                                            artifactInfo.PackageInfo.Version.ToNormalizedString());
                        sharedSrcProj.SolutionInfo = sln;
                        var identity = new PackageIdentity(artifactInfo.PackageInfo.Id, artifactInfo.PackageInfo.Version);
                        packageToProjectMap.Add(identity, sharedSrcProj);
                    }
                }
            }

            if (Log.HasLoggedErrors)
            {
                return(false);
            }

            for (var i = 0; i < solutions.Count; i++)
            {
                var src = repoGraph[i];

                foreach (var proj in src.Projects)
                {
                    if (!proj.IsPackable ||
                        proj.FullPath.Contains("samples"))
                    {
                        continue;
                    }

                    foreach (var dep in proj.Frameworks.SelectMany(f => f.Dependencies.Values))
                    {
                        if (packageToProjectMap.TryGetValue(new PackageIdentity(dep.Id, new NuGetVersion(dep.Version)), out var target))
                        {
                            var j = repoGraph.FindIndex(target.SolutionInfo);
                            repoGraph.SetLink(i, j);
                        }
                    }

                    foreach (var toolDep in proj.Tools)
                    {
                        if (packageToProjectMap.TryGetValue(new PackageIdentity(toolDep.Id, new NuGetVersion(toolDep.Version)), out var target))
                        {
                            var j = repoGraph.FindIndex(target.SolutionInfo);
                            repoGraph.SetLink(i, j);
                        }
                    }
                }
            }

            var repos = Repositories.ToDictionary(i => i.ItemSpec, i => i, StringComparer.OrdinalIgnoreCase);

            for (var i = 0; i < repoGraph.Count; i++)
            {
                var src      = repoGraph[i];
                var repoName = GetDirectoryName(src.Directory);
                var repo     = repos[repoName];

                for (var j = 0; j < repoGraph.Count; j++)
                {
                    if (j == i)
                    {
                        continue;
                    }
                    if (repoGraph.HasLink(i, j))
                    {
                        var target         = repoGraph[j];
                        var targetRepoName = GetDirectoryName(target.Directory);
                        var targetRepo     = repos[targetRepoName];

                        if (src.Shipped && !target.Shipped)
                        {
                            Log.LogError($"{repoName} cannot depend on {targetRepoName}. Repos marked as 'Shipped' cannot depend on repos that are rebuilding. Update the configuration in submodule.props.");
                        }
                    }
                }
            }

            return(!Log.HasLoggedErrors);
        }
Ejemplo n.º 10
0
        public IReadOnlyList <SolutionInfo> Create(IEnumerable <ITaskItem> solutionItems, IDictionary <string, string> properties, string defaultConfig, CancellationToken ct)
        {
            var timer = Stopwatch.StartNew();

            var solutions = new ConcurrentBag <SolutionInfo>();

            Parallel.ForEach(solutionItems, solution =>
            {
                if (ct.IsCancellationRequested)
                {
                    return;
                }

                var solutionFile  = solution.ItemSpec.Replace('\\', '/');
                var solutionProps = new Dictionary <string, string>(properties, StringComparer.OrdinalIgnoreCase);
                foreach (var prop in MSBuildListSplitter.GetNamedProperties(solution.GetMetadata("AdditionalProperties")))
                {
                    solutionProps[prop.Key] = prop.Value;
                }

                if (!solutionProps.TryGetValue("Configuration", out var configName))
                {
                    solutionProps["Configuration"] = configName = defaultConfig;
                }

                var key = $"SlnInfo:{solutionFile}:{configName}";
                var obj = _buildEngine.GetRegisteredTaskObject(key, RegisteredTaskObjectLifetime.Build);

                if (obj is SolutionInfo cachedSlnInfo)
                {
                    solutions.Add(cachedSlnInfo);
                    return;
                }

                _logger.LogMessage($"Analyzing {solutionFile} ({configName})");
                var projects     = new ConcurrentBag <ProjectInfo>();
                var projectFiles = GetProjectsForSolutionConfig(solutionFile, configName);
                using (var projCollection = new ProjectCollection(solutionProps)
                {
                    IsBuildEnabled = false
                })
                {
                    Parallel.ForEach(projectFiles, projectFile =>
                    {
                        if (ct.IsCancellationRequested)
                        {
                            return;
                        }

                        try
                        {
                            projects.Add(new ProjectInfoFactory(_logger).Create(projectFile, projCollection));
                        }
                        catch (Exception ex)
                        {
                            _logger.LogErrorFromException(ex);
                        }
                    });
                }

                bool.TryParse(solution.GetMetadata("Build"), out var shouldBuild);
                bool.TryParse(solution.GetMetadata("IsPatching"), out var isPatching);

                var solutionInfo = new SolutionInfo(
                    solutionFile,
                    configName,
                    projects.ToArray(),
                    shouldBuild,
                    isPatching);

                _buildEngine.RegisterTaskObject(key, solutionInfo, RegisteredTaskObjectLifetime.Build, allowEarlyCollection: true);

                solutions.Add(solutionInfo);
            });

            timer.Stop();
            _logger.LogMessage(MessageImportance.High, $"Finished design-time build in {timer.ElapsedMilliseconds}ms");
            return(solutions.ToArray());
        }