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); }
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)); } }
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)); }
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); }
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()); }
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); }
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); }
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); }
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()); }