static XUnitSettings BuildXUnitSettings(ParsedProject projectUnderTest, AnyUnitTestSettings settings, DirectoryPath outputDir) { var outputFile = outputDir.CombineWithFilePath(projectUnderTest.Project.AssemblyName + ".xunit.xml"); var traitArgs = new List <KeyValuePair <string, string> >(); AddTraits(traitArgs, "/-trait", settings.ExcludedTraits); AddTraits(traitArgs, "/trait", settings.IncludedTraits); var s = new XUnitSettings(); XBuildHelper.ApplyToolSettings(s, NUnitToolArgs); s.ShadowCopy = settings.ShadowCopyAssemblies; s.ArgumentCustomization = args => { foreach (var traitArg in traitArgs) { args.Append(traitArg.Key); args.AppendQuoted(traitArg.Value); } args.Append("/xml"); args.AppendQuoted(outputFile.MakeAbsolute(Context.Environment).FullPath); return(args); }; return(s); }
static ProjectFile FindMetaDataFile(ParsedProject p) { var projectFiles = p.Project.Files; return (projectFiles.FirstOrDefault( f => f.Compile && f.RelativePath.EndsWith(BuildConfig.GeneratedAssemblyMetaDataReference))); }
public static DirectoryPath ComputeOutputPath(string type, ParsedProject project) { return(Context.Directory(string.Format("{0}/{4}/{1}/{2}/{3}", TargetDirectory, project.Platform, Config.Configuration, project.Project.AssemblyName, type))); }
public static void SalvageBuildResults(ParsedProject project) { var relativeProjectFile = Context.Environment.WorkingDirectory.GetRelativePath(project.ProjectFile); Context.Log.Verbose(string.Format("Salvaging build result for {0} with platform {1}", relativeProjectFile, project.Platform)); var outputDir = project.ProjectFile.GetDirectory().Combine(project.Project.OutputPath); var targetDir = ComputeProjectBinPath(project); Context.CopyDirectory(outputDir, targetDir); }
static FilePath ComputeTargetFile(ParsedProject project) { string extension; if ("Exe".Equals(project.Project.OutputType, StringComparison.InvariantCultureIgnoreCase) || "Winexe".Equals(project.Project.OutputType, StringComparison.InvariantCultureIgnoreCase)) { extension = ".exe"; } else { extension = ".dll"; } return(project.Project.AssemblyName + extension); }
static NUnitSettings BuildNUnitSettings(ParsedProject projectUnderTest, AnyUnitTestSettings settings, DirectoryPath outputDir) { var s = new NUnitSettings(); XBuildHelper.ApplyToolSettings(s, NUnitToolArgs); if (settings.ExcludedCategories.Count > 0) { s.Exclude = string.Join(",", settings.ExcludedCategories); } if (settings.IncludedCategories.Count > 0) { s.Include = string.Join(",", settings.IncludedCategories); } s.ShadowCopy = settings.ShadowCopyAssemblies; s.UseSingleThreadedApartment = settings.UseSingleThreadedApartment; s.X86 = settings.ForceX86 || projectUnderTest.Platform == PlatformTarget.x86; s.ResultsFile = outputDir.CombineWithFilePath(projectUnderTest.Project.AssemblyName + ".nunit2.xml"); return(s); }
public static ParseResult ParseProjects(ICakeContext context, string configuration, List <PlatformTarget> platforms, List <FilePath> effectiveProjects) { var c = new ParseResult(); foreach (var platform in platforms) { var projects = effectiveProjects .Select(p => ParsedProject.Parse(context, p, configuration, platform)) .Where(IsValidProjectForPlatform(context)) .ToList(); if (projects.Count > 0) { c.ProjectsByPlatform[platform] = projects; c.PlatformBuildOrder.Add(platform); } } return(c); }
static NUnit3Settings BuildNUnit3Settings(ParsedProject projectUnderTest, AnyUnitTestSettings settings, DirectoryPath outputDir) { var whereClause = BuildNUnit3WhereClause(settings); var s = new NUnit3Settings(); XBuildHelper.ApplyToolSettings(s, NUnitToolArgs); s.ShadowCopy = settings.ShadowCopyAssemblies; s.X86 = settings.ForceX86 || projectUnderTest.Platform == PlatformTarget.x86; var path = outputDir.CombineWithFilePath(projectUnderTest.Project.AssemblyName + ".nunit3.xml"); NUnit3Result rxx = new NUnit3Result(); rxx.FileName = path; rxx.Format = "nunit3"; rxx.Transform = null; s.Results = new List <NUnit3Result> { rxx }; s.ArgumentCustomization = args => { if (whereClause != null) { args.Append("--where"); args.AppendQuoted(whereClause); } // Additionally generate NUnit2 output. AppendNUnit3AlternativeOutput(args, outputDir.CombineWithFilePath(projectUnderTest.Project.AssemblyName + ".nunit2.xml")); return(args); }; return(s); }
private void AddEdgesFromProjectReferenceItems(Dictionary <ConfigurationMetadata, ParsedProject> allParsedProjects, GraphEdges edges) { Dictionary <ProjectGraphNode, HashSet <ProjectGraphNode> > transitiveReferenceCache = new(allParsedProjects.Count); foreach (var parsedProject in allParsedProjects) { var currentNode = parsedProject.Value.GraphNode; var requiresTransitiveProjectReferences = _projectInterpretation.RequiresTransitiveProjectReferences(currentNode.ProjectInstance); foreach (var referenceInfo in parsedProject.Value.ReferenceInfos) { // Always add direct references. currentNode.AddProjectReference( allParsedProjects[referenceInfo.ReferenceConfiguration].GraphNode, referenceInfo.ProjectReferenceItem, edges); // Add transitive references only if the project requires it. if (requiresTransitiveProjectReferences) { foreach (var transitiveProjectReference in GetTransitiveProjectReferencesExcludingSelf(allParsedProjects[referenceInfo.ReferenceConfiguration])) { currentNode.AddProjectReference( transitiveProjectReference, new ProjectItemInstance( project: currentNode.ProjectInstance, itemType: ProjectInterpretation.TransitiveReferenceItemName, includeEscaped: referenceInfo.ReferenceConfiguration.ProjectFullPath, directMetadata: null, definingFileEscaped: currentNode.ProjectInstance.FullPath ), edges); } } } } HashSet <ProjectGraphNode> GetTransitiveProjectReferencesExcludingSelf(ParsedProject parsedProject) { if (transitiveReferenceCache.TryGetValue(parsedProject.GraphNode, out HashSet <ProjectGraphNode> transitiveReferences)) { return(transitiveReferences); } transitiveReferences = new(); // Add the results to the cache early, even though it'll be incomplete until the loop below finishes. This helps handle cycles by not allowing them to recurse infinitely. // Note that this makes transitive references incomplete in the case of a cycle, but direct dependencies are always added so a cycle will still be detected and an exception will still be thrown. transitiveReferenceCache[parsedProject.GraphNode] = transitiveReferences; foreach (ProjectInterpretation.ReferenceInfo referenceInfo in parsedProject.ReferenceInfos) { ParsedProject reference = allParsedProjects[referenceInfo.ReferenceConfiguration]; transitiveReferences.Add(reference.GraphNode); // Perf note: avoiding UnionWith to avoid boxing the HashSet enumerator. foreach (ProjectGraphNode transitiveReference in GetTransitiveProjectReferencesExcludingSelf(reference)) { transitiveReferences.Add(transitiveReference); } } return(transitiveReferences); } }
/// <summary> /// Thanks to NuGet being an extension of VisualStudio instead of a proper standalone /// tool (just look at the tight binding of 'nuget pack .xxproj' into MSBuild internals /// and the massive amount of code just to manage that stuff), we cannot build NuGet /// packages from existing binaries by just looking at the project and nuspec files. /// Therefore we preemtively produce nuget packages as soon as the compiler finished /// building the project (and before any testing has been done). We then copy the /// NuGet package into a safe place before eventually pushing it to a server or achiving /// the files by other means. /// </summary> /// <param name="project"></param> public static void ProduceNuGetPackage(ParsedProject project) { var nuspec = project.ProjectFile.ChangeExtension(".nuspec"); if (!Context.FileExists(nuspec)) { Context.Log.Verbose("Skipping package as there is no *.nuspec file for project " + BuildConfig.AsRelativePath(project.ProjectFile)); return; } var settings = new NuGetXPackSettings(PackSettings); if (PackSettingsCustomisation != null) { settings = PackSettingsCustomisation.Invoke(settings, project); } var nugetSettings = new NuGetPackSettings(); XBuildHelper.ApplyToolSettings(nugetSettings, settings); nugetSettings.Verbosity = settings.Verbosity; nugetSettings.Symbols = settings.Symbols.GetValueOrDefault(); nugetSettings.IncludeReferencedProjects = settings.IncludeReferencedProjects.GetValueOrDefault(); nugetSettings.Properties = new Dictionary <string, string>(settings.Properties); nugetSettings.ArgumentCustomization = settings.ArgumentCustomization; if (NuGetPackSettingsCustomisation != null) { nugetSettings = NuGetPackSettingsCustomisation.Invoke(nugetSettings, project); if (nugetSettings.Properties == null) { nugetSettings.Properties = new Dictionary <string, string>(); } } var targetPath = BuildConfig.ComputeOutputPath("packages", project); Context.CreateDirectory(targetPath); nugetSettings.WorkingDirectory = targetPath; nugetSettings.Properties["Configuration"] = BuildConfig.Configuration; nugetSettings.Properties["Platform"] = BuildConfig.ConvertPlatformTargetToString(project.Platform); if (!string.IsNullOrEmpty(Version)) { Context.Log.Information("Publishing package as version " + Version); nugetSettings.Properties["version"] = Version; nugetSettings.Version = Version; } if (settings.Tool.GetValueOrDefault()) { var argCustomization = nugetSettings.ArgumentCustomization; nugetSettings.ArgumentCustomization = args => { if (argCustomization != null) { args = argCustomization.Invoke(args); } args.Append("-Tool"); return(args); }; } Context.NuGetPack(project.ProjectFile, nugetSettings); var assembly = LoadZipAssembly(); if (assembly != null) { Context.Log.Information("Unzipping nuget package: " + targetPath.FullPath + "/*.nupkg"); foreach (var file in Context.Globber.GetFiles(targetPath.FullPath + "/*.nupkg")) { Context.Log.Information("Unzipping " + file); Unzip(file, targetPath, assembly); } } }
static bool HasTestFramework(ParsedProject project, string id) { return(project.Project.References.Any(refs => refs.Include.StartsWith(id, true, CultureInfo.InvariantCulture))); }
public static DirectoryPath ComputeProjectUnitTestPath(ParsedProject project) { return(BuildConfig.ComputeOutputPath("tests", project)); }
static void InvokeUnitTest(ParsedProject project, PlatformTarget platformTarget, AnyUnitTestSettings settings) { if (settings == null) { throw new ArgumentNullException("settings"); } if (project == null) { throw new ArgumentNullException("project"); } var path = project.ProjectFile; var relativeProjectFile = Context.Environment.WorkingDirectory.GetRelativePath(path); // parse referenced dependencies. We assume that if you reference a unit-test dll in your project, then that project // in fact contains tests. But there are always exceptions, and thus we allow you to exclude projects as well. if (ExcludeFromUnitTests.Contains(project.Project.AssemblyName) || ExcludeFromUnitTests.Contains(project.Project.ProjectGuid) || ExcludeFromUnitTests.Contains(relativeProjectFile.ToString())) { Context.Log.Verbose(string.Format(" Skipping project {0} as it was explicitly excluded from testing.", relativeProjectFile)); return; } Context.Log.Information(string.Format("Searching for tests in {0} with platform {1}", relativeProjectFile, platformTarget)); var binDir = BuildActions.ComputeProjectBinPath(project); var testDir = ComputeProjectUnitTestPath(project); var testDll = binDir.CombineWithFilePath(ComputeTargetFile(project)); if (HasTestFramework(project, NUnit2Reference)) { Context.Log.Verbose(string.Format(" Testing with NUnit 2: {0}.", relativeProjectFile)); Context.CreateDirectory(testDir); Context.NUnit(new[] { testDll }, BuildNUnitSettings(project, settings, testDir)); } else if (HasTestFramework(project, NUnit3Reference)) { Context.Log.Verbose(string.Format(" Testing with NUnit 3: {0}.", relativeProjectFile)); Context.CreateDirectory(testDir); Context.NUnit3(new[] { testDll }, BuildNUnit3Settings(project, settings, testDir)); } else if (HasTestFramework(project, Xunit2Reference)) { Context.Log.Verbose(string.Format(" Testing with XUnit 2: {0}.", relativeProjectFile)); Context.CreateDirectory(testDir); Context.XUnit2(new[] { testDll }, BuildXUnit2Settings(project, settings, testDir)); var inputFile = testDir.CombineWithFilePath(project.Project.AssemblyName + ".xunit2.xml"); var outputFile = testDir.CombineWithFilePath(project.Project.AssemblyName + ".nunit2.xml"); Context.XmlTransform(Context.File("tools/xunit.runner.console/tools/NUnitXml.xslt"), inputFile, outputFile); } else if (HasTestFramework(project, Xunit1Reference)) { Context.Log.Verbose(string.Format(" Testing with XUnit 1: {0}.", relativeProjectFile)); Context.CreateDirectory(testDir); var xunitSettings = BuildXUnitSettings(project, settings, testDir); var xunitRunner = new FixedXUnitRunner(Context.FileSystem, Context.Environment, Context.ProcessRunner, Context.Tools); xunitRunner.Run(testDll, xunitSettings, settings.ForceX86 || project.Platform == PlatformTarget.x86); var inputFile = testDir.CombineWithFilePath(project.Project.AssemblyName + ".xunit.xml"); var outputFile = testDir.CombineWithFilePath(project.Project.AssemblyName + ".nunit2.xml"); Context.XmlTransform(Context.File("tools/xunit.runners/tools/NUnitXml.xslt"), inputFile, outputFile); } else { Context.Log.Verbose( string.Format(" Skipping project {0} as it does not reference a supported unit-testing framework.", relativeProjectFile)); } }
private void AddEdgesFromProjectReferenceItems(Dictionary <ConfigurationMetadata, ParsedProject> allParsedProjects, GraphEdges edges) { var transitiveReferenceCache = new Dictionary <ProjectGraphNode, HashSet <ProjectGraphNode> >(allParsedProjects.Count); foreach (var parsedProject in allParsedProjects) { var currentNode = parsedProject.Value.GraphNode; var requiresTransitiveProjectReferences = _projectInterpretation.RequiresTransitiveProjectReferences(currentNode.ProjectInstance); foreach (var referenceInfo in parsedProject.Value.ReferenceInfos) { // Always add direct references. currentNode.AddProjectReference( allParsedProjects[referenceInfo.ReferenceConfiguration].GraphNode, referenceInfo.ProjectReferenceItem, edges); // Add transitive references only if the project requires it. if (requiresTransitiveProjectReferences) { foreach (var transitiveProjectReference in GetTransitiveProjectReferencesExcludingSelf(allParsedProjects[referenceInfo.ReferenceConfiguration])) { currentNode.AddProjectReference( transitiveProjectReference, new ProjectItemInstance( project: currentNode.ProjectInstance, itemType: ProjectInterpretation.TransitiveReferenceItemName, includeEscaped: referenceInfo.ReferenceConfiguration.ProjectFullPath, directMetadata: null, definingFileEscaped: currentNode.ProjectInstance.FullPath ), edges); } } } } HashSet <ProjectGraphNode> GetTransitiveProjectReferencesExcludingSelf(ParsedProject parsedProject) { HashSet <ProjectGraphNode> references = new(); GetTransitiveProjectReferencesExcludingSelfHelper(parsedProject, references, null); return(references); } // transitiveReferences contains all of the references we've found so far from the initial GetTransitiveProjectReferencesExcludingSelf call. // referencesFromHere is essentially "reset" at each level of the recursion. // The first is important because if we find a cycle at some point, we need to know not to keep recursing. We wouldn't have added to transitiveReferenceCache yet, since we haven't finished // finding all the transitive references yet. // On the other hand, the second is important to help us fill that cache afterwards. The cache is from a particular node to all of its references, including transitive references // but not including itself, which means we can't include parents as we would if we used transitiveReferences. You can see that for any particular call, it creates a new "toCache" // HashSet that we fill with direct references and pass as referencesFromHere in recursive calls to fill it with transitive references. It is then used to populate the cache. // Meanwhile, we avoid going into the recursive step at all if transitiveReferences already includes a particular node to avoid a StackOverflowException if there's a loop. void GetTransitiveProjectReferencesExcludingSelfHelper(ParsedProject parsedProject, HashSet <ProjectGraphNode> traversedReferences, HashSet <ProjectGraphNode> incompleteReferencesOfDirectlyReferencingNode) { if (transitiveReferenceCache.TryGetValue(parsedProject.GraphNode, out HashSet <ProjectGraphNode> cachedTransitiveReferences)) { traversedReferences.UnionWith(cachedTransitiveReferences); } else { HashSet <ProjectGraphNode> referencesFromThisNode = new(); foreach (ProjectInterpretation.ReferenceInfo referenceInfo in parsedProject.ReferenceInfos) { ParsedProject reference = allParsedProjects[referenceInfo.ReferenceConfiguration]; if (traversedReferences.Add(reference.GraphNode)) { GetTransitiveProjectReferencesExcludingSelfHelper(reference, traversedReferences, referencesFromThisNode); } else if (transitiveReferenceCache.TryGetValue(reference.GraphNode, out cachedTransitiveReferences)) { referencesFromThisNode.UnionWith(cachedTransitiveReferences); } referencesFromThisNode.Add(reference.GraphNode); } // We've returned from recursing through all transitive references // of this node, so add that set to the cache transitiveReferenceCache[parsedProject.GraphNode] = referencesFromThisNode; if (incompleteReferencesOfDirectlyReferencingNode is not null) { // Also add it to the set of transitive dependencies of // the referencing node (which are probably still incomplete) incompleteReferencesOfDirectlyReferencingNode.UnionWith(referencesFromThisNode); } } } }
public ProjectReference(ParsedProject project, string unevaluatedInclude) { this.Project = project; this.UnevaluatedInclude = unevaluatedInclude; }
internal static DirectoryPath ComputeProjectBinPath(ParsedProject project) { return(BuildConfig.ComputeOutputPath("compiled", project)); }