#addin "nuget:?package=Cake.FileHelpers&version=3.2.1" // Contains .NET - related Cake targets var ext = IsRunningOnWindows() ? ".exe" : ""; var dotnetPath = $"./.dotnet/dotnet{ext}"; string configuration = GetBuildVariable("configuration", GetBuildVariable("BUILD_CONFIGURATION", "DEBUG")); var localDotnet = GetBuildVariable("workloads", "local") == "local"; var vsVersion = GetBuildVariable("VS", ""); string MSBuildExe = Argument("msbuild", EnvironmentVariable("MSBUILD_EXE", "")); string nugetSource = Argument("nugetsource", ""); string officialBuildId = Argument("officialbuildid", ""); string testFilter = Argument("test-filter", EnvironmentVariable("TEST_FILTER")); var rootFolder = Context.Environment.WorkingDirectory; if (rootFolder.FullPath.EndsWith("/devices", StringComparison.OrdinalIgnoreCase)) rootFolder = rootFolder.Combine("../../").Collapse(); var arcadeBin = MakeAbsolute(new DirectoryPath("./artifacts/bin/")); string TestTFM = Argument("testtfm", ""); var useNuget = Argument("usenuget", true); if (TestTFM == "default") TestTFM = ""; Exception pendingException = null; var NuGetOnlyPackages = new string[] { "Microsoft.Maui.Controls.*.{nupkg,snupkg}", "Microsoft.Maui.Core.*.{nupkg,snupkg}", "Microsoft.Maui.Essentials.*.{nupkg,snupkg}", "Microsoft.Maui.Graphics.*.{nupkg,snupkg}", "Microsoft.Maui.Maps.*.{nupkg,snupkg}", "Microsoft.Maui.Resizetizer.*.{nupkg,snupkg}", "Microsoft.AspNetCore.Components.WebView.*.{nupkg,snupkg}", }; public enum RuntimeVariant { Mono, NativeAOT } RuntimeVariant RUNTIME_VARIANT = Argument("runtimevariant", RuntimeVariant.Mono); bool USE_NATIVE_AOT = RUNTIME_VARIANT == RuntimeVariant.NativeAOT ? true : false; ProcessTFMSwitches(); // Tasks for CI Task("dotnet") .Description("Provisions the .NET SDK into bin/dotnet based on eng/Versions.props") .Does(() => { if (!localDotnet) return; //We are passing a nuget folder with nuget locations if(!string.IsNullOrEmpty(nugetSource)) { EnsureDirectoryExists(nugetSource); var originalNuget = File($"{rootFolder}/NuGet.config"); ReplaceTextInFiles(originalNuget, "", ""); ReplaceTextInFiles(originalNuget, "NUGET_ONLY_PLACEHOLDER", nugetSource); } DotNetBuild($"{rootFolder}/src/DotNet/DotNet.csproj", new DotNetBuildSettings { MSBuildSettings = new DotNetMSBuildSettings() .EnableBinaryLogger($"{GetLogDirectory()}/dotnet-{configuration}-{DateTime.UtcNow.ToFileTimeUtc()}.binlog") .SetConfiguration(configuration), }); DotNetTool("tool", new DotNetToolSettings { ToolPath = dotnetPath, DiagnosticOutput = true, ArgumentCustomization = args => args.Append("restore") }); }); Task("dotnet-local-workloads") .Does(() => { if (!localDotnet) return; DotNetBuild("./src/DotNet/DotNet.csproj", new DotNetBuildSettings { MSBuildSettings = new DotNetMSBuildSettings() .EnableBinaryLogger($"{GetLogDirectory()}/dotnet-{configuration}-{DateTime.UtcNow.ToFileTimeUtc()}.binlog") .SetConfiguration(configuration) .WithProperty("InstallWorkloadPacks", "false"), }); DotNetBuild("./src/DotNet/DotNet.csproj", new DotNetBuildSettings { MSBuildSettings = new DotNetMSBuildSettings() .EnableBinaryLogger($"{GetLogDirectory()}/dotnet-install-{configuration}-{DateTime.UtcNow.ToFileTimeUtc()}.binlog") .SetConfiguration(configuration) .WithTarget("Install"), ToolPath = dotnetPath, }); DotNetTool("tool", new DotNetToolSettings { ToolPath = dotnetPath, DiagnosticOutput = true, ArgumentCustomization = args => args.Append("restore") }); }); Task("dotnet-buildtasks") .WithCriteria(Argument("sln", null) == null) .IsDependentOn("dotnet") .Does(() => { RunMSBuildWithDotNet($"{rootFolder}/Microsoft.Maui.BuildTasks.slnf"); }) .OnError(exception => { if (IsTarget("VS")) { pendingException = exception; return; } throw exception; }); Task("android-aar") .Does(() => { var root = "./src/Core/AndroidNative/"; var gradlew = root + "gradlew"; if (IsRunningOnWindows()) gradlew += ".bat"; var exitCode = StartProcess( MakeAbsolute((FilePath)gradlew), new ProcessSettings { Arguments = $"createAar --rerun-tasks", WorkingDirectory = root }); if (exitCode != 0) { if (IsCIBuild() || IsTarget("android-aar")) throw new Exception("Gradle failed to build maui.aar: " + exitCode); else Information("This task failing locally will not break local MAUI development. Gradle failed to build maui.aar: {0}", exitCode); } }); Task("dotnet-build") .IsDependentOn("dotnet") .IsDependentOn("dotnet-buildtasks") .IsDependentOn("android-aar") .Description("Build the solutions") .Does(() => { if (IsRunningOnWindows()) { RunMSBuildWithDotNet("./Microsoft.Maui.sln"); } else { RunMSBuildWithDotNet("./Microsoft.Maui-mac.slnf"); } }); Task("dotnet-samples") .IsDependentOn("dotnet-buildtasks") .Does(() => { var tempDir = PrepareSeparateBuildContext("samplesTest"); var properties = new Dictionary(); if(useNuget) { properties = new Dictionary { ["UseWorkload"] = "true", // ["GenerateAppxPackageOnBuild"] = "true", ["RestoreConfigFile"] = tempDir.CombineWithFilePath("NuGet.config").FullPath, }; } string projectsToBuild; if (USE_NATIVE_AOT) { if (configuration.Equals("Debug", StringComparison.OrdinalIgnoreCase)) { var errMsg = $"Error: Building dotnet-samples with NativeAOT is only supported in Release configuration"; Error(errMsg); throw new Exception(errMsg); } projectsToBuild = "./src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj"; properties["_UseNativeAot"] = "true"; properties["RuntimeIdentifier"] = "iossimulator-x64"; properties["BuildIpa"] = "true"; } else { projectsToBuild = "./Microsoft.Maui.Samples.slnf"; } RunMSBuildWithDotNet(projectsToBuild, properties, binlogPrefix: "sample-"); }); // Builds the host app for the UI Tests Task("uitests-apphost") .IsDependentOn("dotnet-buildtasks") .Does(() => { var tempDir = PrepareSeparateBuildContext("samplesTest"); var properties = new Dictionary(); if(useNuget) { properties = new Dictionary { ["UseWorkload"] = "true", // ["GenerateAppxPackageOnBuild"] = "true", ["RestoreConfigFile"] = tempDir.CombineWithFilePath("NuGet.config").FullPath, }; } RunMSBuildWithDotNet("./src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj", properties, binlogPrefix: "uitests-apphost-"); }); Task("dotnet-legacy-controlgallery") .IsDependentOn("dotnet-legacy-controlgallery-android") .IsDependentOn("dotnet-legacy-controlgallery-ios"); Task("dotnet-legacy-controlgallery-ios") .Does(() => { var properties = new Dictionary(); properties.Add("RuntimeIdentifier","iossimulator-x64"); RunMSBuildWithDotNet("./src/Compatibility/ControlGallery/src/iOS/Compatibility.ControlGallery.iOS.csproj", properties, binlogPrefix: "controlgallery-ios-"); }); Task("dotnet-legacy-controlgallery-android") .Does(() => { var properties = new Dictionary(); RunMSBuildWithDotNet("./src/Compatibility/ControlGallery/src/Android/Compatibility.ControlGallery.Android.csproj", properties, binlogPrefix: "controlgallery-android-"); }); Task("dotnet-integration-build") .Does(() => { var properties = new Dictionary(); RunMSBuildWithDotNet("./src/TestUtils/src/Microsoft.Maui.IntegrationTests/Microsoft.Maui.IntegrationTests.csproj", properties, binlogPrefix: "integration-"); }); Task("dotnet-integration-test") .Does(() => { RunTestWithLocalDotNet( "./src/TestUtils/src/Microsoft.Maui.IntegrationTests/Microsoft.Maui.IntegrationTests.csproj", config: configuration, pathDotnet: dotnetPath, noBuild: true, resultsFileNameWithoutExtension: Argument("resultsfilename", ""), filter: Argument("filter", "")); }); Task("dotnet-test") .IsDependentOn("dotnet") .Description("Build the solutions") .Does(() => { var tests = new [] { "**/Controls.Core.UnitTests.csproj", // "**/Controls.Core.Design.UnitTests.csproj", "**/Controls.Xaml.UnitTests.csproj", "**/SourceGen.UnitTests.csproj", "**/Controls.BindingSourceGen.UnitTests.csproj", "**/Core.UnitTests.csproj", "**/Essentials.UnitTests.csproj", "**/Resizetizer.UnitTests.csproj", "**/Graphics.Tests.csproj", "**/Compatibility.Core.UnitTests.csproj", }; var success = true; foreach (var test in tests) { if (!IsRunningOnWindows() && (test.Contains("Compatibility.Core.UnitTests") || test.Contains("Controls.Core.Design.UnitTests"))) { continue; } foreach (var project in GetFiles(test)) { try { RunTestWithLocalDotNet(project.FullPath, configuration, dotnetPath); } catch { success = false; Error($"Test project failed: {project}"); } } } if (!success) throw new Exception("Some tests failed. Check the logs or test results."); }); Task("dotnet-pack-maui") .IsDependentOn("android-aar") .WithCriteria(RunPackTarget()) .Does(() => { // We are passing a nuget folder with nuget locations if (!string.IsNullOrEmpty(nugetSource)) { EnsureDirectoryExists(nugetSource); var originalNuget = File("./NuGet.config"); ReplaceTextInFiles(originalNuget, "", ""); ReplaceTextInFiles(originalNuget, "LOCAL_PLACEHOLDER", nugetSource); } var sln = "./Microsoft.Maui.Packages.slnf"; if (!IsRunningOnWindows()) sln = "./Microsoft.Maui.Packages-mac.slnf"; if(string.IsNullOrEmpty(officialBuildId)) { officialBuildId = DateTime.UtcNow.ToString("yyyyMMdd.1"); } RunMSBuildWithDotNet(sln, target: "Pack", properties: new Dictionary { { "SymbolPackageFormat", "snupkg" }, { "OfficialBuildId", officialBuildId }, }); }); Task("dotnet-pack-additional") .WithCriteria(RunPackTarget()) .Does(() => { // Download some additional symbols that need to be archived along with the maui symbols: // - _NativeAssets.windows // - libSkiaSharp.pdb // - libHarfBuzzSharp.pdb var assetsDir = $"./artifacts/additional-assets"; var nativeAssetsVersion = XmlPeek("./eng/Versions.props", "/Project/PropertyGroup/_SkiaSharpNativeAssetsVersion"); NuGetInstall("_NativeAssets.windows", new NuGetInstallSettings { Version = nativeAssetsVersion, ExcludeVersion = true, OutputDirectory = assetsDir, Source = new[] { "https://pkgs.dev.azure.com/xamarin/public/_packaging/SkiaSharp/nuget/v3/index.json" }, }); foreach (var nupkg in GetFiles($"{assetsDir}/**/*.nupkg")) DeleteFile(nupkg); Zip(assetsDir, $"{assetsDir}.zip"); }); Task("dotnet-pack-library-packs") .WithCriteria(RunPackTarget()) .Does(() => { var tempDir = $"./artifacts/library-packs-temp"; var destDir = $"./artifacts/library-packs"; EnsureDirectoryExists(destDir); CleanDirectories(destDir); }); Task("dotnet-pack-docs") .WithCriteria(RunPackTarget()) .Does(() => { var tempDir = $"./artifacts/docs-packs-temp"; EnsureDirectoryExists(tempDir); CleanDirectories(tempDir); var destDir = $"./artifacts/docs-packs"; EnsureDirectoryExists(destDir); CleanDirectories(destDir); // Extract the binaries, xml & pdb files for docs purposes foreach (var pattern in NuGetOnlyPackages) { foreach (var nupkg in GetFiles($"./artifacts/**/{pattern}")) { var filename = nupkg.GetFilename().ToString(); var d = $"{tempDir}/{filename}"; Unzip(nupkg, d); DeleteFiles($"{d}/**/*.pri"); DeleteFiles($"{d}/**/*.aar"); DeleteFiles($"{d}/**/*.DesignTools.*"); DeleteFiles($"{d}/**/*.resources.dll"); if (filename.StartsWith("Microsoft.AspNetCore.Components.WebView.Wpf") || filename.StartsWith("Microsoft.AspNetCore.Components.WebView.WindowsForms")) { CopyFiles($"{d}/lib/**/net?.?-windows?.?/**/*.{{dll,xml,pdb}}", $"{destDir}"); continue; } CopyFiles($"{d}/lib/**/net?.?/**/*.{{dll,xml,pdb}}", $"{destDir}"); } } CleanDirectories(tempDir); }); Task("dotnet-pack") .WithCriteria(RunPackTarget()) .IsDependentOn("dotnet-pack-maui") .IsDependentOn("dotnet-pack-additional") .IsDependentOn("dotnet-pack-library-packs") .IsDependentOn("dotnet-pack-docs"); Task("dotnet-build-test") .IsDependentOn("dotnet") .IsDependentOn("dotnet-buildtasks") .IsDependentOn("dotnet-build") .IsDependentOn("dotnet-test"); Task("dotnet-diff") .Does(() => { var nupkgs = GetFiles($"./artifacts/**/*.nupkg"); if (!nupkgs.Any()) { Warning($"##vso[task.logissue type=warning]No NuGet packages were found."); } else { // clean all working folders var diffCacheDir = GetTempDirectory().Combine("diffCache"); EnsureDirectoryExists(diffCacheDir); CleanDirectories(diffCacheDir.FullPath); EnsureDirectoryExists(GetDiffDirectory()); CleanDirectories(GetDiffDirectory().FullPath); // run the diff foreach (var nupkg in nupkgs) { DotNetTool("api-tools", new DotNetToolSettings { DiagnosticOutput = true, ArgumentCustomization = builder => builder .Append("nuget-diff") .AppendQuoted(nupkg.FullPath) .Append("--latest") // .Append("--verbose") .Append("--group-ids") .Append("--ignore-unchanged") .AppendSwitchQuoted("--output", GetDiffDirectory().FullPath) .AppendSwitchQuoted("--cache", diffCacheDir.FullPath) }); } // clean working folders try { CleanDirectories(diffCacheDir.FullPath); } catch { Information("Unable to clean up diff cache directory."); } var diffs = GetFiles($"{GetDiffDirectory()}/**/*.md"); if (!diffs.Any()) { Warning($"##vso[task.logissue type=warning]No NuGet diffs were found."); } else { // clean working folders var temp = diffCacheDir.Combine("md-files"); EnsureDirectoryExists(diffCacheDir); CleanDirectories(diffCacheDir.FullPath); // copy and rename files for UI foreach (var diff in diffs) { var segments = diff.Segments.Reverse().ToArray(); var nugetId = segments[2]; var platform = segments[1]; var assembly = ((FilePath)segments[0]).GetFilenameWithoutExtension().GetFilenameWithoutExtension(); var breaking = segments[0].EndsWith(".breaking.md"); // using non-breaking spaces var newName = breaking ? "[BREAKING] " : ""; newName += $"{nugetId} {assembly} ({platform}).md"; var newPath = diffCacheDir.CombineWithFilePath(newName); CopyFile(diff, newPath); } // push changes to UI var temps = GetFiles($"{diffCacheDir}/**/*.md"); foreach (var t in temps.OrderBy(x => x.FullPath)) { Information($"##vso[task.uploadsummary]{t}"); } } } }); Task("VSCode") .Description("Provisions .NET, and launches an instance of Visual Studio Code using it.") .IsDependentOn("Clean") .IsDependentOn("dotnet") .IsDependentOn("dotnet-buildtasks") .IsDependentOn("dotnet-pack") // Run conditionally .Does(() => { if (pendingException != null) { Error($"{pendingException}"); Error("!!!!BUILD TASKS FAILED: !!!!!"); } UseLocalNuGetCacheFolder(); StartVisualStudioCodeForDotNet(); }); // Tasks for Local Development Task("VS") .Description("Provisions .NET, and launches an instance of Visual Studio using it.") .IsDependentOn("Clean") .IsDependentOn("dotnet") .IsDependentOn("dotnet-buildtasks") .IsDependentOn("dotnet-pack") // Run conditionally .Does(() => { if (pendingException != null) { Error($"{pendingException}"); Error("!!!!BUILD TASKS FAILED: !!!!!"); } UseLocalNuGetCacheFolder(); StartVisualStudioForDotNet(); }); bool RunPackTarget() { // Is the user running the pack target explicitly? if (TargetStartsWith("dotnet-pack")) return true; // If the default target is running then let the pack target run if (IsTarget("Default")) return true; // Does the user want to run a pack as part of a different target? if (HasArgument("pack") && Argument("pack", "true") != "false") return true; // If the request is to open a different sln then let's see if pack has ever run // if it hasn't then lets pack maui so the sln will open if (Argument("sln", null) != null) { return Argument("pack", "true") == "true"; } return false; } Dictionary GetDotNetEnvironmentVariables() { Dictionary envVariables = new Dictionary(); var dotnet = MakeAbsolute(Directory("./.dotnet/")).ToString(); envVariables.Add("DOTNET_INSTALL_DIR", dotnet); envVariables.Add("DOTNET_ROOT", dotnet); envVariables.Add("DOTNET_MSBUILD_SDK_RESOLVER_CLI_DIR", dotnet); envVariables.Add("DOTNET_MULTILEVEL_LOOKUP", "0"); envVariables.Add("MSBuildEnableWorkloadResolver", "true"); var existingPath = EnvironmentVariable("PATH"); Information(dotnet + ":" + existingPath); envVariables.Add("PATH", dotnet + ":" + existingPath); // Get "full" .binlog in Project System Tools if (HasArgument("debug")) envVariables.Add("MSBuildDebugEngine", "1"); return envVariables; } void SetDotNetEnvironmentVariables(string dotnetDir = null) { var dotnet = dotnetDir ?? MakeAbsolute(Directory("./.dotnet/")).ToString(); SetEnvironmentVariable("VSDebugger_ValidateDotnetDebugLibSignatures", "0"); SetEnvironmentVariable("DOTNET_INSTALL_DIR", dotnet); SetEnvironmentVariable("DOTNET_ROOT", dotnet); SetEnvironmentVariable("DOTNET_MSBUILD_SDK_RESOLVER_CLI_DIR", dotnet); SetEnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0"); SetEnvironmentVariable("MSBuildEnableWorkloadResolver", "true"); SetEnvironmentVariable("PATH", dotnet, prepend: true); // Get "full" .binlog in Project System Tools if (HasArgument("dbg")) SetEnvironmentVariable("MSBuildDebugEngine", "1"); } void UseLocalNuGetCacheFolder(bool reset = false) { var temp = Context.Environment.GetSpecialPath(SpecialPath.LocalTemp); var packages = temp.Combine("Microsoft.Maui.Cache/NuGet/packages"); EnsureDirectoryExists(packages); CleanDirectories(packages.FullPath + "/microsoft.maui.*"); CleanDirectories(packages.FullPath + "/microsoft.aspnetcore.*"); if (reset) CleanDirectories(packages.FullPath); SetEnvironmentVariable("RestorePackagesPath", packages.FullPath); SetEnvironmentVariable("NUGET_PACKAGES", packages.FullPath); } void StartVisualStudioCodeForDotNet() { string workspace = "./maui.code-workspace"; if (IsCIBuild()) { Error("This target should not run on CI."); return; } if(localDotnet) { SetDotNetEnvironmentVariables(); } StartProcess("code", new ProcessSettings{ Arguments = workspace, EnvironmentVariables = GetDotNetEnvironmentVariables() }); } void StartVisualStudioForDotNet() { string sln = Argument("sln", null); bool includePrerelease = true; if (!String.IsNullOrEmpty(vsVersion)) includePrerelease = (vsVersion == "preview"); if (String.IsNullOrWhiteSpace(sln)) { if (IsRunningOnWindows()) { sln = "./Microsoft.Maui-windows.slnf"; } else { sln = "./Microsoft.Maui-mac.slnf"; } } if (IsCIBuild()) { Error("This target should not run on CI."); return; } if(localDotnet) { SetDotNetEnvironmentVariables(); } if (IsRunningOnWindows()) { var vsLatest = VSWhereLatest(new VSWhereLatestSettings { IncludePrerelease = includePrerelease, }); if (vsLatest == null) throw new Exception("Unable to find Visual Studio!"); StartProcess(vsLatest.CombineWithFilePath("./Common7/IDE/devenv.exe"), sln); } else { StartProcess("open", new ProcessSettings{ Arguments = sln, EnvironmentVariables = GetDotNetEnvironmentVariables() }); } } // NOTE: These methods work as long as the "dotnet" target has already run void RunMSBuildWithDotNet( string sln, Dictionary properties = null, string target = "Build", bool warningsAsError = false, bool restore = true, string targetFramework = null, bool forceDotNetBuild = false, int maxCpuCount = 0, string binlogPrefix = null) { var useDotNetBuild = forceDotNetBuild || !IsRunningOnWindows() || target == "Run"; var name = System.IO.Path.GetFileNameWithoutExtension(sln); var type = useDotNetBuild ? "dotnet" : "msbuild"; var binlog = string.IsNullOrEmpty(targetFramework) ? $"\"{GetLogDirectory()}/{binlogPrefix}{name}-{configuration}-{target}-{type}-{DateTime.UtcNow.ToFileTimeUtc()}.binlog\"" : $"\"{GetLogDirectory()}/{binlogPrefix}{name}-{configuration}-{target}-{targetFramework}-{type}-{DateTime.UtcNow.ToFileTimeUtc()}.binlog\""; if(localDotnet) SetDotNetEnvironmentVariables(); var msbuildSettings = new DotNetMSBuildSettings() .SetConfiguration(configuration) .SetMaxCpuCount(maxCpuCount) .WithTarget(target) .EnableBinaryLogger(binlog) // .SetVerbosity(Verbosity.Diagnostic) ; if (warningsAsError) { msbuildSettings.TreatAllWarningsAs(MSBuildTreatAllWarningsAs.Error); } if (properties != null) { foreach (var property in properties) { msbuildSettings.WithProperty(property.Key, property.Value); } } var dotnetBuildSettings = new DotNetBuildSettings { MSBuildSettings = msbuildSettings, }; dotnetBuildSettings.ArgumentCustomization = args => { if (!restore) args.Append("--no-restore"); if (!string.IsNullOrEmpty(targetFramework)) args.Append($"-f {targetFramework}"); return args; }; if (localDotnet) dotnetBuildSettings.ToolPath = dotnetPath; DotNetBuild(sln, dotnetBuildSettings); } void RunTestWithLocalDotNet(string csproj, string config, string pathDotnet = null, Dictionary argsExtra = null, bool noBuild = false, string resultsFileNameWithoutExtension = null, string filter = "", int maxCpuCount = 0) { if (string.IsNullOrWhiteSpace(filter)) { filter = testFilter; } if (!string.IsNullOrWhiteSpace(filter)) { Information("Run Tests With Filter {0}", filter); } string binlog; string results; var name = System.IO.Path.GetFileNameWithoutExtension(csproj); var logDirectory = GetLogDirectory(); if (!string.IsNullOrWhiteSpace(pathDotnet) && localDotnet) { // Make sure the path doesn't refer to the dotnet executable and make path absolute var localDotnetRoot = MakeAbsolute(Directory(System.IO.Path.GetDirectoryName(pathDotnet))); Information("new dotnet root: {0}", localDotnetRoot); SetDotNetEnvironmentVariables(localDotnetRoot.FullPath); } if (string.IsNullOrWhiteSpace(resultsFileNameWithoutExtension)) { binlog = $"{logDirectory}/{name}-{config}.binlog"; results = $"{name}-{config}.trx"; } else { binlog = $"{logDirectory}/{resultsFileNameWithoutExtension}.binlog"; results = $"{resultsFileNameWithoutExtension}.trx"; } Information("Run Test binlog: {0}", binlog); var settings = new DotNetTestSettings { Configuration = config, NoBuild = noBuild, Filter = filter, Loggers = { $"trx;LogFileName={results}", $"console;verbosity=normal" }, ResultsDirectory = GetTestResultsDirectory(), // Verbosity = Cake.Common.Tools.DotNetCore.DotNetCoreVerbosity.Diagnostic, ArgumentCustomization = args => { args.Append($"-bl:{binlog}"); if(maxCpuCount > 0) { args.Append($"-maxcpucount:{maxCpuCount}"); } if(argsExtra != null) { foreach(var prop in argsExtra) { args.Append($"/p:{prop.Key}={prop.Value}"); } } // https://github.com/microsoft/vstest/issues/5112 args.Append($"/p:VStestUseMSBuildOutput=false"); return args; } }; if(!string.IsNullOrEmpty(pathDotnet)) { settings.ToolPath = pathDotnet; } try { DotNetTest(csproj, settings); } finally { Information("Test Run complete: {0}", results); } } DirectoryPath PrepareSeparateBuildContext(string dirName, string props = null, string targets = null) { var dir = GetTempDirectory().Combine(dirName); EnsureDirectoryExists(dir); CleanDirectories(dir.FullPath); var nugetOnly = dir.Combine("nuget-only"); EnsureDirectoryExists(nugetOnly); CleanDirectories(nugetOnly.FullPath); CopyFileToDirectory(File("./NuGet.config"), dir); var config = dir.CombineWithFilePath("NuGet.config"); foreach (var pattern in NuGetOnlyPackages) { CopyFiles($"./artifacts/{pattern}", nugetOnly, false); } // Add a specific folder for nuget-only packages ReplaceTextInFiles(config.FullPath, "", ""); ReplaceTextInFiles(config.FullPath, "NUGET_ONLY_PLACEHOLDER", nugetOnly.FullPath); // Create empty or copy test Directory.Build.props/targets if (string.IsNullOrEmpty(props)) FileWriteText(dir.CombineWithFilePath("Directory.Build.props"), ""); else CopyFile(props, dir.CombineWithFilePath("Directory.Build.props")); if (string.IsNullOrEmpty(targets)) FileWriteText(dir.CombineWithFilePath("Directory.Build.targets"), ""); else CopyFile(targets, dir.CombineWithFilePath("Directory.Build.targets")); return MakeAbsolute(dir); } void ProcessTFMSwitches() { List replaceTarget = new List(); if(HasArgument("android")) replaceTarget.Add("_IncludeAndroid"); if(HasArgument("windows")) replaceTarget.Add("_IncludeWindows"); if(HasArgument("ios")) replaceTarget.Add("_IncludeIos"); if(HasArgument("catalyst") || HasArgument("maccatalyst")) replaceTarget.Add("_IncludeMacCatalyst"); if(HasArgument("tizen")) replaceTarget.Add("_IncludeTizen"); if (replaceTarget.Count > 0) { CopyFile("Directory.Build.Override.props.in", "Directory.Build.Override.props"); foreach(var replaceWith in replaceTarget) { ReplaceTextInFiles("Directory.Build.Override.props", $"<{replaceWith}>", $"<{replaceWith}>true"); } } else { if (FileExists("Directory.Build.Override.props")) DeleteFile("Directory.Build.Override.props"); } }