Build.cs 13 KB

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