Build.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Runtime.InteropServices;
  7. using System.Threading;
  8. using System.Threading.Tasks;
  9. using System.Xml.Linq;
  10. using Nuke.Common;
  11. using Nuke.Common.Git;
  12. using Nuke.Common.ProjectModel;
  13. using Nuke.Common.Tooling;
  14. using Nuke.Common.Tools.DotNet;
  15. using Nuke.Common.Tools.MSBuild;
  16. using Nuke.Common.Tools.Npm;
  17. using Nuke.Common.Utilities;
  18. using Nuke.Common.Utilities.Collections;
  19. using static Nuke.Common.EnvironmentInfo;
  20. using static Nuke.Common.IO.FileSystemTasks;
  21. using static Nuke.Common.IO.PathConstruction;
  22. using static Nuke.Common.Tools.MSBuild.MSBuildTasks;
  23. using static Nuke.Common.Tools.DotNet.DotNetTasks;
  24. using static Nuke.Common.Tools.Xunit.XunitTasks;
  25. using static Nuke.Common.Tools.VSWhere.VSWhereTasks;
  26. using MicroCom.CodeGenerator;
  27. /*
  28. Before editing this file, install support plugin for your IDE,
  29. running and debugging a particular target (optionally without deps) would be way easier
  30. ReSharper/Rider - https://plugins.jetbrains.com/plugin/10803-nuke-support
  31. VSCode - https://marketplace.visualstudio.com/items?itemName=nuke.support
  32. */
  33. partial class Build : NukeBuild
  34. {
  35. BuildParameters Parameters { get; set; }
  36. protected override void OnBuildInitialized()
  37. {
  38. Parameters = new BuildParameters(this);
  39. Information("Building version {0} of Avalonia ({1}) using version {2} of Nuke.",
  40. Parameters.Version,
  41. Parameters.Configuration,
  42. typeof(NukeBuild).Assembly.GetName().Version.ToString());
  43. if (Parameters.IsLocalBuild)
  44. {
  45. Information("Repository Name: " + Parameters.RepositoryName);
  46. Information("Repository Branch: " + Parameters.RepositoryBranch);
  47. }
  48. Information("Configuration: " + Parameters.Configuration);
  49. Information("IsLocalBuild: " + Parameters.IsLocalBuild);
  50. Information("IsRunningOnUnix: " + Parameters.IsRunningOnUnix);
  51. Information("IsRunningOnWindows: " + Parameters.IsRunningOnWindows);
  52. Information("IsRunningOnAzure:" + Parameters.IsRunningOnAzure);
  53. Information("IsPullRequest: " + Parameters.IsPullRequest);
  54. Information("IsMainRepo: " + Parameters.IsMainRepo);
  55. Information("IsMasterBranch: " + Parameters.IsMasterBranch);
  56. Information("IsReleaseBranch: " + Parameters.IsReleaseBranch);
  57. Information("IsReleasable: " + Parameters.IsReleasable);
  58. Information("IsMyGetRelease: " + Parameters.IsMyGetRelease);
  59. Information("IsNuGetRelease: " + Parameters.IsNuGetRelease);
  60. void ExecWait(string preamble, string command, string args)
  61. {
  62. Console.WriteLine(preamble);
  63. Process.Start(new ProcessStartInfo(command, args) {UseShellExecute = false}).WaitForExit();
  64. }
  65. ExecWait("dotnet version:", "dotnet", "--info");
  66. ExecWait("dotnet workloads:", "dotnet", "workload list");
  67. Information("Processor count: " + Environment.ProcessorCount);
  68. Information("Available RAM: " + GC.GetGCMemoryInfo().TotalAvailableMemoryBytes / 0x100000 + "MB");
  69. }
  70. DotNetConfigHelper ApplySettingCore(DotNetConfigHelper c)
  71. {
  72. if (Parameters.IsRunningOnAzure)
  73. c.AddProperty("JavaSdkDirectory", GetVariable<string>("JAVA_HOME_11_X64"));
  74. c.AddProperty("PackageVersion", Parameters.Version)
  75. .SetConfiguration(Parameters.Configuration)
  76. .SetVerbosity(DotNetVerbosity.Minimal);
  77. return c;
  78. }
  79. DotNetBuildSettings ApplySetting(DotNetBuildSettings c, Configure<DotNetBuildSettings> configurator = null) =>
  80. ApplySettingCore(c).Build.Apply(configurator);
  81. DotNetPackSettings ApplySetting(DotNetPackSettings c, Configure<DotNetPackSettings> configurator = null) =>
  82. ApplySettingCore(c).Pack.Apply(configurator);
  83. DotNetTestSettings ApplySetting(DotNetTestSettings c, Configure<DotNetTestSettings> configurator = null) =>
  84. ApplySettingCore(c).Test.Apply(configurator);
  85. Target Clean => _ => _.Executes(() =>
  86. {
  87. Parameters.BuildDirs.ForEach(DeleteDirectory);
  88. Parameters.BuildDirs.ForEach(EnsureCleanDirectory);
  89. EnsureCleanDirectory(Parameters.ArtifactsDir);
  90. EnsureCleanDirectory(Parameters.NugetIntermediateRoot);
  91. EnsureCleanDirectory(Parameters.NugetRoot);
  92. EnsureCleanDirectory(Parameters.ZipRoot);
  93. EnsureCleanDirectory(Parameters.TestResultsRoot);
  94. });
  95. Target CompileHtmlPreviewer => _ => _
  96. .DependsOn(Clean)
  97. .OnlyWhenStatic(() => !Parameters.SkipPreviewer)
  98. .Executes(() =>
  99. {
  100. var webappDir = RootDirectory / "src" / "Avalonia.DesignerSupport" / "Remote" / "HtmlTransport" / "webapp";
  101. NpmTasks.NpmInstall(c => c
  102. .SetProcessWorkingDirectory(webappDir)
  103. .SetProcessArgumentConfigurator(a => a.Add("--silent")));
  104. NpmTasks.NpmRun(c => c
  105. .SetProcessWorkingDirectory(webappDir)
  106. .SetCommand("dist"));
  107. });
  108. Target CompileNative => _ => _
  109. .DependsOn(Clean)
  110. .DependsOn(GenerateCppHeaders)
  111. .OnlyWhenStatic(() => EnvironmentInfo.IsOsx)
  112. .Executes(() =>
  113. {
  114. var project = $"{RootDirectory}/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/";
  115. var args = $"-project {project} -configuration {Parameters.Configuration} CONFIGURATION_BUILD_DIR={RootDirectory}/Build/Products/Release";
  116. ProcessTasks.StartProcess("xcodebuild", args).AssertZeroExitCode();
  117. });
  118. Target Compile => _ => _
  119. .DependsOn(Clean, CompileNative)
  120. .DependsOn(CompileHtmlPreviewer)
  121. .Executes(() =>
  122. {
  123. DotNetBuild(c => ApplySetting(c)
  124. .SetProjectFile(Parameters.MSBuildSolution)
  125. );
  126. });
  127. void RunCoreTest(string projectName)
  128. {
  129. Information($"Running tests from {projectName}");
  130. var project = RootDirectory.GlobFiles(@$"**\{projectName}.csproj").FirstOrDefault()
  131. ?? throw new InvalidOperationException($"Project {projectName} doesn't exist");
  132. // Nuke and MSBuild tools have build-in helpers to get target frameworks from the project.
  133. // Unfortunately, it gets broken with every second SDK update, so we had to do it manually.
  134. var fileXml = XDocument.Parse(File.ReadAllText(project));
  135. var targetFrameworks = fileXml.Descendants("TargetFrameworks")
  136. .FirstOrDefault()?.Value.Split(';').Select(f => f.Trim());
  137. if (targetFrameworks is null)
  138. {
  139. var targetFramework = fileXml.Descendants("TargetFramework").FirstOrDefault()?.Value;
  140. if (targetFramework is not null)
  141. {
  142. targetFrameworks = new[] { targetFramework };
  143. }
  144. }
  145. if (targetFrameworks is null)
  146. {
  147. throw new InvalidOperationException("No target frameworks were found in the test project");
  148. }
  149. foreach (var fw in targetFrameworks)
  150. {
  151. if (fw.StartsWith("net4")
  152. && (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
  153. && Environment.GetEnvironmentVariable("FORCE_LINUX_TESTS") != "1")
  154. {
  155. Information($"Skipping {projectName} ({fw}) tests on *nix - https://github.com/mono/mono/issues/13969");
  156. continue;
  157. }
  158. Information($"Running for {projectName} ({fw}) ...");
  159. DotNetTest(c => ApplySetting(c)
  160. .SetProjectFile(project)
  161. .SetFramework(fw)
  162. .EnableNoBuild()
  163. .EnableNoRestore()
  164. .When(Parameters.PublishTestResults, _ => _
  165. .SetLoggers("trx")
  166. .SetResultsDirectory(Parameters.TestResultsRoot)));
  167. }
  168. }
  169. Target RunHtmlPreviewerTests => _ => _
  170. .DependsOn(CompileHtmlPreviewer)
  171. .OnlyWhenStatic(() => !(Parameters.SkipPreviewer || Parameters.SkipTests))
  172. .Executes(() =>
  173. {
  174. var webappTestDir = RootDirectory / "tests" / "Avalonia.DesignerSupport.Tests" / "Remote" / "HtmlTransport" / "webapp";
  175. NpmTasks.NpmInstall(c => c
  176. .SetProcessWorkingDirectory(webappTestDir)
  177. .SetProcessArgumentConfigurator(a => a.Add("--silent")));
  178. NpmTasks.NpmRun(c => c
  179. .SetProcessWorkingDirectory(webappTestDir)
  180. .SetCommand("test"));
  181. });
  182. Target RunCoreLibsTests => _ => _
  183. .OnlyWhenStatic(() => !Parameters.SkipTests)
  184. .DependsOn(Compile)
  185. .Executes(() =>
  186. {
  187. RunCoreTest("Avalonia.Base.UnitTests");
  188. RunCoreTest("Avalonia.Controls.UnitTests");
  189. RunCoreTest("Avalonia.Controls.DataGrid.UnitTests");
  190. RunCoreTest("Avalonia.Markup.UnitTests");
  191. RunCoreTest("Avalonia.Markup.Xaml.UnitTests");
  192. RunCoreTest("Avalonia.Skia.UnitTests");
  193. RunCoreTest("Avalonia.ReactiveUI.UnitTests");
  194. RunCoreTest("Avalonia.Headless.NUnit.UnitTests");
  195. RunCoreTest("Avalonia.Headless.XUnit.UnitTests");
  196. });
  197. Target RunRenderTests => _ => _
  198. .OnlyWhenStatic(() => !Parameters.SkipTests)
  199. .DependsOn(Compile)
  200. .Executes(() =>
  201. {
  202. RunCoreTest("Avalonia.Skia.RenderTests");
  203. if (Parameters.IsRunningOnWindows)
  204. RunCoreTest("Avalonia.Direct2D1.RenderTests");
  205. });
  206. Target RunToolsTests => _ => _
  207. .OnlyWhenStatic(() => !Parameters.SkipTests)
  208. .DependsOn(Compile)
  209. .Executes(() =>
  210. {
  211. RunCoreTest("Avalonia.Generators.Tests");
  212. if (Parameters.IsRunningOnWindows)
  213. RunCoreTest("Avalonia.DesignerSupport.Tests");
  214. });
  215. Target RunLeakTests => _ => _
  216. .OnlyWhenStatic(() => !Parameters.SkipTests && Parameters.IsRunningOnWindows)
  217. .DependsOn(Compile)
  218. .Executes(() =>
  219. {
  220. void DoMemoryTest()
  221. {
  222. RunCoreTest("Avalonia.LeakTests");
  223. }
  224. ControlFlow.ExecuteWithRetry(DoMemoryTest, delay: TimeSpan.FromMilliseconds(3));
  225. });
  226. Target ZipFiles => _ => _
  227. .After(CreateNugetPackages, Compile, RunCoreLibsTests, Package)
  228. .Executes(() =>
  229. {
  230. var data = Parameters;
  231. Zip(data.ZipNuGetArtifacts, data.NugetRoot);
  232. });
  233. Target CreateIntermediateNugetPackages => _ => _
  234. .DependsOn(Compile)
  235. .After(RunTests)
  236. .Executes(() =>
  237. {
  238. DotNetPack(c => ApplySetting(c).SetProject(Parameters.MSBuildSolution));
  239. });
  240. Target CreateNugetPackages => _ => _
  241. .DependsOn(CreateIntermediateNugetPackages)
  242. .Executes(() =>
  243. {
  244. BuildTasksPatcher.PatchBuildTasksInPackage(Parameters.NugetIntermediateRoot / "Avalonia.Build.Tasks." +
  245. Parameters.Version + ".nupkg");
  246. var config = Numerge.MergeConfiguration.LoadFile(RootDirectory / "nukebuild" / "numerge.config");
  247. EnsureCleanDirectory(Parameters.NugetRoot);
  248. if(!Numerge.NugetPackageMerger.Merge(Parameters.NugetIntermediateRoot, Parameters.NugetRoot, config,
  249. new NumergeNukeLogger()))
  250. throw new Exception("Package merge failed");
  251. RefAssemblyGenerator.GenerateRefAsmsInPackage(Parameters.NugetRoot / "Avalonia." +
  252. Parameters.Version + ".nupkg");
  253. });
  254. Target RunTests => _ => _
  255. .DependsOn(RunCoreLibsTests)
  256. .DependsOn(RunRenderTests)
  257. .DependsOn(RunToolsTests)
  258. .DependsOn(RunHtmlPreviewerTests)
  259. .DependsOn(RunLeakTests);
  260. Target Package => _ => _
  261. .DependsOn(RunTests)
  262. .DependsOn(CreateNugetPackages);
  263. Target CiAzureLinux => _ => _
  264. .DependsOn(RunTests);
  265. Target CiAzureOSX => _ => _
  266. .DependsOn(Package)
  267. .DependsOn(ZipFiles);
  268. Target CiAzureWindows => _ => _
  269. .DependsOn(Package)
  270. .DependsOn(ZipFiles);
  271. Target GenerateCppHeaders => _ => _.Executes(() =>
  272. {
  273. var file = MicroComCodeGenerator.Parse(
  274. File.ReadAllText(RootDirectory / "src" / "Avalonia.Native" / "avn.idl"));
  275. File.WriteAllText(RootDirectory / "native" / "Avalonia.Native" / "inc" / "avalonia-native.h",
  276. file.GenerateCppHeader());
  277. });
  278. public static int Main() =>
  279. RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
  280. ? Execute<Build>(x => x.RunToolsTests)
  281. : Execute<Build>(x => x.RunTests);
  282. }
  283. public static class ToolSettingsExtensions
  284. {
  285. public static T Apply<T>(this T settings, Configure<T> configurator)
  286. {
  287. return configurator != null ? configurator(settings) : settings;
  288. }
  289. }