Build.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  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 Pharmacist.Core;
  20. using static Nuke.Common.EnvironmentInfo;
  21. using static Nuke.Common.IO.FileSystemTasks;
  22. using static Nuke.Common.IO.PathConstruction;
  23. using static Nuke.Common.Tools.MSBuild.MSBuildTasks;
  24. using static Nuke.Common.Tools.DotNet.DotNetTasks;
  25. using static Nuke.Common.Tools.Xunit.XunitTasks;
  26. using static Nuke.Common.Tools.VSWhere.VSWhereTasks;
  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. [Solution("Avalonia.sln")] readonly Solution Solution;
  36. static Lazy<string> MsBuildExe = new Lazy<string>(() =>
  37. {
  38. if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
  39. return null;
  40. var msBuildDirectory = VSWhere("-latest -nologo -property installationPath -format value -prerelease").FirstOrDefault().Text;
  41. if (!string.IsNullOrWhiteSpace(msBuildDirectory))
  42. {
  43. string msBuildExe = Path.Combine(msBuildDirectory, @"MSBuild\Current\Bin\MSBuild.exe");
  44. if (!System.IO.File.Exists(msBuildExe))
  45. msBuildExe = Path.Combine(msBuildDirectory, @"MSBuild\15.0\Bin\MSBuild.exe");
  46. return msBuildExe;
  47. }
  48. return null;
  49. }, false);
  50. BuildParameters Parameters { get; set; }
  51. protected override void OnBuildInitialized()
  52. {
  53. Parameters = new BuildParameters(this);
  54. Information("Building version {0} of Avalonia ({1}) using version {2} of Nuke.",
  55. Parameters.Version,
  56. Parameters.Configuration,
  57. typeof(NukeBuild).Assembly.GetName().Version.ToString());
  58. if (Parameters.IsLocalBuild)
  59. {
  60. Information("Repository Name: " + Parameters.RepositoryName);
  61. Information("Repository Branch: " + Parameters.RepositoryBranch);
  62. }
  63. Information("Configuration: " + Parameters.Configuration);
  64. Information("IsLocalBuild: " + Parameters.IsLocalBuild);
  65. Information("IsRunningOnUnix: " + Parameters.IsRunningOnUnix);
  66. Information("IsRunningOnWindows: " + Parameters.IsRunningOnWindows);
  67. Information("IsRunningOnAzure:" + Parameters.IsRunningOnAzure);
  68. Information("IsPullRequest: " + Parameters.IsPullRequest);
  69. Information("IsMainRepo: " + Parameters.IsMainRepo);
  70. Information("IsMasterBranch: " + Parameters.IsMasterBranch);
  71. Information("IsReleaseBranch: " + Parameters.IsReleaseBranch);
  72. Information("IsReleasable: " + Parameters.IsReleasable);
  73. Information("IsMyGetRelease: " + Parameters.IsMyGetRelease);
  74. Information("IsNuGetRelease: " + Parameters.IsNuGetRelease);
  75. void ExecWait(string preamble, string command, string args)
  76. {
  77. Console.WriteLine(preamble);
  78. Process.Start(new ProcessStartInfo(command, args) {UseShellExecute = false}).WaitForExit();
  79. }
  80. ExecWait("dotnet version:", "dotnet", "--version");
  81. if (Parameters.IsRunningOnUnix)
  82. ExecWait("Mono version:", "mono", "--version");
  83. }
  84. IReadOnlyCollection<Output> MsBuildCommon(
  85. string projectFile,
  86. Configure<MSBuildSettings> configurator = null)
  87. {
  88. return MSBuild(c => c
  89. .SetProjectFile(projectFile)
  90. // This is required for VS2019 image on Azure Pipelines
  91. .When(Parameters.IsRunningOnWindows &&
  92. Parameters.IsRunningOnAzure, _ => _
  93. .AddProperty("JavaSdkDirectory", GetVariable<string>("JAVA_HOME_8_X64")))
  94. .AddProperty("PackageVersion", Parameters.Version)
  95. .AddProperty("iOSRoslynPathHackRequired", true)
  96. .SetToolPath(MsBuildExe.Value)
  97. .SetConfiguration(Parameters.Configuration)
  98. .SetVerbosity(MSBuildVerbosity.Minimal)
  99. .Apply(configurator));
  100. }
  101. Target Clean => _ => _.Executes(() =>
  102. {
  103. Parameters.BuildDirs.ForEach(DeleteDirectory);
  104. Parameters.BuildDirs.ForEach(EnsureCleanDirectory);
  105. EnsureCleanDirectory(Parameters.ArtifactsDir);
  106. EnsureCleanDirectory(Parameters.NugetIntermediateRoot);
  107. EnsureCleanDirectory(Parameters.NugetRoot);
  108. EnsureCleanDirectory(Parameters.ZipRoot);
  109. EnsureCleanDirectory(Parameters.TestResultsRoot);
  110. });
  111. Target CompileHtmlPreviewer => _ => _
  112. .DependsOn(Clean)
  113. .OnlyWhenStatic(() => !Parameters.SkipPreviewer)
  114. .Executes(() =>
  115. {
  116. var webappDir = RootDirectory / "src" / "Avalonia.DesignerSupport" / "Remote" / "HtmlTransport" / "webapp";
  117. NpmTasks.NpmInstall(c => c
  118. .SetWorkingDirectory(webappDir)
  119. .SetArgumentConfigurator(a => a.Add("--silent")));
  120. NpmTasks.NpmRun(c => c
  121. .SetWorkingDirectory(webappDir)
  122. .SetCommand("dist"));
  123. });
  124. Target CompileNative => _ => _
  125. .DependsOn(Clean)
  126. .DependsOn(GenerateCppHeaders)
  127. .OnlyWhenStatic(() => EnvironmentInfo.IsOsx)
  128. .Executes(() =>
  129. {
  130. var project = $"{RootDirectory}/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/";
  131. var args = $"-project {project} -configuration {Parameters.Configuration} CONFIGURATION_BUILD_DIR={RootDirectory}/Build/Products/Release";
  132. ProcessTasks.StartProcess("xcodebuild", args).AssertZeroExitCode();
  133. });
  134. Target Compile => _ => _
  135. .DependsOn(Clean, CompileNative)
  136. .DependsOn(CompileHtmlPreviewer)
  137. .Executes(async () =>
  138. {
  139. if (Parameters.IsRunningOnWindows)
  140. MsBuildCommon(Parameters.MSBuildSolution, c => c
  141. .SetArgumentConfigurator(a => a.Add("/r"))
  142. .AddTargets("Build")
  143. );
  144. else
  145. DotNetBuild(c => c
  146. .SetProjectFile(Parameters.MSBuildSolution)
  147. .AddProperty("PackageVersion", Parameters.Version)
  148. .SetConfiguration(Parameters.Configuration)
  149. );
  150. await CompileReactiveEvents();
  151. });
  152. async Task CompileReactiveEvents()
  153. {
  154. var avaloniaBuildOutput = Path.Combine(RootDirectory, "packages", "Avalonia", "bin", Parameters.Configuration);
  155. var avaloniaAssemblies = GlobFiles(avaloniaBuildOutput, "**/Avalonia*.dll")
  156. .Where(file => !file.Contains("Avalonia.Build.Tasks") &&
  157. !file.Contains("Avalonia.Remote.Protocol"));
  158. var eventsDirectory = GlobDirectories($"{RootDirectory}/src/**/Avalonia.ReactiveUI.Events").First();
  159. var eventsBuildFile = Path.Combine(eventsDirectory, "Events_Avalonia.cs");
  160. if (File.Exists(eventsBuildFile))
  161. File.Delete(eventsBuildFile);
  162. using (var stream = File.Create(eventsBuildFile))
  163. using (var writer = new StreamWriter(stream))
  164. {
  165. await ObservablesForEventGenerator.ExtractEventsFromAssemblies(
  166. writer, avaloniaAssemblies, new string[0], "netstandard2.0"
  167. );
  168. }
  169. var eventsProject = Path.Combine(eventsDirectory, "Avalonia.ReactiveUI.Events.csproj");
  170. if (Parameters.IsRunningOnWindows)
  171. MsBuildCommon(eventsProject, c => c
  172. .SetArgumentConfigurator(a => a.Add("/r"))
  173. .AddTargets("Build")
  174. );
  175. else
  176. DotNetBuild(c => c
  177. .SetProjectFile(eventsProject)
  178. .AddProperty("PackageVersion", Parameters.Version)
  179. .SetConfiguration(Parameters.Configuration)
  180. );
  181. }
  182. void RunCoreTest(string projectName)
  183. {
  184. Information($"Running tests from {projectName}");
  185. var project = Solution.GetProject(projectName).NotNull("project != null");
  186. foreach (var fw in project.GetTargetFrameworks())
  187. {
  188. if (fw.StartsWith("net4")
  189. && RuntimeInformation.IsOSPlatform(OSPlatform.Linux)
  190. && Environment.GetEnvironmentVariable("FORCE_LINUX_TESTS") != "1")
  191. {
  192. Information($"Skipping {projectName} ({fw}) tests on Linux - https://github.com/mono/mono/issues/13969");
  193. continue;
  194. }
  195. Information($"Running for {projectName} ({fw}) ...");
  196. DotNetTest(c => c
  197. .SetProjectFile(project)
  198. .SetConfiguration(Parameters.Configuration)
  199. .SetFramework(fw)
  200. .EnableNoBuild()
  201. .EnableNoRestore()
  202. .When(Parameters.PublishTestResults, _ => _
  203. .SetLogger("trx")
  204. .SetResultsDirectory(Parameters.TestResultsRoot)));
  205. }
  206. }
  207. Target RunHtmlPreviewerTests => _ => _
  208. .DependsOn(CompileHtmlPreviewer)
  209. .OnlyWhenStatic(() => !(Parameters.SkipPreviewer || Parameters.SkipTests))
  210. .Executes(() =>
  211. {
  212. var webappTestDir = RootDirectory / "tests" / "Avalonia.DesignerSupport.Tests" / "Remote" / "HtmlTransport" / "webapp";
  213. NpmTasks.NpmInstall(c => c
  214. .SetWorkingDirectory(webappTestDir)
  215. .SetArgumentConfigurator(a => a.Add("--silent")));
  216. NpmTasks.NpmRun(c => c
  217. .SetWorkingDirectory(webappTestDir)
  218. .SetCommand("test"));
  219. });
  220. Target RunCoreLibsTests => _ => _
  221. .OnlyWhenStatic(() => !Parameters.SkipTests)
  222. .DependsOn(Compile)
  223. .Executes(() =>
  224. {
  225. RunCoreTest("Avalonia.Animation.UnitTests");
  226. RunCoreTest("Avalonia.Base.UnitTests");
  227. RunCoreTest("Avalonia.Controls.UnitTests");
  228. RunCoreTest("Avalonia.Controls.DataGrid.UnitTests");
  229. RunCoreTest("Avalonia.Input.UnitTests");
  230. RunCoreTest("Avalonia.Interactivity.UnitTests");
  231. RunCoreTest("Avalonia.Layout.UnitTests");
  232. RunCoreTest("Avalonia.Markup.UnitTests");
  233. RunCoreTest("Avalonia.Markup.Xaml.UnitTests");
  234. RunCoreTest("Avalonia.Styling.UnitTests");
  235. RunCoreTest("Avalonia.Visuals.UnitTests");
  236. RunCoreTest("Avalonia.Skia.UnitTests");
  237. RunCoreTest("Avalonia.ReactiveUI.UnitTests");
  238. });
  239. Target RunRenderTests => _ => _
  240. .OnlyWhenStatic(() => !Parameters.SkipTests)
  241. .DependsOn(Compile)
  242. .Executes(() =>
  243. {
  244. RunCoreTest("Avalonia.Skia.RenderTests");
  245. if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
  246. RunCoreTest("Avalonia.Direct2D1.RenderTests");
  247. });
  248. Target RunDesignerTests => _ => _
  249. .OnlyWhenStatic(() => !Parameters.SkipTests && Parameters.IsRunningOnWindows)
  250. .DependsOn(Compile)
  251. .Executes(() =>
  252. {
  253. RunCoreTest("Avalonia.DesignerSupport.Tests");
  254. });
  255. [PackageExecutable("JetBrains.dotMemoryUnit", "dotMemoryUnit.exe")] readonly Tool DotMemoryUnit;
  256. Target RunLeakTests => _ => _
  257. .OnlyWhenStatic(() => !Parameters.SkipTests && Parameters.IsRunningOnWindows)
  258. .DependsOn(Compile)
  259. .Executes(() =>
  260. {
  261. var testAssembly = "tests\\Avalonia.LeakTests\\bin\\Release\\net461\\Avalonia.LeakTests.dll";
  262. DotMemoryUnit(
  263. $"{XunitPath.DoubleQuoteIfNeeded()} --propagate-exit-code -- {testAssembly}",
  264. timeout: 120_000);
  265. });
  266. Target ZipFiles => _ => _
  267. .After(CreateNugetPackages, Compile, RunCoreLibsTests, Package)
  268. .Executes(() =>
  269. {
  270. var data = Parameters;
  271. Zip(data.ZipCoreArtifacts, data.BinRoot);
  272. Zip(data.ZipNuGetArtifacts, data.NugetRoot);
  273. Zip(data.ZipTargetControlCatalogDesktopDir,
  274. GlobFiles(data.ZipSourceControlCatalogDesktopDir, "*.dll").Concat(
  275. GlobFiles(data.ZipSourceControlCatalogDesktopDir, "*.config")).Concat(
  276. GlobFiles(data.ZipSourceControlCatalogDesktopDir, "*.so")).Concat(
  277. GlobFiles(data.ZipSourceControlCatalogDesktopDir, "*.dylib")).Concat(
  278. GlobFiles(data.ZipSourceControlCatalogDesktopDir, "*.exe")));
  279. });
  280. Target CreateIntermediateNugetPackages => _ => _
  281. .DependsOn(Compile)
  282. .After(RunTests)
  283. .Executes(() =>
  284. {
  285. if (Parameters.IsRunningOnWindows)
  286. MsBuildCommon(Parameters.MSBuildSolution, c => c
  287. .AddTargets("Pack"));
  288. else
  289. DotNetPack(c => c
  290. .SetProject(Parameters.MSBuildSolution)
  291. .SetConfiguration(Parameters.Configuration)
  292. .AddProperty("PackageVersion", Parameters.Version));
  293. });
  294. Target CreateNugetPackages => _ => _
  295. .DependsOn(CreateIntermediateNugetPackages)
  296. .Executes(() =>
  297. {
  298. BuildTasksPatcher.PatchBuildTasksInPackage(Parameters.NugetIntermediateRoot / "Avalonia.Build.Tasks." +
  299. Parameters.Version + ".nupkg");
  300. var config = Numerge.MergeConfiguration.LoadFile(RootDirectory / "nukebuild" / "numerge.config");
  301. EnsureCleanDirectory(Parameters.NugetRoot);
  302. if(!Numerge.NugetPackageMerger.Merge(Parameters.NugetIntermediateRoot, Parameters.NugetRoot, config,
  303. new NumergeNukeLogger()))
  304. throw new Exception("Package merge failed");
  305. });
  306. Target RunTests => _ => _
  307. .DependsOn(RunCoreLibsTests)
  308. .DependsOn(RunRenderTests)
  309. .DependsOn(RunDesignerTests)
  310. .DependsOn(RunHtmlPreviewerTests)
  311. .DependsOn(RunLeakTests);
  312. Target Package => _ => _
  313. .DependsOn(RunTests)
  314. .DependsOn(CreateNugetPackages);
  315. Target CiAzureLinux => _ => _
  316. .DependsOn(RunTests);
  317. Target CiAzureOSX => _ => _
  318. .DependsOn(Package)
  319. .DependsOn(ZipFiles);
  320. Target CiAzureWindows => _ => _
  321. .DependsOn(Package)
  322. .DependsOn(ZipFiles);
  323. public static int Main() =>
  324. RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
  325. ? Execute<Build>(x => x.Package)
  326. : Execute<Build>(x => x.RunTests);
  327. }
  328. public static class ToolSettingsExtensions
  329. {
  330. public static T Apply<T>(this T settings, Configure<T> configurator)
  331. {
  332. return configurator != null ? configurator(settings) : settings;
  333. }
  334. }