github.com/ssdev-go/moby@v17.12.1-ce-rc2+incompatible/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 All
    64       Runs everything this script knows about that can run in a container.
    65  
    66  
    67  TODO
    68  - Unify the head commit
    69  - Add golint and other checks (swagger maybe?)
    70  
    71  #>
    72  
    73  
    74  param(
    75      [Parameter(Mandatory=$False)][switch]$Client,
    76      [Parameter(Mandatory=$False)][switch]$Daemon,
    77      [Parameter(Mandatory=$False)][switch]$Binary,
    78      [Parameter(Mandatory=$False)][switch]$Race,
    79      [Parameter(Mandatory=$False)][switch]$Noisy,
    80      [Parameter(Mandatory=$False)][switch]$ForceBuildAll,
    81      [Parameter(Mandatory=$False)][switch]$NoOpt,
    82      [Parameter(Mandatory=$False)][string]$CommitSuffix="",
    83      [Parameter(Mandatory=$False)][switch]$DCO,
    84      [Parameter(Mandatory=$False)][switch]$PkgImports,
    85      [Parameter(Mandatory=$False)][switch]$GoFormat,
    86      [Parameter(Mandatory=$False)][switch]$TestUnit,
    87      [Parameter(Mandatory=$False)][switch]$All
    88  )
    89  
    90  $ErrorActionPreference = "Stop"
    91  $ProgressPreference = "SilentlyContinue"
    92  $pushed=$False  # To restore the directory if we have temporarily pushed to one.
    93  
    94  # Utility function to get the commit ID of the repository
    95  Function Get-GitCommit() {
    96      if (-not (Test-Path ".\.git")) {
    97          # If we don't have a .git directory, but we do have the environment
    98          # variable DOCKER_GITCOMMIT set, that can override it.
    99          if ($env:DOCKER_GITCOMMIT.Length -eq 0) {
   100              Throw ".git directory missing and DOCKER_GITCOMMIT environment variable not specified."
   101          }
   102          Write-Host "INFO: Git commit ($env:DOCKER_GITCOMMIT) assumed from DOCKER_GITCOMMIT environment variable"
   103          return $env:DOCKER_GITCOMMIT
   104      }
   105      $gitCommit=$(git rev-parse --short HEAD)
   106      if ($(git status --porcelain --untracked-files=no).Length -ne 0) {
   107          $gitCommit="$gitCommit-unsupported"
   108          Write-Host ""
   109          Write-Warning "This version is unsupported because there are uncommitted file(s)."
   110          Write-Warning "Either commit these changes, or add them to .gitignore."
   111          git status --porcelain --untracked-files=no | Write-Warning
   112          Write-Host ""
   113      }
   114      return $gitCommit
   115  }
   116  
   117  # Utility function to determine if we are running in a container or not.
   118  # In Windows, we get this through an environment variable set in `Dockerfile.Windows`
   119  Function Check-InContainer() {
   120      if ($env:FROM_DOCKERFILE.Length -eq 0) {
   121          Write-Host ""
   122          Write-Warning "Not running in a container. The result might be an incorrect build."
   123          Write-Host ""
   124          return $False
   125      }
   126      return $True
   127  }
   128  
   129  # Utility function to warn if the version of go is correct. Used for local builds
   130  # outside of a container where it may be out of date with master.
   131  Function Verify-GoVersion() {
   132      Try {
   133          $goVersionDockerfile=(Get-Content ".\Dockerfile" | Select-String "ENV GO_VERSION").ToString().Split(" ")[2]
   134          $goVersionInstalled=(go version).ToString().Split(" ")[2].SubString(2)
   135      }
   136      Catch [Exception] {
   137          Throw "Failed to validate go version correctness: $_"
   138      }
   139      if (-not($goVersionInstalled -eq $goVersionDockerfile)) {
   140          Write-Host ""
   141          Write-Warning "Building with golang version $goVersionInstalled. You should update to $goVersionDockerfile"
   142          Write-Host ""
   143      }
   144  }
   145  
   146  # Utility function to get the commit for HEAD
   147  Function Get-HeadCommit() {
   148      $head = Invoke-Expression "git rev-parse --verify HEAD"
   149      if ($LASTEXITCODE -ne 0) { Throw "Failed getting HEAD commit" }
   150  
   151      return $head
   152  }
   153  
   154  # Utility function to get the commit for upstream
   155  Function Get-UpstreamCommit() {
   156      Invoke-Expression "git fetch -q https://github.com/docker/docker.git refs/heads/master"
   157      if ($LASTEXITCODE -ne 0) { Throw "Failed fetching" }
   158  
   159      $upstream = Invoke-Expression "git rev-parse --verify FETCH_HEAD"
   160      if ($LASTEXITCODE -ne 0) { Throw "Failed getting upstream commit" }
   161  
   162      return $upstream
   163  }
   164  
   165  # Build a binary (client or daemon)
   166  Function Execute-Build($type, $additionalBuildTags, $directory) {
   167      # Generate the build flags
   168      $buildTags = "autogen"
   169      if ($Noisy)                     { $verboseParm=" -v" }
   170      if ($Race)                      { Write-Warning "Using race detector"; $raceParm=" -race"}
   171      if ($ForceBuildAll)             { $allParm=" -a" }
   172      if ($NoOpt)                     { $optParm=" -gcflags "+""""+"-N -l"+"""" }
   173      if ($additionalBuildTags -ne "") { $buildTags += $(" " + $additionalBuildTags) }
   174  
   175      # Do the go build in the appropriate directory
   176      # Note -linkmode=internal is required to be able to debug on Windows.
   177      # https://github.com/golang/go/issues/14319#issuecomment-189576638
   178      Write-Host "INFO: Building $type..."
   179      Push-Location $root\cmd\$directory; $global:pushed=$True
   180      $buildCommand = "go build" + `
   181                      $raceParm + `
   182                      $verboseParm + `
   183                      $allParm + `
   184                      $optParm + `
   185                      " -tags """ + $buildTags + """" + `
   186                      " -ldflags """ + "-linkmode=internal" + """" + `
   187                      " -o $root\bundles\"+$directory+".exe"
   188      Invoke-Expression $buildCommand
   189      if ($LASTEXITCODE -ne 0) { Throw "Failed to compile $type" }
   190      Pop-Location; $global:pushed=$False
   191  }
   192  
   193  
   194  # Validates the DCO marker is present on each commit
   195  Function Validate-DCO($headCommit, $upstreamCommit) {
   196      Write-Host "INFO: Validating Developer Certificate of Origin..."
   197      # Username may only contain alphanumeric characters or dashes and cannot begin with a dash
   198      $usernameRegex='[a-zA-Z0-9][a-zA-Z0-9-]+'
   199  
   200      $dcoPrefix="Signed-off-by:"
   201      $dcoRegex="^(Docker-DCO-1.1-)?$dcoPrefix ([^<]+) <([^<>@]+@[^<>]+)>( \(github: ($usernameRegex)\))?$"
   202  
   203      $counts = Invoke-Expression "git diff --numstat $upstreamCommit...$headCommit"
   204      if ($LASTEXITCODE -ne 0) { Throw "Failed git diff --numstat" }
   205  
   206      # Counts of adds and deletes after removing multiple white spaces. AWK anyone? :(
   207      $adds=0; $dels=0; $($counts -replace '\s+', ' ') | %{ 
   208          $a=$_.Split(" "); 
   209          if ($a[0] -ne "-") { $adds+=[int]$a[0] }
   210          if ($a[1] -ne "-") { $dels+=[int]$a[1] }
   211      }
   212      if (($adds -eq 0) -and ($dels -eq 0)) { 
   213          Write-Warning "DCO validation - nothing to validate!"
   214          return
   215      }
   216  
   217      $commits = Invoke-Expression "git log  $upstreamCommit..$headCommit --format=format:%H%n"
   218      if ($LASTEXITCODE -ne 0) { Throw "Failed git log --format" }
   219      $commits = $($commits -split '\s+' -match '\S')
   220      $badCommits=@()
   221      $commits | %{
   222          # Skip commits with no content such as merge commits etc
   223          if ($(git log -1 --format=format: --name-status $_).Length -gt 0) {
   224              # Ignore exit code on next call - always process regardless
   225              $commitMessage = Invoke-Expression "git log -1 --format=format:%B --name-status $_"
   226              if (($commitMessage -match $dcoRegex).Length -eq 0) { $badCommits+=$_ }
   227          }
   228      }
   229      if ($badCommits.Length -eq 0) {
   230          Write-Host "Congratulations!  All commits are properly signed with the DCO!"
   231      } else {
   232          $e = "`nThese commits do not have a proper '$dcoPrefix' marker:`n"
   233          $badCommits | %{ $e+=" - $_`n"}
   234          $e += "`nPlease amend each commit to include a properly formatted DCO marker.`n`n"
   235          $e += "Visit the following URL for information about the Docker DCO:`n"
   236          $e += "https://github.com/docker/docker/blob/master/CONTRIBUTING.md#sign-your-work`n"
   237          Throw $e
   238      }
   239  }
   240  
   241  # Validates that .\pkg\... is safely isolated from internal code
   242  Function Validate-PkgImports($headCommit, $upstreamCommit) {
   243      Write-Host "INFO: Validating pkg import isolation..."
   244  
   245      # Get a list of go source-code files which have changed under pkg\. Ignore exit code on next call - always process regardless
   246      $files=@(); $files = Invoke-Expression "git diff $upstreamCommit...$headCommit --diff-filter=ACMR --name-only -- `'pkg\*.go`'"
   247      $badFiles=@(); $files | %{
   248          $file=$_
   249          # For the current changed file, get its list of dependencies, sorted and uniqued.
   250          $imports = Invoke-Expression "go list -e -f `'{{ .Deps }}`' $file"
   251          if ($LASTEXITCODE -ne 0) { Throw "Failed go list for dependencies on $file" }
   252          $imports = $imports -Replace "\[" -Replace "\]", "" -Split(" ") | Sort-Object | Get-Unique
   253          # Filter out what we are looking for
   254          $imports = $imports -NotMatch "^github.com/docker/docker/pkg/" `
   255                              -NotMatch "^github.com/docker/docker/vendor" `
   256                              -Match "^github.com/docker/docker" `
   257                              -Replace "`n", ""
   258          $imports | % { $badFiles+="$file imports $_`n" }
   259      }
   260      if ($badFiles.Length -eq 0) {
   261          Write-Host 'Congratulations!  ".\pkg\*.go" is safely isolated from internal code.'
   262      } else {
   263          $e = "`nThese files import internal code: (either directly or indirectly)`n"
   264          $badFiles | %{ $e+=" - $_"}
   265          Throw $e
   266      }
   267  }
   268  
   269  # Validates that changed files are correctly go-formatted
   270  Function Validate-GoFormat($headCommit, $upstreamCommit) {
   271      Write-Host "INFO: Validating go formatting on changed files..."
   272  
   273      # Verify gofmt is installed
   274      if ($(Get-Command gofmt -ErrorAction SilentlyContinue) -eq $nil) { Throw "gofmt does not appear to be installed" }
   275  
   276      # Get a list of all go source-code files which have changed.  Ignore exit code on next call - always process regardless
   277      $files=@(); $files = Invoke-Expression "git diff $upstreamCommit...$headCommit --diff-filter=ACMR --name-only -- `'*.go`'"
   278      $files = $files | Select-String -NotMatch "^vendor/"
   279      $badFiles=@(); $files | %{
   280          # Deliberately ignore error on next line - treat as failed
   281          $content=Invoke-Expression "git show $headCommit`:$_"
   282  
   283          # Next set of hoops are to ensure we have LF not CRLF semantics as otherwise gofmt on Windows will not succeed.
   284          # Also note that gofmt on Windows does not appear to support stdin piping correctly. Hence go through a temporary file.
   285          $content=$content -join "`n"
   286          $content+="`n"
   287          $outputFile=[System.IO.Path]::GetTempFileName()
   288          if (Test-Path $outputFile) { Remove-Item $outputFile }
   289          [System.IO.File]::WriteAllText($outputFile, $content, (New-Object System.Text.UTF8Encoding($False)))
   290          $currentFile = $_ -Replace("/","\")
   291          Write-Host Checking $currentFile
   292          Invoke-Expression "gofmt -s -l $outputFile"
   293          if ($LASTEXITCODE -ne 0) { $badFiles+=$currentFile }
   294          if (Test-Path $outputFile) { Remove-Item $outputFile }
   295      }
   296      if ($badFiles.Length -eq 0) {
   297          Write-Host 'Congratulations!  All Go source files are properly formatted.'
   298      } else {
   299          $e = "`nThese files are not properly gofmt`'d:`n"
   300          $badFiles | %{ $e+=" - $_`n"}
   301          $e+= "`nPlease reformat the above files using `"gofmt -s -w`" and commit the result."
   302          Throw $e
   303      }
   304  }
   305  
   306  # Run the unit tests
   307  Function Run-UnitTests() {
   308      Write-Host "INFO: Running unit tests..."
   309      $testPath="./..."
   310      $goListCommand = "go list -e -f '{{if ne .Name """ + '\"github.com/docker/docker\"' + """}}{{.ImportPath}}{{end}}' $testPath"
   311      $pkgList = $(Invoke-Expression $goListCommand)
   312      if ($LASTEXITCODE -ne 0) { Throw "go list for unit tests failed" }
   313      $pkgList = $pkgList | Select-String -Pattern "github.com/docker/docker"
   314      $pkgList = $pkgList | Select-String -NotMatch "github.com/docker/docker/vendor"
   315      $pkgList = $pkgList | Select-String -NotMatch "github.com/docker/docker/man"
   316      $pkgList = $pkgList | Select-String -NotMatch "github.com/docker/docker/integration"
   317      $pkgList = $pkgList -replace "`r`n", " "
   318      $goTestCommand = "go test" + $raceParm + " -cover -ldflags -w -tags """ + "autogen daemon" + """ -a """ + "-test.timeout=10m" + """ $pkgList"
   319      Invoke-Expression $goTestCommand
   320      if ($LASTEXITCODE -ne 0) { Throw "Unit tests failed" }
   321  }
   322  
   323  # Start of main code.
   324  Try {
   325      Write-Host -ForegroundColor Cyan "INFO: make.ps1 starting at $(Get-Date)"
   326  
   327      # Get to the root of the repo
   328      $root = $(Split-Path $MyInvocation.MyCommand.Definition -Parent | Split-Path -Parent)
   329      Push-Location $root
   330  
   331      # Handle the "-All" shortcut to turn on all things we can handle.
   332      # Note we expressly only include the items which can run in a container - the validations tests cannot
   333      # as they require the .git directory which is excluded from the image by .dockerignore
   334      if ($All) { $Client=$True; $Daemon=$True; $TestUnit=$True }
   335  
   336      # Handle the "-Binary" shortcut to build both client and daemon.
   337      if ($Binary) { $Client = $True; $Daemon = $True }
   338  
   339      # Default to building the daemon if not asked for anything explicitly.
   340      if (-not($Client) -and -not($Daemon) -and -not($DCO) -and -not($PkgImports) -and -not($GoFormat) -and -not($TestUnit)) { $Daemon=$True }
   341  
   342      # Verify git is installed
   343      if ($(Get-Command git -ErrorAction SilentlyContinue) -eq $nil) { Throw "Git does not appear to be installed" }
   344  
   345      # Verify go is installed
   346      if ($(Get-Command go -ErrorAction SilentlyContinue) -eq $nil) { Throw "GoLang does not appear to be installed" }
   347  
   348      # Get the git commit. This will also verify if we are in a repo or not. Then add a custom string if supplied.
   349      $gitCommit=Get-GitCommit
   350      if ($CommitSuffix -ne "") { $gitCommit += "-"+$CommitSuffix -Replace ' ', '' }
   351  
   352      # Get the version of docker (eg 17.04.0-dev)
   353      $dockerVersion="0.0.0-dev"
   354  
   355      # Give a warning if we are not running in a container and are building binaries or running unit tests.
   356      # Not relevant for validation tests as these are fine to run outside of a container.
   357      if ($Client -or $Daemon -or $TestUnit) { $inContainer=Check-InContainer }
   358  
   359      # If we are not in a container, validate the version of GO that is installed.
   360      if (-not $inContainer) { Verify-GoVersion }
   361  
   362      # Verify GOPATH is set
   363      if ($env:GOPATH.Length -eq 0) { Throw "Missing GOPATH environment variable. See https://golang.org/doc/code.html#GOPATH" }
   364  
   365      # Run autogen if building binaries or running unit tests.
   366      if ($Client -or $Daemon -or $TestUnit) {
   367          Write-Host "INFO: Invoking autogen..."
   368          Try { .\hack\make\.go-autogen.ps1 -CommitString $gitCommit -DockerVersion $dockerVersion -Platform "$env:PLATFORM" }
   369          Catch [Exception] { Throw $_ }
   370      }
   371  
   372      # DCO, Package import and Go formatting tests.
   373      if ($DCO -or $PkgImports -or $GoFormat) {
   374          # We need the head and upstream commits for these
   375          $headCommit=Get-HeadCommit
   376          $upstreamCommit=Get-UpstreamCommit
   377  
   378          # Run DCO validation
   379          if ($DCO) { Validate-DCO $headCommit $upstreamCommit }
   380  
   381          # Run `gofmt` validation
   382          if ($GoFormat) { Validate-GoFormat $headCommit $upstreamCommit }
   383  
   384          # Run pkg isolation validation
   385          if ($PkgImports) { Validate-PkgImports $headCommit $upstreamCommit }
   386      }
   387  
   388      # Build the binaries
   389      if ($Client -or $Daemon) {
   390          # Create the bundles directory if it doesn't exist
   391          if (-not (Test-Path ".\bundles")) { New-Item ".\bundles" -ItemType Directory | Out-Null }
   392  
   393          # Perform the actual build
   394          if ($Daemon) { Execute-Build "daemon" "daemon" "dockerd" }
   395          if ($Client) {
   396              # Get the Docker channel and version from the environment, or use the defaults.
   397              if (-not ($channel = $env:DOCKERCLI_CHANNEL)) { $channel = "edge" }
   398              if (-not ($version = $env:DOCKERCLI_VERSION)) { $version = "17.06.0-ce" }
   399  
   400              # Download the zip file and extract the client executable.
   401              Write-Host "INFO: Downloading docker/cli version $version from $channel..."
   402              $url = "https://download.docker.com/win/static/$channel/x86_64/docker-$version.zip"
   403              Invoke-WebRequest $url -OutFile "docker.zip"
   404              Try {
   405                  Add-Type -AssemblyName System.IO.Compression.FileSystem
   406                  $zip = [System.IO.Compression.ZipFile]::OpenRead("$PWD\docker.zip")
   407                  Try {
   408                      if (-not ($entry = $zip.Entries | Where-Object { $_.Name -eq "docker.exe" })) {
   409                          Throw "Cannot find docker.exe in $url"
   410                      }
   411                      [System.IO.Compression.ZipFileExtensions]::ExtractToFile($entry, "$PWD\bundles\docker.exe", $true)
   412                  }
   413                  Finally {
   414                      $zip.Dispose()
   415                  }
   416              }
   417              Finally {
   418                  Remove-Item -Force "docker.zip"
   419              }
   420          }
   421      }
   422  
   423      # Run unit tests
   424      if ($TestUnit) { Run-UnitTests }
   425  
   426      # Gratuitous ASCII art.
   427      if ($Daemon -or $Client) {
   428          Write-Host
   429          Write-Host -ForegroundColor Green " ________   ____  __."
   430          Write-Host -ForegroundColor Green " \_____  \ `|    `|/ _`|"
   431          Write-Host -ForegroundColor Green " /   `|   \`|      `<"
   432          Write-Host -ForegroundColor Green " /    `|    \    `|  \"
   433          Write-Host -ForegroundColor Green " \_______  /____`|__ \"
   434          Write-Host -ForegroundColor Green "         \/        \/"
   435          Write-Host
   436      }
   437  }
   438  Catch [Exception] {
   439      Write-Host -ForegroundColor Red ("`nERROR: make.ps1 failed:`n$_")
   440  
   441      # More gratuitous ASCII art.
   442      Write-Host
   443      Write-Host -ForegroundColor Red  "___________      .__.__             .___"
   444      Write-Host -ForegroundColor Red  "\_   _____/____  `|__`|  `|   ____   __`| _/"
   445      Write-Host -ForegroundColor Red  " `|    __) \__  \ `|  `|  `| _/ __ \ / __ `| "
   446      Write-Host -ForegroundColor Red  " `|     \   / __ \`|  `|  `|_\  ___// /_/ `| "
   447      Write-Host -ForegroundColor Red  " \___  /  (____  /__`|____/\___  `>____ `| "
   448      Write-Host -ForegroundColor Red  "     \/        \/             \/     \/ "
   449      Write-Host
   450  
   451      Throw $_
   452  }
   453  Finally {
   454      Pop-Location # As we pushed to the root of the repo as the very first thing
   455      if ($global:pushed) { Pop-Location }
   456      Write-Host -ForegroundColor Cyan "INFO: make.ps1 ended at $(Get-Date)"
   457  }