build.ps1 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. #requires -version 5
  2. <#
  3. .SYNOPSIS
  4. Builds this repository.
  5. .DESCRIPTION
  6. This build script installs required tools and runs an MSBuild command on this repository.
  7. This script can be used to invoke various targets, such as targets to produce packages,
  8. build projects, run tests, and generate code.
  9. .PARAMETER CI
  10. Sets up CI specific settings and variables.
  11. .PARAMETER Restore
  12. Run restore.
  13. .PARAMETER NoRestore
  14. Suppress running restore on projects.
  15. .PARAMETER NoBuild
  16. Suppress re-compile projects. (Implies -NoRestore)
  17. .PARAMETER NoBuildDeps
  18. Do not build project-to-project references and only build the specified project.
  19. .PARAMETER NoBuildRepoTasks
  20. Skip building eng/tools/RepoTasks/
  21. .PARAMETER Pack
  22. Produce packages.
  23. .PARAMETER Test
  24. Run tests.
  25. .PARAMETER Sign
  26. Run code signing.
  27. .PARAMETER Configuration
  28. Debug or Release
  29. .PARAMETER Architecture
  30. The CPU architecture to build for (x64, x86, arm). Default=x64
  31. .PARAMETER Projects
  32. A list of projects to build. Globbing patterns are supported, such as "$(pwd)/**/*.csproj"
  33. .PARAMETER All
  34. Build all project types.
  35. .PARAMETER BuildManaged
  36. Build managed projects (C#, F#, VB).
  37. You can also use -NoBuildManaged to suppress this project type.
  38. .PARAMETER BuildNative
  39. Build native projects (C++).
  40. This is the default for x64 and x86 builds but useful when you want to build _only_ native projects.
  41. You can use -NoBuildNative to suppress this project type.
  42. .PARAMETER BuildNodeJS
  43. Build NodeJS projects (TypeScript, JS).
  44. You can also use -NoBuildNodeJS to suppress this project type.
  45. .PARAMETER BuildJava
  46. Build Java projects.
  47. You can also use -NoBuildJava to suppress this project type.
  48. .PARAMETER BuildInstallers
  49. Build Windows Installers. Required .NET 3.5 to be installed (WiX toolset requirement).
  50. You can also use -NoBuildInstallers to suppress this project type.
  51. .PARAMETER BinaryLog
  52. Enable the binary logger
  53. .PARAMETER ExcludeCIBinarylog
  54. Don't output binary log by default in CI builds (short: -nobl).
  55. .PARAMETER Verbosity
  56. MSBuild verbosity: q[uiet], m[inimal], n[ormal], d[etailed], and diag[nostic]
  57. .PARAMETER MSBuildArguments
  58. Additional MSBuild arguments to be passed through.
  59. .PARAMETER RuntimeSourceFeed
  60. Additional feed that can be used when downloading .NET runtimes and SDKs
  61. .PARAMETER RuntimeSourceFeedKey
  62. Key for feed that can be used when downloading .NET runtimes and SDKs
  63. .EXAMPLE
  64. Building both native and managed projects.
  65. build.ps1
  66. or
  67. build.ps1 -BuildManaged
  68. or
  69. build.ps1 -BuildManaged -BuildNative
  70. .EXAMPLE
  71. Build only native projects.
  72. build.ps1 -BuildNative
  73. .EXAMPLE
  74. Building a subfolder of code.
  75. build.ps1 -projects "$(pwd)/src/SomeFolder/**/*.csproj"
  76. .EXAMPLE
  77. Running tests.
  78. build.ps1 -test
  79. .LINK
  80. Online version: https://github.com/dotnet/aspnetcore/blob/main/docs/BuildFromSource.md
  81. #>
  82. [CmdletBinding(PositionalBinding = $false, DefaultParameterSetName='Groups')]
  83. param(
  84. [switch]$CI,
  85. # Build lifecycle options
  86. [switch]$Restore,
  87. [switch]$NoRestore, # Suppress restore
  88. [switch]$NoBuild, # Suppress compiling
  89. [switch]$NoBuildDeps, # Suppress project to project dependencies
  90. [switch]$Pack, # Produce packages
  91. [switch]$Test, # Run tests
  92. [switch]$Sign, # Code sign
  93. [Alias('c')]
  94. [ValidateSet('Debug', 'Release')]
  95. $Configuration,
  96. [ValidateSet('x64', 'x86', 'arm', 'arm64')]
  97. $Architecture = 'x64',
  98. # A list of projects which should be built.
  99. [string]$Projects,
  100. # Project selection
  101. [switch]$All, # Build everything
  102. # Build a specified set of project groups
  103. [switch]$BuildManaged,
  104. [switch]$BuildNative,
  105. [switch]$BuildNodeJS,
  106. [switch]$BuildJava,
  107. [switch]$BuildInstallers,
  108. # Inverse of the previous switches because specifying '-switch:$false' is not intuitive for most command line users
  109. [switch]$NoBuildManaged,
  110. [switch]$NoBuildNative,
  111. [switch]$NoBuildNodeJS,
  112. [switch]$NoBuildJava,
  113. [switch]$NoBuildInstallers,
  114. [switch]$NoBuildRepoTasks,
  115. # Diagnostics
  116. [Alias('bl')]
  117. [switch]$BinaryLog,
  118. [Alias('nobl')]
  119. [switch]$ExcludeCIBinarylog,
  120. [Alias('v')]
  121. [string]$Verbosity = 'minimal',
  122. [switch]$DumpProcesses, # Capture all running processes and dump them to a file.
  123. # Other lifecycle targets
  124. [switch]$Help, # Show help
  125. # Optional arguments that enable downloading an internal
  126. # runtime or runtime from a non-default location
  127. [Alias('DotNetRuntimeSourceFeed')]
  128. [string]$RuntimeSourceFeed,
  129. [Alias('DotNetRuntimeSourceFeedKey')]
  130. [string]$RuntimeSourceFeedKey,
  131. # Capture the rest
  132. [Parameter(ValueFromRemainingArguments = $true)]
  133. [string[]]$MSBuildArguments
  134. )
  135. Set-StrictMode -Version 2
  136. $ErrorActionPreference = 'Stop'
  137. if ($Help) {
  138. Get-Help $PSCommandPath
  139. exit 1
  140. }
  141. if ($DumpProcesses -or $CI) {
  142. # Dump running processes
  143. Start-Job -Name DumpProcesses -FilePath $PSScriptRoot\scripts\dump_process.ps1 -ArgumentList $PSScriptRoot
  144. }
  145. # Project selection
  146. if ($Projects) {
  147. if (![System.IO.Path]::IsPathRooted($Projects))
  148. {
  149. $Projects = Join-Path (Get-Location) $Projects
  150. }
  151. }
  152. # When adding new sub-group build flags, add them to this check.
  153. elseif (-not ($All -or $BuildNative -or $BuildManaged -or $BuildNodeJS -or $BuildInstallers -or $BuildJava)) {
  154. Write-Warning "No default group of projects was specified, so building the managed and native projects and their dependencies. Run ``build.cmd -help`` for more details."
  155. # The goal of this is to pick a sensible default for `build.cmd` with zero arguments.
  156. $BuildManaged = $true
  157. }
  158. if ($BuildManaged -or ($All -and (-not $NoBuildManaged))) {
  159. if (-not ($BuildNodeJS -or $NoBuildNodeJS)) {
  160. $node = Get-Command node -ErrorAction Ignore -CommandType Application
  161. if ($node) {
  162. $nodeHome = Split-Path -Parent (Split-Path -Parent $node.Path)
  163. Write-Host -f Magenta "Building of C# project is enabled and has dependencies on NodeJS projects. Building of NodeJS projects is enabled since node is detected in $nodeHome."
  164. $BuildNodeJS = $true
  165. }
  166. else {
  167. Write-Host -f Magenta "Building of NodeJS projects is disabled since node is not detected on Path and no BuildNodeJs or NoBuildNodeJs setting is set explicitly."
  168. $NoBuildNodeJS = $true
  169. }
  170. }
  171. if ($NoBuildNodeJS){
  172. Write-Warning "Some managed projects depend on NodeJS projects. Building NodeJS is disabled so the managed projects will fallback to using the output from previous builds. The output may not be correct or up to date."
  173. }
  174. }
  175. if ($NoBuildDeps) { $MSBuildArguments += "/p:BuildProjectReferences=false" }
  176. $RunBuild = if ($NoBuild) { $false } else { $true }
  177. # Run restore by default unless -NoRestore is set.
  178. # -NoBuild implies -NoRestore, unless -Restore is explicitly set (as in restore.cmd)
  179. $RunRestore = if ($NoRestore) { $false }
  180. elseif ($Restore) { $true }
  181. elseif ($NoBuild) { $false }
  182. else { $true }
  183. # Target selection
  184. $MSBuildArguments += "/p:Restore=$RunRestore"
  185. $MSBuildArguments += "/p:Build=$RunBuild"
  186. if (-not $RunBuild) { $MSBuildArguments += "/p:NoBuild=true" }
  187. $MSBuildArguments += "/p:Pack=$Pack"
  188. $MSBuildArguments += "/p:Test=$Test"
  189. $MSBuildArguments += "/p:Sign=$Sign"
  190. $MSBuildArguments += "/p:TargetArchitecture=$Architecture"
  191. $MSBuildArguments += "/p:TargetOsName=win"
  192. if (-not $Configuration) {
  193. $Configuration = if ($CI) { 'Release' } else { 'Debug' }
  194. }
  195. $MSBuildArguments += "/p:Configuration=$Configuration"
  196. [string[]]$ToolsetBuildArguments = @()
  197. if ($RuntimeSourceFeed -or $RuntimeSourceFeedKey) {
  198. $runtimeFeedArg = "/p:DotNetRuntimeSourceFeed=$RuntimeSourceFeed"
  199. $runtimeFeedKeyArg = "/p:DotNetRuntimeSourceFeedKey=$RuntimeSourceFeedKey"
  200. $MSBuildArguments += $runtimeFeedArg
  201. $MSBuildArguments += $runtimeFeedKeyArg
  202. $ToolsetBuildArguments += $runtimeFeedArg
  203. $ToolsetBuildArguments += $runtimeFeedKeyArg
  204. }
  205. # Split build categories between dotnet msbuild and desktop msbuild. Use desktop msbuild as little as possible.
  206. [string[]]$dotnetBuildArguments = $MSBuildArguments
  207. if ($All) { $dotnetBuildArguments += '/p:BuildAllProjects=true' }
  208. if ($Projects) {
  209. if ($BuildNative) {
  210. $MSBuildArguments += "/p:ProjectToBuild=$Projects"
  211. } else {
  212. $dotnetBuildArguments += "/p:ProjectToBuild=$Projects"
  213. }
  214. }
  215. if ($NoBuildInstallers) { $MSBuildArguments += "/p:BuildInstallers=false"; $BuildInstallers = $false }
  216. if ($BuildInstallers) { $MSBuildArguments += "/p:BuildInstallers=true" }
  217. # Build native projects by default unless -NoBuildNative was specified.
  218. $specifiedBuildNative = $BuildNative
  219. $BuildNative = $true
  220. if ($NoBuildNative) { $MSBuildArguments += "/p:BuildNative=false"; $BuildNative = $false }
  221. if ($BuildNative) { $MSBuildArguments += "/p:BuildNative=true"}
  222. if ($NoBuildJava) { $dotnetBuildArguments += "/p:BuildJava=false"; $BuildJava = $false }
  223. if ($BuildJava) { $dotnetBuildArguments += "/p:BuildJava=true" }
  224. if ($NoBuildManaged) { $dotnetBuildArguments += "/p:BuildManaged=false"; $BuildManaged = $false }
  225. if ($BuildManaged) { $dotnetBuildArguments += "/p:BuildManaged=true" }
  226. if ($NoBuildNodeJS) { $dotnetBuildArguments += "/p:BuildNodeJS=false"; $BuildNodeJS = $false }
  227. if ($BuildNodeJS) { $dotnetBuildArguments += "/p:BuildNodeJS=true" }
  228. # Don't bother with two builds if just one will build everything. Ignore super-weird cases like
  229. # "-Projects ... -NoBuildJava -NoBuildManaged -NoBuildNodeJS". An empty `./build.ps1` command will build both
  230. # managed and native projects.
  231. $performDesktopBuild = ($BuildInstallers -and $Architecture -ne "arm") -or `
  232. ($BuildNative -and -not $Architecture.StartsWith("arm", [System.StringComparison]::OrdinalIgnoreCase))
  233. $performDotnetBuild = $BuildJava -or $BuildManaged -or $BuildNodeJS -or `
  234. ($All -and -not ($NoBuildJava -and $NoBuildManaged -and $NoBuildNodeJS)) -or `
  235. ($Projects -and -not ($BuildInstallers -or $specifiedBuildNative))
  236. $foundJdk = $false
  237. $javac = Get-Command javac -ErrorAction Ignore -CommandType Application
  238. $localJdkPath = "$PSScriptRoot\..\.tools\jdk\win-x64\"
  239. if (Test-Path "$localJdkPath\bin\javac.exe") {
  240. $foundJdk = $true
  241. Write-Host -f Magenta "Detected JDK in $localJdkPath (via local repo convention)"
  242. $env:JAVA_HOME = $localJdkPath
  243. }
  244. elseif ($env:JAVA_HOME) {
  245. if (-not (Test-Path "${env:JAVA_HOME}\bin\javac.exe")) {
  246. Write-Error "The environment variable JAVA_HOME was set, but ${env:JAVA_HOME}\bin\javac.exe does not exist. Remove JAVA_HOME or update it to the correct location for the JDK. See https://www.bing.com/search?q=java_home for details."
  247. }
  248. else {
  249. Write-Host -f Magenta "Detected JDK in ${env:JAVA_HOME} (via JAVA_HOME)"
  250. $foundJdk = $true
  251. }
  252. }
  253. elseif ($javac) {
  254. $foundJdk = $true
  255. $javaHome = Split-Path -Parent (Split-Path -Parent $javac.Path)
  256. $env:JAVA_HOME = $javaHome
  257. Write-Host -f Magenta "Detected JDK in $javaHome (via PATH)"
  258. }
  259. else {
  260. try {
  261. $jdkRegistryKeys = @(
  262. "HKLM:\SOFTWARE\JavaSoft\JDK", # for JDK 10+
  263. "HKLM:\SOFTWARE\JavaSoft\Java Development Kit" # fallback for JDK 8
  264. )
  265. $jdkRegistryKey = $jdkRegistryKeys | Where-Object { Test-Path $_ } | Select-Object -First 1
  266. if ($jdkRegistryKey) {
  267. $jdkVersion = (Get-Item $jdkRegistryKey | Get-ItemProperty -name CurrentVersion).CurrentVersion
  268. $javaHome = (Get-Item $jdkRegistryKey\$jdkVersion | Get-ItemProperty -Name JavaHome).JavaHome
  269. if (Test-Path "${javaHome}\bin\javac.exe") {
  270. $env:JAVA_HOME = $javaHome
  271. Write-Host -f Magenta "Detected JDK $jdkVersion in $env:JAVA_HOME (via registry)"
  272. $foundJdk = $true
  273. }
  274. }
  275. }
  276. catch {
  277. Write-Verbose "Failed to detect Java: $_"
  278. }
  279. }
  280. if ($env:PATH -notlike "*${env:JAVA_HOME}*") {
  281. $env:PATH = "$(Join-Path $env:JAVA_HOME bin);${env:PATH}"
  282. }
  283. if (-not $foundJdk -and $RunBuild -and ($All -or $BuildJava) -and -not $NoBuildJava) {
  284. Write-Error "Could not find the JDK. Either run $PSScriptRoot\scripts\InstallJdk.ps1 to install for this repo, or install the JDK globally on your machine (see $PSScriptRoot\..\docs\BuildFromSource.md for details)."
  285. }
  286. # Initialize global variables need to be set before the import of Arcade is imported
  287. $restore = $RunRestore
  288. # Though VS Code may indicate $nodeReuse and $msbuildEngine are unused, tools.ps1 uses them.
  289. # Disable node reuse - Workaround perpetual issues in node reuse and custom task assemblies
  290. $nodeReuse = $false
  291. $env:MSBUILDDISABLENODEREUSE=1
  292. # Use `dotnet msbuild` by default
  293. $msbuildEngine = 'dotnet'
  294. # Ensure passing neither -bl nor -nobl on CI avoids errors in tools.ps1. This is needed because both parameters are
  295. # $false by default i.e. they always exist. (We currently avoid binary logs but that is made visible in the YAML.)
  296. if ($CI -and -not $excludeCIBinarylog) {
  297. $binaryLog = $true
  298. }
  299. # tools.ps1 corrupts global state, so reset these values in case they carried over from a previous build
  300. Remove-Item variable:global:_BuildTool -ea Ignore
  301. Remove-Item variable:global:_DotNetInstallDir -ea Ignore
  302. Remove-Item variable:global:_ToolsetBuildProj -ea Ignore
  303. Remove-Item variable:global:_MSBuildExe -ea Ignore
  304. # Import Arcade
  305. . "$PSScriptRoot/common/tools.ps1"
  306. # Add default .binlog location if not already on the command line. tools.ps1 does not handle this; it just checks
  307. # $BinaryLog, $CI and $ExcludeCIBinarylog values for an error case. But tools.ps1 provides a nice function to help.
  308. if ($BinaryLog) {
  309. $bl = GetMSBuildBinaryLogCommandLineArgument($MSBuildArguments)
  310. if (-not $bl) {
  311. $dotnetBuildArguments += "/bl:" + (Join-Path $LogDir "Build.binlog")
  312. $MSBuildArguments += "/bl:" + (Join-Path $LogDir "Build.native.binlog")
  313. $ToolsetBuildArguments += "/bl:" + (Join-Path $LogDir "Build.repotasks.binlog")
  314. } else {
  315. # Use a different binary log path when running desktop msbuild if doing both builds.
  316. if ($performDesktopBuild -and $performDotnetBuild) {
  317. $MSBuildArguments += "/bl:" + [System.IO.Path]::ChangeExtension($bl, "native.binlog")
  318. }
  319. $ToolsetBuildArguments += "/bl:" + [System.IO.Path]::ChangeExtension($bl, "repotasks.binlog")
  320. }
  321. } elseif ($CI) {
  322. # Ensure the artifacts/log directory isn't empty to avoid warnings.
  323. New-Item (Join-Path $LogDir "empty.log") -ItemType File -ErrorAction SilentlyContinue >$null
  324. }
  325. # Capture MSBuild crash logs
  326. $env:MSBUILDDEBUGPATH = $LogDir
  327. $local:exit_code = $null
  328. try {
  329. # Set this global property so Arcade will always initialize the toolset. The error message you get when you build on a clean machine
  330. # with -norestore is not obvious about what to do to fix it. As initialization takes very little time, we think always initializing
  331. # the toolset is a better default behavior.
  332. $tmpRestore = $restore
  333. $restore = $true
  334. $toolsetBuildProj = InitializeToolset
  335. $restore = $tmpRestore
  336. if ($ci) {
  337. $global:VerbosePreference = 'Continue'
  338. }
  339. if (-not $NoBuildRepoTasks) {
  340. Write-Host
  341. MSBuild $toolsetBuildProj `
  342. /p:RepoRoot=$RepoRoot `
  343. /p:Projects=$EngRoot\tools\RepoTasks\RepoTasks.csproj `
  344. /p:Configuration=Release `
  345. /p:Restore=$RunRestore `
  346. /p:Build=true `
  347. /clp:NoSummary `
  348. @ToolsetBuildArguments
  349. }
  350. if ($performDesktopBuild) {
  351. Write-Host
  352. Remove-Item variable:global:_BuildTool -ErrorAction Ignore
  353. $msbuildEngine = 'vs'
  354. MSBuild $toolsetBuildProj /p:RepoRoot=$RepoRoot @MSBuildArguments
  355. }
  356. if ($performDotnetBuild) {
  357. Write-Host
  358. Remove-Item variable:global:_BuildTool -ErrorAction Ignore
  359. $msbuildEngine = 'dotnet'
  360. MSBuild $toolsetBuildProj /p:RepoRoot=$RepoRoot @dotnetBuildArguments
  361. }
  362. }
  363. catch {
  364. Write-Host $_.ScriptStackTrace
  365. Write-PipelineTaskError -Message $_
  366. $exit_code = 1
  367. }
  368. finally {
  369. if (! $exit_code) {
  370. $exit_code = $LASTEXITCODE
  371. }
  372. # tools.ps1 corrupts global state, so reset these values so they don't carry between invocations of build.ps1
  373. Remove-Item variable:global:_BuildTool -ea Ignore
  374. Remove-Item variable:global:_DotNetInstallDir -ea Ignore
  375. Remove-Item variable:global:_ToolsetBuildProj -ea Ignore
  376. Remove-Item variable:global:_MSBuildExe -ea Ignore
  377. if ($DumpProcesses -or $ci) {
  378. Stop-Job -Name DumpProcesses
  379. Remove-Job -Name DumpProcesses
  380. }
  381. if ($ci) {
  382. & "$PSScriptRoot/scripts/KillProcesses.ps1"
  383. }
  384. }
  385. ExitWithExitCode $exit_code