github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/engine/hack/make.ps1 (about)

     1  <#
     2  .NOTES
     3      Author:  @jhowardmsft
     4  
     5      Summary: Windows native build script. This is similar to functionality provided
     6               by hack\make.sh, but uses native Windows PowerShell semantics. It does
     7               not support the full set of options provided by the Linux counterpart.
     8               For example:
     9  
    10               - You can't cross-build Linux docker binaries on Windows
    11               - Hashes aren't generated on binaries
    12               - 'Releasing' isn't supported.
    13               - Integration tests. This is because they currently cannot run inside a container,
    14                 and require significant external setup.
    15  
    16               It does however provided the minimum necessary to support parts of local Windows
    17               development and Windows to Windows CI.
    18  
    19               Usage Examples (run from repo root):
    20                  "hack\make.ps1 -Client" to build docker.exe client 64-bit binary (remote repo)
    21                  "hack\make.ps1 -TestUnit" to run unit tests
    22                  "hack\make.ps1 -Daemon -TestUnit" to build the daemon and run unit tests
    23                  "hack\make.ps1 -All" to run everything this script knows about that can run in a container
    24                  "hack\make.ps1" to build the daemon binary (same as -Daemon)
    25                  "hack\make.ps1 -Binary" shortcut to -Client and -Daemon
    26  
    27  .PARAMETER Client
    28       Builds the client binaries.
    29  
    30  .PARAMETER Daemon
    31       Builds the daemon binary.
    32  
    33  .PARAMETER Binary
    34       Builds the client and daemon binaries. A convenient shortcut to `make.ps1 -Client -Daemon`.
    35  
    36  .PARAMETER Race
    37       Use -race in go build and go test.
    38  
    39  .PARAMETER Noisy
    40       Use -v in go build.
    41  
    42  .PARAMETER ForceBuildAll
    43       Use -a in go build.
    44  
    45  .PARAMETER NoOpt
    46       Use -gcflags -N -l in go build to disable optimisation (can aide debugging).
    47  
    48  .PARAMETER CommitSuffix
    49       Adds a custom string to be appended to the commit ID (spaces are stripped).
    50  
    51  .PARAMETER DCO
    52       Runs the DCO (Developer Certificate Of Origin) test (must be run outside a container).
    53  
    54  .PARAMETER PkgImports
    55       Runs the pkg\ directory imports test (must be run outside a container).
    56  
    57  .PARAMETER GoFormat
    58       Runs the Go formatting test (must be run outside a container).
    59  
    60  .PARAMETER TestUnit
    61       Runs unit tests.
    62  
    63  .PARAMETER TestIntegration
    64       Runs integration tests.
    65  
    66  .PARAMETER All
    67       Runs everything this script knows about that can run in a container.
    68  
    69  
    70  TODO
    71  - Unify the head commit
    72  - Add golint and other checks (swagger maybe?)
    73  
    74  #>
    75  
    76  
    77  param(
    78      [Parameter(Mandatory=$False)][switch]$Client,
    79      [Parameter(Mandatory=$False)][switch]$Daemon,
    80      [Parameter(Mandatory=$False)][switch]$Binary,
    81      [Parameter(Mandatory=$False)][switch]$Race,
    82      [Parameter(Mandatory=$False)][switch]$Noisy,
    83      [Parameter(Mandatory=$False)][switch]$ForceBuildAll,
    84      [Parameter(Mandatory=$False)][switch]$NoOpt,
    85      [Parameter(Mandatory=$False)][string]$CommitSuffix="",
    86      [Parameter(Mandatory=$False)][switch]$DCO,
    87      [Parameter(Mandatory=$False)][switch]$PkgImports,
    88      [Parameter(Mandatory=$False)][switch]$GoFormat,
    89      [Parameter(Mandatory=$False)][switch]$TestUnit,
    90      [Parameter(Mandatory=$False)][switch]$TestIntegration,
    91      [Parameter(Mandatory=$False)][switch]$All
    92  )
    93  
    94  $ErrorActionPreference = "Stop"
    95  $ProgressPreference = "SilentlyContinue"
    96  $pushed=$False  # To restore the directory if we have temporarily pushed to one.
    97  Set-Variable GOTESTSUM_LOCATION -option Constant -value "$env:GOPATH/bin/"
    98  
    99  # Utility function to get the commit ID of the repository
   100  Function Get-GitCommit() {
   101      if (-not (Test-Path ".\.git")) {
   102          # If we don't have a .git directory, but we do have the environment
   103          # variable DOCKER_GITCOMMIT set, that can override it.
   104          if ($env:DOCKER_GITCOMMIT.Length -eq 0) {
   105              Throw ".git directory missing and DOCKER_GITCOMMIT environment variable not specified."
   106          }
   107          Write-Host "INFO: Git commit ($env:DOCKER_GITCOMMIT) assumed from DOCKER_GITCOMMIT environment variable"
   108          return $env:DOCKER_GITCOMMIT
   109      }
   110      $gitCommit=$(git rev-parse --short HEAD)
   111      if ($(git status --porcelain --untracked-files=no).Length -ne 0) {
   112          $gitCommit="$gitCommit-unsupported"
   113          Write-Host ""
   114          Write-Warning "This version is unsupported because there are uncommitted file(s)."
   115          Write-Warning "Either commit these changes, or add them to .gitignore."
   116          git status --porcelain --untracked-files=no | Write-Warning
   117          Write-Host ""
   118      }
   119      return $gitCommit
   120  }
   121  
   122  # Utility function to determine if we are running in a container or not.
   123  # In Windows, we get this through an environment variable set in `Dockerfile.Windows`
   124  Function Check-InContainer() {
   125      if ($env:FROM_DOCKERFILE.Length -eq 0) {
   126          Write-Host ""
   127          Write-Warning "Not running in a container. The result might be an incorrect build."
   128          Write-Host ""
   129          return $False
   130      }
   131      return $True
   132  }
   133  
   134  # Utility function to warn if the version of go is correct. Used for local builds
   135  # outside of a container where it may be out of date with master.
   136  Function Verify-GoVersion() {
   137      Try {
   138          $goVersionDockerfile=(Select-String -Path ".\Dockerfile" -Pattern "^ARG[\s]+GO_VERSION=(.*)$").Matches.groups[1].Value -replace '\.0$',''
   139          $goVersionInstalled=(go version).ToString().Split(" ")[2].SubString(2)
   140      }
   141      Catch [Exception] {
   142          Throw "Failed to validate go version correctness: $_"
   143      }
   144      if (-not($goVersionInstalled -eq $goVersionDockerfile)) {
   145          Write-Host ""
   146          Write-Warning "Building with golang version $goVersionInstalled. You should update to $goVersionDockerfile"
   147          Write-Host ""
   148      }
   149  }
   150  
   151  # Utility function to get the commit for HEAD
   152  Function Get-HeadCommit() {
   153      $head = Invoke-Expression "git rev-parse --verify HEAD"
   154      if ($LASTEXITCODE -ne 0) { Throw "Failed getting HEAD commit" }
   155  
   156      return $head
   157  }
   158  
   159  # Utility function to get the commit for upstream
   160  Function Get-UpstreamCommit() {
   161      Invoke-Expression "git fetch -q https://github.com/docker/docker.git refs/heads/master"
   162      if ($LASTEXITCODE -ne 0) { Throw "Failed fetching" }
   163  
   164      $upstream = Invoke-Expression "git rev-parse --verify FETCH_HEAD"
   165      if ($LASTEXITCODE -ne 0) { Throw "Failed getting upstream commit" }
   166  
   167      return $upstream
   168  }
   169  
   170  # Build a binary (client or daemon)
   171  Function Execute-Build($type, $additionalBuildTags, $directory) {
   172      # Generate the build flags
   173      $buildTags = "autogen"
   174      if ($Noisy)                     { $verboseParm=" -v" }
   175      if ($Race)                      { Write-Warning "Using race detector"; $raceParm=" -race"}
   176      if ($ForceBuildAll)             { $allParm=" -a" }
   177      if ($NoOpt)                     { $optParm=" -gcflags "+""""+"-N -l"+"""" }
   178      if ($additionalBuildTags -ne "") { $buildTags += $(" " + $additionalBuildTags) }
   179  
   180      # Do the go build in the appropriate directory
   181      # Note -linkmode=internal is required to be able to debug on Windows.
   182      # https://github.com/golang/go/issues/14319#issuecomment-189576638
   183      Write-Host "INFO: Building $type..."
   184      Push-Location $root\cmd\$directory; $global:pushed=$True
   185      $buildCommand = "go build" + `
   186                      $raceParm + `
   187                      $verboseParm + `
   188                      $allParm + `
   189                      $optParm + `
   190                      " -tags """ + $buildTags + """" + `
   191                      " -ldflags """ + "-linkmode=internal" + """" + `
   192                      " -o $root\bundles\"+$directory+".exe"
   193      Invoke-Expression $buildCommand
   194      if ($LASTEXITCODE -ne 0) { Throw "Failed to compile $type" }
   195      Pop-Location; $global:pushed=$False
   196  }
   197  
   198  
   199  # Validates the DCO marker is present on each commit
   200  Function Validate-DCO($headCommit, $upstreamCommit) {
   201      Write-Host "INFO: Validating Developer Certificate of Origin..."
   202      # Username may only contain alphanumeric characters or dashes and cannot begin with a dash
   203      $usernameRegex='[a-zA-Z0-9][a-zA-Z0-9-]+'
   204  
   205      $dcoPrefix="Signed-off-by:"
   206      $dcoRegex="^(Docker-DCO-1.1-)?$dcoPrefix ([^<]+) <([^<>@]+@[^<>]+)>( \(github: ($usernameRegex)\))?$"
   207  
   208      $counts = Invoke-Expression "git diff --numstat $upstreamCommit...$headCommit"
   209      if ($LASTEXITCODE -ne 0) { Throw "Failed git diff --numstat" }
   210  
   211      # Counts of adds and deletes after removing multiple white spaces. AWK anyone? :(
   212      $adds=0; $dels=0; $($counts -replace '\s+', ' ') | %{ 
   213          $a=$_.Split(" "); 
   214          if ($a[0] -ne "-") { $adds+=[int]$a[0] }
   215          if ($a[1] -ne "-") { $dels+=[int]$a[1] }
   216      }
   217      if (($adds -eq 0) -and ($dels -eq 0)) { 
   218          Write-Warning "DCO validation - nothing to validate!"
   219          return
   220      }
   221  
   222      $commits = Invoke-Expression "git log  $upstreamCommit..$headCommit --format=format:%H%n"
   223      if ($LASTEXITCODE -ne 0) { Throw "Failed git log --format" }
   224      $commits = $($commits -split '\s+' -match '\S')
   225      $badCommits=@()
   226      $commits | ForEach-Object{
   227          # Skip commits with no content such as merge commits etc
   228          if ($(git log -1 --format=format: --name-status $_).Length -gt 0) {
   229              # Ignore exit code on next call - always process regardless
   230              $commitMessage = Invoke-Expression "git log -1 --format=format:%B --name-status $_"
   231              if (($commitMessage -match $dcoRegex).Length -eq 0) { $badCommits+=$_ }
   232          }
   233      }
   234      if ($badCommits.Length -eq 0) {
   235          Write-Host "Congratulations!  All commits are properly signed with the DCO!"
   236      } else {
   237          $e = "`nThese commits do not have a proper '$dcoPrefix' marker:`n"
   238          $badCommits | %{ $e+=" - $_`n"}
   239          $e += "`nPlease amend each commit to include a properly formatted DCO marker.`n`n"
   240          $e += "Visit the following URL for information about the Docker DCO:`n"
   241          $e += "https://github.com/docker/docker/blob/master/CONTRIBUTING.md#sign-your-work`n"
   242          Throw $e
   243      }
   244  }
   245  
   246  # Validates that .\pkg\... is safely isolated from internal code
   247  Function Validate-PkgImports($headCommit, $upstreamCommit) {
   248      Write-Host "INFO: Validating pkg import isolation..."
   249  
   250      # Get a list of go source-code files which have changed under pkg\. Ignore exit code on next call - always process regardless
   251      $files=@(); $files = Invoke-Expression "git diff $upstreamCommit...$headCommit --diff-filter=ACMR --name-only -- `'pkg\*.go`'"
   252      $badFiles=@(); $files | ForEach-Object{
   253          $file=$_
   254          # For the current changed file, get its list of dependencies, sorted and uniqued.
   255          $imports = Invoke-Expression "go list -e -f `'{{ .Deps }}`' $file"
   256          if ($LASTEXITCODE -ne 0) { Throw "Failed go list for dependencies on $file" }
   257          $imports = $imports -Replace "\[" -Replace "\]", "" -Split(" ") | Sort-Object | Get-Unique
   258          # Filter out what we are looking for
   259          $imports = @() + $imports -NotMatch "^github.com/docker/docker/pkg/" `
   260                                    -NotMatch "^github.com/docker/docker/vendor" `
   261                                    -Match "^github.com/docker/docker" `
   262                                    -Replace "`n", ""
   263          $imports | ForEach-Object{ $badFiles+="$file imports $_`n" }
   264      }
   265      if ($badFiles.Length -eq 0) {
   266          Write-Host 'Congratulations!  ".\pkg\*.go" is safely isolated from internal code.'
   267      } else {
   268          $e = "`nThese files import internal code: (either directly or indirectly)`n"
   269          $badFiles | ForEach-Object{ $e+=" - $_"}
   270          Throw $e
   271      }
   272  }
   273  
   274  # Validates that changed files are correctly go-formatted
   275  Function Validate-GoFormat($headCommit, $upstreamCommit) {
   276      Write-Host "INFO: Validating go formatting on changed files..."
   277  
   278      # Verify gofmt is installed
   279      if ($(Get-Command gofmt -ErrorAction SilentlyContinue) -eq $nil) { Throw "gofmt does not appear to be installed" }
   280  
   281      # Get a list of all go source-code files which have changed.  Ignore exit code on next call - always process regardless
   282      $files=@(); $files = Invoke-Expression "git diff $upstreamCommit...$headCommit --diff-filter=ACMR --name-only -- `'*.go`'"
   283      $files = $files | Select-String -NotMatch "^vendor/"
   284      $badFiles=@(); $files | %{
   285          # Deliberately ignore error on next line - treat as failed
   286          $content=Invoke-Expression "git show $headCommit`:$_"
   287  
   288          # Next set of hoops are to ensure we have LF not CRLF semantics as otherwise gofmt on Windows will not succeed.
   289          # Also note that gofmt on Windows does not appear to support stdin piping correctly. Hence go through a temporary file.
   290          $content=$content -join "`n"
   291          $content+="`n"
   292          $outputFile=[System.IO.Path]::GetTempFileName()
   293          if (Test-Path $outputFile) { Remove-Item $outputFile }
   294          [System.IO.File]::WriteAllText($outputFile, $content, (New-Object System.Text.UTF8Encoding($False)))
   295          $currentFile = $_ -Replace("/","\")
   296          Write-Host Checking $currentFile
   297          Invoke-Expression "gofmt -s -l $outputFile"
   298          if ($LASTEXITCODE -ne 0) { $badFiles+=$currentFile }
   299          if (Test-Path $outputFile) { Remove-Item $outputFile }
   300      }
   301      if ($badFiles.Length -eq 0) {
   302          Write-Host 'Congratulations!  All Go source files are properly formatted.'
   303      } else {
   304          $e = "`nThese files are not properly gofmt`'d:`n"
   305          $badFiles | ForEach-Object{ $e+=" - $_`n"}
   306          $e+= "`nPlease reformat the above files using `"gofmt -s -w`" and commit the result."
   307          Throw $e
   308      }
   309  }
   310  
   311  # Run the unit tests
   312  Function Run-UnitTests() {
   313      Write-Host "INFO: Running unit tests..."
   314      $testPath="./..."
   315      $goListCommand = "go list -e -f '{{if ne .Name """ + '\"github.com/docker/docker\"' + """}}{{.ImportPath}}{{end}}' $testPath"
   316      $pkgList = $(Invoke-Expression $goListCommand)
   317      if ($LASTEXITCODE -ne 0) { Throw "go list for unit tests failed" }
   318      $pkgList = $pkgList | Select-String -Pattern "github.com/docker/docker"
   319      $pkgList = $pkgList | Select-String -NotMatch "github.com/docker/docker/vendor"
   320      $pkgList = $pkgList | Select-String -NotMatch "github.com/docker/docker/man"
   321      $pkgList = $pkgList | Select-String -NotMatch "github.com/docker/docker/integration"
   322      $pkgList = $pkgList -replace "`r`n", " "
   323  
   324      $goTestArg = "--format=standard-verbose --jsonfile=bundles\go-test-report-unit-tests.json --junitfile=bundles\junit-report-unit-tests.xml -- " + $raceParm + " -cover -ldflags -w -a """ + "-test.timeout=10m" + """ $pkgList"
   325      Write-Host "INFO: Invoking unit tests run with $GOTESTSUM_LOCATION\gotestsum.exe $goTestArg"
   326      $pinfo = New-Object System.Diagnostics.ProcessStartInfo
   327      $pinfo.FileName = "$GOTESTSUM_LOCATION\gotestsum.exe"
   328      $pinfo.WorkingDirectory = "$($PWD.Path)"
   329      $pinfo.UseShellExecute = $false
   330      $pinfo.Arguments = $goTestArg
   331      $p = New-Object System.Diagnostics.Process
   332      $p.StartInfo = $pinfo
   333      $p.Start() | Out-Null
   334      $p.WaitForExit()
   335      if ($p.ExitCode -ne 0) { Throw "Unit tests failed" }
   336  }
   337  
   338  # Run the integration tests
   339  Function Run-IntegrationTests() {
   340      $escRoot = [Regex]::Escape($root)
   341      $env:DOCKER_INTEGRATION_DAEMON_DEST = $bundlesDir + "\tmp"
   342      $dirs = go list -test -f '{{- if ne .ForTest `"`" -}}{{- .Dir -}}{{- end -}}' .\integration\...
   343      ForEach($dir in $dirs) {
   344          # Normalize directory name for using in the test results files.
   345          $normDir = $dir.Trim()
   346          $normDir = $normDir -replace $escRoot, ""
   347          $normDir = $normDir -replace "\\", "-"
   348          $normDir = $normDir -replace "\/", "-"
   349          $normDir = $normDir -replace "\.", "-"
   350          if ($normDir.StartsWith("-"))
   351          {
   352              $normDir = $normDir.TrimStart("-")
   353          }
   354          if ($normDir.EndsWith("-"))
   355          {
   356              $normDir = $normDir.TrimEnd("-")
   357          }
   358          $jsonFilePath = $bundlesDir + "\go-test-report-int-tests-$normDir" + ".json"
   359          $xmlFilePath = $bundlesDir + "\junit-report-int-tests-$normDir" + ".xml"
   360          Set-Location $dir
   361          Write-Host "Running $($PWD.Path)"
   362          $pinfo = New-Object System.Diagnostics.ProcessStartInfo
   363          $pinfo.FileName = "gotestsum.exe"
   364          $pinfo.WorkingDirectory = "$($PWD.Path)"
   365          $pinfo.UseShellExecute = $false
   366          $pinfo.Arguments = "--format=standard-verbose --jsonfile=$jsonFilePath --junitfile=$xmlFilePath -- $env:INTEGRATION_TESTFLAGS"
   367          $p = New-Object System.Diagnostics.Process
   368          $p.StartInfo = $pinfo
   369          $p.Start() | Out-Null
   370          $p.WaitForExit()
   371          if ($p.ExitCode -ne 0) { Throw "Integration tests failed" }
   372      }
   373  }
   374  
   375  # Start of main code.
   376  Try {
   377      Write-Host -ForegroundColor Cyan "INFO: make.ps1 starting at $(Get-Date)"
   378  
   379      # Get to the root of the repo
   380      $root = $(Split-Path $MyInvocation.MyCommand.Definition -Parent | Split-Path -Parent)
   381      Push-Location $root
   382  
   383      # Ensure the bundles directory exists
   384      $bundlesDir = $root + "\bundles"
   385      Set-Variable bundlesDir -option ReadOnly
   386      New-Item -Force $bundlesDir -ItemType Directory | Out-Null
   387  
   388      # Handle the "-All" shortcut to turn on all things we can handle.
   389      # Note we expressly only include the items which can run in a container - the validations tests cannot
   390      # as they require the .git directory which is excluded from the image by .dockerignore
   391      if ($All) { $Client=$True; $Daemon=$True; $TestUnit=$True; }
   392  
   393      # Handle the "-Binary" shortcut to build both client and daemon.
   394      if ($Binary) { $Client = $True; $Daemon = $True }
   395  
   396      # Default to building the daemon if not asked for anything explicitly.
   397      if (-not($Client) -and -not($Daemon) -and -not($DCO) -and -not($PkgImports) -and -not($GoFormat) -and -not($TestUnit) -and -not($TestIntegration)) { $Daemon=$True }
   398  
   399      # Verify git is installed
   400      if ($(Get-Command git -ErrorAction SilentlyContinue) -eq $nil) { Throw "Git does not appear to be installed" }
   401  
   402      # Verify go is installed
   403      if ($(Get-Command go -ErrorAction SilentlyContinue) -eq $nil) { Throw "GoLang does not appear to be installed" }
   404  
   405      # Get the git commit. This will also verify if we are in a repo or not. Then add a custom string if supplied.
   406      $gitCommit=Get-GitCommit
   407      if ($CommitSuffix -ne "") { $gitCommit += "-"+$CommitSuffix -Replace ' ', '' }
   408  
   409      # Get the version of docker (eg 17.04.0-dev)
   410      $dockerVersion="0.0.0-dev"
   411      # Overwrite dockerVersion if VERSION Environment variable is available
   412      if (Test-Path Env:\VERSION) { $dockerVersion=$env:VERSION }
   413  
   414      # Give a warning if we are not running in a container and are building binaries or running unit tests.
   415      # Not relevant for validation tests as these are fine to run outside of a container.
   416      if ($Client -or $Daemon -or $TestUnit) { $inContainer=Check-InContainer }
   417  
   418      # If we are not in a container, validate the version of GO that is installed.
   419      if (-not $inContainer) { Verify-GoVersion }
   420  
   421      # Verify GOPATH is set
   422      if ($env:GOPATH.Length -eq 0) { Throw "Missing GOPATH environment variable. See https://golang.org/doc/code.html#GOPATH" }
   423  
   424      # Run autogen if building binaries or running unit tests.
   425      if ($Client -or $Daemon -or $TestUnit) {
   426          Write-Host "INFO: Invoking autogen..."
   427          Try { .\hack\make\.go-autogen.ps1 -CommitString $gitCommit -DockerVersion $dockerVersion -Platform "$env:PLATFORM" -Product "$env:PRODUCT" }
   428          Catch [Exception] { Throw $_ }
   429      }
   430  
   431      # DCO, Package import and Go formatting tests.
   432      if ($DCO -or $PkgImports -or $GoFormat) {
   433          # We need the head and upstream commits for these
   434          $headCommit=Get-HeadCommit
   435          $upstreamCommit=Get-UpstreamCommit
   436  
   437          # Run DCO validation
   438          if ($DCO) { Validate-DCO $headCommit $upstreamCommit }
   439  
   440          # Run `gofmt` validation
   441          if ($GoFormat) { Validate-GoFormat $headCommit $upstreamCommit }
   442  
   443          # Run pkg isolation validation
   444          if ($PkgImports) { Validate-PkgImports $headCommit $upstreamCommit }
   445      }
   446  
   447      # Build the binaries
   448      if ($Client -or $Daemon) {
   449  
   450          # Perform the actual build
   451          if ($Daemon) { Execute-Build "daemon" "daemon" "dockerd" }
   452          if ($Client) {
   453              # Get the Docker channel and version from the environment, or use the defaults.
   454              if (-not ($channel = $env:DOCKERCLI_CHANNEL)) { $channel = "stable" }
   455              if (-not ($version = $env:DOCKERCLI_VERSION)) { $version = "17.06.2-ce" }
   456  
   457              # Download the zip file and extract the client executable.
   458              Write-Host "INFO: Downloading docker/cli version $version from $channel..."
   459              $url = "https://download.docker.com/win/static/$channel/x86_64/docker-$version.zip"
   460              Invoke-WebRequest $url -OutFile "docker.zip"
   461              Try {
   462                  Add-Type -AssemblyName System.IO.Compression.FileSystem
   463                  $zip = [System.IO.Compression.ZipFile]::OpenRead("$PWD\docker.zip")
   464                  Try {
   465                      if (-not ($entry = $zip.Entries | Where-Object { $_.Name -eq "docker.exe" })) {
   466                          Throw "Cannot find docker.exe in $url"
   467                      }
   468                      [System.IO.Compression.ZipFileExtensions]::ExtractToFile($entry, "$PWD\bundles\docker.exe", $true)
   469                  }
   470                  Finally {
   471                      $zip.Dispose()
   472                  }
   473              }
   474              Finally {
   475                  Remove-Item -Force "docker.zip"
   476              }
   477          }
   478      }
   479  
   480      # Run unit tests
   481      if ($TestUnit) { Run-UnitTests }
   482  
   483      # Run integration tests
   484      if ($TestIntegration) { Run-IntegrationTests }
   485  
   486      # Gratuitous ASCII art.
   487      if ($Daemon -or $Client) {
   488          Write-Host
   489          Write-Host -ForegroundColor Green " ________   ____  __."
   490          Write-Host -ForegroundColor Green " \_____  \ `|    `|/ _`|"
   491          Write-Host -ForegroundColor Green " /   `|   \`|      `<"
   492          Write-Host -ForegroundColor Green " /    `|    \    `|  \"
   493          Write-Host -ForegroundColor Green " \_______  /____`|__ \"
   494          Write-Host -ForegroundColor Green "         \/        \/"
   495          Write-Host
   496      }
   497  }
   498  Catch [Exception] {
   499      Write-Host -ForegroundColor Red ("`nERROR: make.ps1 failed:`n$_")
   500      Write-Host -ForegroundColor Red ($_.InvocationInfo.PositionMessage)
   501  
   502      # More gratuitous ASCII art.
   503      Write-Host
   504      Write-Host -ForegroundColor Red  "___________      .__.__             .___"
   505      Write-Host -ForegroundColor Red  "\_   _____/____  `|__`|  `|   ____   __`| _/"
   506      Write-Host -ForegroundColor Red  " `|    __) \__  \ `|  `|  `| _/ __ \ / __ `| "
   507      Write-Host -ForegroundColor Red  " `|     \   / __ \`|  `|  `|_\  ___// /_/ `| "
   508      Write-Host -ForegroundColor Red  " \___  /  (____  /__`|____/\___  `>____ `| "
   509      Write-Host -ForegroundColor Red  "     \/        \/             \/     \/ "
   510      Write-Host
   511  
   512      exit 1
   513  }
   514  Finally {
   515      Pop-Location # As we pushed to the root of the repo as the very first thing
   516      if ($global:pushed) { Pop-Location }
   517      Write-Host -ForegroundColor Cyan "INFO: make.ps1 ended at $(Get-Date)"
   518  }