k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/hack/lib/golang.sh (about)

     1  #!/usr/bin/env bash
     2  
     3  # Copyright 2014 The Kubernetes Authors.
     4  #
     5  # Licensed under the Apache License, Version 2.0 (the "License");
     6  # you may not use this file except in compliance with the License.
     7  # You may obtain a copy of the License at
     8  #
     9  #     http://www.apache.org/licenses/LICENSE-2.0
    10  #
    11  # Unless required by applicable law or agreed to in writing, software
    12  # distributed under the License is distributed on an "AS IS" BASIS,
    13  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  # See the License for the specific language governing permissions and
    15  # limitations under the License.
    16  
    17  # shellcheck disable=SC2034 # Variables sourced in other scripts.
    18  
    19  readonly KUBE_GOPATH="${KUBE_GOPATH:-"${KUBE_OUTPUT}/go"}"
    20  export KUBE_GOPATH
    21  
    22  # The server platform we are building on.
    23  readonly KUBE_SUPPORTED_SERVER_PLATFORMS=(
    24    linux/amd64
    25    linux/arm64
    26    linux/s390x
    27    linux/ppc64le
    28  )
    29  
    30  # The node platforms we build for
    31  readonly KUBE_SUPPORTED_NODE_PLATFORMS=(
    32    linux/amd64
    33    linux/arm64
    34    linux/s390x
    35    linux/ppc64le
    36    windows/amd64
    37  )
    38  
    39  # If we update this we should also update the set of platforms whose standard
    40  # library is precompiled for in build/build-image/cross/Dockerfile
    41  readonly KUBE_SUPPORTED_CLIENT_PLATFORMS=(
    42    linux/amd64
    43    linux/386
    44    linux/arm
    45    linux/arm64
    46    linux/s390x
    47    linux/ppc64le
    48    darwin/amd64
    49    darwin/arm64
    50    windows/amd64
    51    windows/386
    52    windows/arm64
    53  )
    54  
    55  # Which platforms we should compile test targets for.
    56  # Not all client platforms need these tests
    57  readonly KUBE_SUPPORTED_TEST_PLATFORMS=(
    58    linux/amd64
    59    linux/arm64
    60    linux/s390x
    61    linux/ppc64le
    62    darwin/amd64
    63    darwin/arm64
    64    windows/amd64
    65    windows/arm64
    66  )
    67  
    68  # The set of server targets that we are only building for Linux
    69  kube::golang::server_targets() {
    70    local targets=(
    71      cmd/kube-proxy
    72      cmd/kube-apiserver
    73      cmd/kube-controller-manager
    74      cmd/kubelet
    75      cmd/kubeadm
    76      cmd/kube-scheduler
    77      staging/src/k8s.io/component-base/logs/kube-log-runner
    78      staging/src/k8s.io/kube-aggregator
    79      staging/src/k8s.io/apiextensions-apiserver
    80      cluster/gce/gci/mounter
    81    )
    82    echo "${targets[@]}"
    83  }
    84  
    85  IFS=" " read -ra KUBE_SERVER_TARGETS <<< "$(kube::golang::server_targets)"
    86  readonly KUBE_SERVER_TARGETS
    87  readonly KUBE_SERVER_BINARIES=("${KUBE_SERVER_TARGETS[@]##*/}")
    88  
    89  # The set of server targets we build docker images for
    90  kube::golang::server_image_targets() {
    91    # NOTE: this contains cmd targets for kube::build::get_docker_wrapped_binaries
    92    local targets=(
    93      cmd/kube-apiserver
    94      cmd/kube-controller-manager
    95      cmd/kube-scheduler
    96      cmd/kube-proxy
    97      cmd/kubectl
    98    )
    99    echo "${targets[@]}"
   100  }
   101  
   102  IFS=" " read -ra KUBE_SERVER_IMAGE_TARGETS <<< "$(kube::golang::server_image_targets)"
   103  readonly KUBE_SERVER_IMAGE_TARGETS
   104  readonly KUBE_SERVER_IMAGE_BINARIES=("${KUBE_SERVER_IMAGE_TARGETS[@]##*/}")
   105  
   106  # The set of conformance targets we build docker image for
   107  kube::golang::conformance_image_targets() {
   108    # NOTE: this contains cmd targets for kube::release::build_conformance_image
   109    local targets=(
   110      ginkgo
   111      test/e2e/e2e.test
   112      test/conformance/image/go-runner
   113      cmd/kubectl
   114    )
   115    echo "${targets[@]}"
   116  }
   117  
   118  IFS=" " read -ra KUBE_CONFORMANCE_IMAGE_TARGETS <<< "$(kube::golang::conformance_image_targets)"
   119  readonly KUBE_CONFORMANCE_IMAGE_TARGETS
   120  
   121  # The set of server targets that we are only building for Kubernetes nodes
   122  kube::golang::node_targets() {
   123    local targets=(
   124      cmd/kube-proxy
   125      cmd/kubeadm
   126      cmd/kubelet
   127      staging/src/k8s.io/component-base/logs/kube-log-runner
   128    )
   129    echo "${targets[@]}"
   130  }
   131  
   132  IFS=" " read -ra KUBE_NODE_TARGETS <<< "$(kube::golang::node_targets)"
   133  readonly KUBE_NODE_TARGETS
   134  readonly KUBE_NODE_BINARIES=("${KUBE_NODE_TARGETS[@]##*/}")
   135  readonly KUBE_NODE_BINARIES_WIN=("${KUBE_NODE_BINARIES[@]/%/.exe}")
   136  
   137  # ------------
   138  # NOTE: All functions that return lists should use newlines.
   139  # bash functions can't return arrays, and spaces are tricky, so newline
   140  # separators are the preferred pattern.
   141  # To transform a string of newline-separated items to an array, use kube::util::read-array:
   142  # kube::util::read-array FOO < <(kube::golang::dups a b c a)
   143  #
   144  # ALWAYS remember to quote your subshells. Not doing so will break in
   145  # bash 4.3, and potentially cause other issues.
   146  # ------------
   147  
   148  # Returns a sorted newline-separated list containing only duplicated items.
   149  kube::golang::dups() {
   150    # We use printf to insert newlines, which are required by sort.
   151    printf "%s\n" "$@" | sort | uniq -d
   152  }
   153  
   154  # Returns a sorted newline-separated list with duplicated items removed.
   155  kube::golang::dedup() {
   156    # We use printf to insert newlines, which are required by sort.
   157    printf "%s\n" "$@" | sort -u
   158  }
   159  
   160  # Depends on values of user-facing KUBE_BUILD_PLATFORMS, KUBE_FASTBUILD,
   161  # and KUBE_BUILDER_OS.
   162  # Configures KUBE_SERVER_PLATFORMS, KUBE_NODE_PLATFOMRS,
   163  # KUBE_TEST_PLATFORMS, and KUBE_CLIENT_PLATFORMS, then sets them
   164  # to readonly.
   165  # The configured vars will only contain platforms allowed by the
   166  # KUBE_SUPPORTED* vars at the top of this file.
   167  declare -a KUBE_SERVER_PLATFORMS
   168  declare -a KUBE_CLIENT_PLATFORMS
   169  declare -a KUBE_NODE_PLATFORMS
   170  declare -a KUBE_TEST_PLATFORMS
   171  kube::golang::setup_platforms() {
   172    if [[ -n "${KUBE_BUILD_PLATFORMS:-}" ]]; then
   173      # KUBE_BUILD_PLATFORMS needs to be read into an array before the next
   174      # step, or quoting treats it all as one element.
   175      local -a platforms
   176      IFS=" " read -ra platforms <<< "${KUBE_BUILD_PLATFORMS}"
   177  
   178      # Deduplicate to ensure the intersection trick with kube::golang::dups
   179      # is not defeated by duplicates in user input.
   180      kube::util::read-array platforms < <(kube::golang::dedup "${platforms[@]}")
   181  
   182      # Use kube::golang::dups to restrict the builds to the platforms in
   183      # KUBE_SUPPORTED_*_PLATFORMS. Items should only appear at most once in each
   184      # set, so if they appear twice after the merge they are in the intersection.
   185      kube::util::read-array KUBE_SERVER_PLATFORMS < <(kube::golang::dups \
   186          "${platforms[@]}" \
   187          "${KUBE_SUPPORTED_SERVER_PLATFORMS[@]}" \
   188        )
   189      readonly KUBE_SERVER_PLATFORMS
   190  
   191      kube::util::read-array KUBE_NODE_PLATFORMS < <(kube::golang::dups \
   192          "${platforms[@]}" \
   193          "${KUBE_SUPPORTED_NODE_PLATFORMS[@]}" \
   194        )
   195      readonly KUBE_NODE_PLATFORMS
   196  
   197      kube::util::read-array KUBE_TEST_PLATFORMS < <(kube::golang::dups \
   198          "${platforms[@]}" \
   199          "${KUBE_SUPPORTED_TEST_PLATFORMS[@]}" \
   200        )
   201      readonly KUBE_TEST_PLATFORMS
   202  
   203      kube::util::read-array KUBE_CLIENT_PLATFORMS < <(kube::golang::dups \
   204          "${platforms[@]}" \
   205          "${KUBE_SUPPORTED_CLIENT_PLATFORMS[@]}" \
   206        )
   207      readonly KUBE_CLIENT_PLATFORMS
   208  
   209    elif [[ "${KUBE_FASTBUILD:-}" == "true" ]]; then
   210      host_arch=$(kube::util::host_arch)
   211      if [[ "${host_arch}" != "amd64" && "${host_arch}" != "arm64" && "${host_arch}" != "ppc64le" && "${host_arch}" != "s390x" ]]; then
   212        # on any platform other than amd64, arm64, ppc64le and s390x, we just default to amd64
   213        host_arch="amd64"
   214      fi
   215      KUBE_SERVER_PLATFORMS=("linux/${host_arch}")
   216      readonly KUBE_SERVER_PLATFORMS
   217      KUBE_NODE_PLATFORMS=("linux/${host_arch}")
   218      readonly KUBE_NODE_PLATFORMS
   219      if [[ "${KUBE_BUILDER_OS:-}" == "darwin"* ]]; then
   220        KUBE_TEST_PLATFORMS=(
   221          "darwin/${host_arch}"
   222          "linux/${host_arch}"
   223        )
   224        readonly KUBE_TEST_PLATFORMS
   225        KUBE_CLIENT_PLATFORMS=(
   226          "darwin/${host_arch}"
   227          "linux/${host_arch}"
   228        )
   229        readonly KUBE_CLIENT_PLATFORMS
   230      else
   231        KUBE_TEST_PLATFORMS=("linux/${host_arch}")
   232        readonly KUBE_TEST_PLATFORMS
   233        KUBE_CLIENT_PLATFORMS=("linux/${host_arch}")
   234        readonly KUBE_CLIENT_PLATFORMS
   235      fi
   236    else
   237      KUBE_SERVER_PLATFORMS=("${KUBE_SUPPORTED_SERVER_PLATFORMS[@]}")
   238      readonly KUBE_SERVER_PLATFORMS
   239  
   240      KUBE_NODE_PLATFORMS=("${KUBE_SUPPORTED_NODE_PLATFORMS[@]}")
   241      readonly KUBE_NODE_PLATFORMS
   242  
   243      KUBE_CLIENT_PLATFORMS=("${KUBE_SUPPORTED_CLIENT_PLATFORMS[@]}")
   244      readonly KUBE_CLIENT_PLATFORMS
   245  
   246      KUBE_TEST_PLATFORMS=("${KUBE_SUPPORTED_TEST_PLATFORMS[@]}")
   247      readonly KUBE_TEST_PLATFORMS
   248    fi
   249  }
   250  
   251  kube::golang::setup_platforms
   252  
   253  # The set of client targets that we are building for all platforms
   254  readonly KUBE_CLIENT_TARGETS=(
   255    cmd/kubectl
   256    cmd/kubectl-convert
   257  )
   258  readonly KUBE_CLIENT_BINARIES=("${KUBE_CLIENT_TARGETS[@]##*/}")
   259  readonly KUBE_CLIENT_BINARIES_WIN=("${KUBE_CLIENT_BINARIES[@]/%/.exe}")
   260  
   261  # The set of test targets that we are building for all platforms
   262  kube::golang::test_targets() {
   263    local targets=(
   264      ginkgo
   265      test/e2e/e2e.test
   266      test/conformance/image/go-runner
   267    )
   268    echo "${targets[@]}"
   269  }
   270  IFS=" " read -ra KUBE_TEST_TARGETS <<< "$(kube::golang::test_targets)"
   271  readonly KUBE_TEST_TARGETS
   272  readonly KUBE_TEST_BINARIES=("${KUBE_TEST_TARGETS[@]##*/}")
   273  readonly KUBE_TEST_BINARIES_WIN=("${KUBE_TEST_BINARIES[@]/%/.exe}")
   274  readonly KUBE_TEST_PORTABLE=(
   275    test/e2e/testing-manifests
   276    test/kubemark
   277    hack/e2e-internal
   278    hack/get-build.sh
   279    hack/ginkgo-e2e.sh
   280    hack/lib
   281  )
   282  
   283  # Test targets which run on the Kubernetes clusters directly, so we only
   284  # need to target server platforms.
   285  # These binaries will be distributed in the kubernetes-test tarball.
   286  kube::golang::server_test_targets() {
   287    local targets=(
   288      cmd/kubemark
   289      ginkgo
   290    )
   291  
   292    if [[ "${OSTYPE:-}" == "linux"* ]]; then
   293      targets+=( test/e2e_node/e2e_node.test )
   294    fi
   295  
   296    echo "${targets[@]}"
   297  }
   298  
   299  IFS=" " read -ra KUBE_TEST_SERVER_TARGETS <<< "$(kube::golang::server_test_targets)"
   300  readonly KUBE_TEST_SERVER_TARGETS
   301  readonly KUBE_TEST_SERVER_BINARIES=("${KUBE_TEST_SERVER_TARGETS[@]##*/}")
   302  readonly KUBE_TEST_SERVER_PLATFORMS=("${KUBE_SERVER_PLATFORMS[@]:+"${KUBE_SERVER_PLATFORMS[@]}"}")
   303  
   304  # Gigabytes necessary for parallel platform builds.
   305  # As of March 2021 (go 1.16/amd64), the RSS usage is 2GiB by using cached
   306  # memory of 15GiB.
   307  # This variable can be overwritten at your own risk.
   308  # It's defaulting to 20G to provide some headroom.
   309  readonly KUBE_PARALLEL_BUILD_MEMORY=${KUBE_PARALLEL_BUILD_MEMORY:-20}
   310  
   311  readonly KUBE_ALL_TARGETS=(
   312    "${KUBE_SERVER_TARGETS[@]}"
   313    "${KUBE_CLIENT_TARGETS[@]}"
   314    "${KUBE_TEST_TARGETS[@]}"
   315    "${KUBE_TEST_SERVER_TARGETS[@]}"
   316  )
   317  readonly KUBE_ALL_BINARIES=("${KUBE_ALL_TARGETS[@]##*/}")
   318  
   319  readonly KUBE_STATIC_BINARIES=(
   320    apiextensions-apiserver
   321    kube-aggregator
   322    kube-apiserver
   323    kube-controller-manager
   324    kube-scheduler
   325    kube-proxy
   326    kube-log-runner
   327    kubeadm
   328    kubectl
   329    kubectl-convert
   330    kubemark
   331    mounter
   332  )
   333  
   334  # Fully-qualified package names that we want to instrument for coverage information.
   335  readonly KUBE_COVERAGE_INSTRUMENTED_PACKAGES=(
   336    k8s.io/kubernetes/cmd/kube-apiserver
   337    k8s.io/kubernetes/cmd/kube-controller-manager
   338    k8s.io/kubernetes/cmd/kube-scheduler
   339    k8s.io/kubernetes/cmd/kube-proxy
   340    k8s.io/kubernetes/cmd/kubelet
   341  )
   342  
   343  # KUBE_CGO_OVERRIDES is a space-separated list of binaries which should be built
   344  # with CGO enabled, assuming CGO is supported on the target platform.
   345  # This overrides any entry in KUBE_STATIC_BINARIES.
   346  IFS=" " read -ra KUBE_CGO_OVERRIDES_LIST <<< "${KUBE_CGO_OVERRIDES:-}"
   347  readonly KUBE_CGO_OVERRIDES_LIST
   348  # KUBE_STATIC_OVERRIDES is a space-separated list of binaries which should be
   349  # built with CGO disabled. This is in addition to the list in
   350  # KUBE_STATIC_BINARIES.
   351  IFS=" " read -ra KUBE_STATIC_OVERRIDES_LIST <<< "${KUBE_STATIC_OVERRIDES:-}"
   352  readonly KUBE_STATIC_OVERRIDES_LIST
   353  
   354  kube::golang::is_statically_linked() {
   355    local e
   356    # Explicitly enable cgo when building kubectl for darwin from darwin.
   357    [[ "$(go env GOHOSTOS)" == "darwin" && "$(go env GOOS)" == "darwin" &&
   358      "$1" == *"/kubectl" ]] && return 1
   359    if [[ -n "${KUBE_CGO_OVERRIDES_LIST:+x}" ]]; then
   360      for e in "${KUBE_CGO_OVERRIDES_LIST[@]}"; do [[ "${1}" == *"/${e}" ]] && return 1; done;
   361    fi
   362    for e in "${KUBE_STATIC_BINARIES[@]}"; do [[ "${1}" == *"/${e}" ]] && return 0; done;
   363    if [[ -n "${KUBE_STATIC_OVERRIDES_LIST:+x}" ]]; then
   364      for e in "${KUBE_STATIC_OVERRIDES_LIST[@]}"; do [[ "${1}" == *"/${e}" ]] && return 0; done;
   365    fi
   366    return 1;
   367  }
   368  
   369  # kube::golang::best_guess_go_targets takes a list of build targets, which might
   370  # be Go-style names (e.g. example.com/foo/bar or ./foo/bar) or just local paths
   371  # (e.g. foo/bar) and produces a respective list (on stdout) of our best guess at
   372  # Go target names.
   373  kube::golang::best_guess_go_targets() {
   374    local target
   375    for target; do
   376      if [ "${target}" = "ginkgo" ] ||
   377         [ "${target}" = "github.com/onsi/ginkgo/ginkgo" ] ||
   378         [ "${target}" = "vendor/github.com/onsi/ginkgo/ginkgo" ]; then
   379        # Aliases that build the ginkgo CLI for hack/ginkgo-e2e.sh.
   380        # "ginkgo" is the one that is documented in the Makefile. The others
   381        # are for backwards compatibility.
   382        echo "github.com/onsi/ginkgo/v2/ginkgo"
   383        continue
   384      fi
   385  
   386      if [[ "${target}" =~ ^([[:alnum:]]+".")+[[:alnum:]]+"/" ]]; then
   387        # If the target starts with what looks like a domain name, assume it has a
   388        # fully-qualified Go package name.
   389        echo "${target}"
   390        continue
   391      fi
   392  
   393      if [[ "${target}" =~ ^vendor/ ]]; then
   394        # Strip vendor/ prefix, since we're building in gomodule mode.  This is
   395        # for backwards compatibility.
   396        echo "${target#"vendor/"}"
   397        continue
   398      fi
   399  
   400      # If the target starts with "./", assume it is a local path which qualifies
   401      # as a Go target name.
   402      if [[ "${target}" =~ ^\./ ]]; then
   403        echo "${target}"
   404        continue
   405      fi
   406  
   407      # Otherwise assume it's a relative path (e.g. foo/bar or foo/bar/bar.test).
   408      # We probably SHOULDN'T accept this, but we did in the past and it would be
   409      # rude to break things if we don't NEED to.  We can't really test if it
   410      # exists or not, because the last element might be an output file (e.g.
   411      # bar.test) or even "...".
   412      echo "./${target}"
   413    done
   414  }
   415  
   416  # kube::golang::normalize_go_targets takes a list of build targets, which might
   417  # be Go-style names (e.g. example.com/foo/bar or ./foo/bar) or just local paths
   418  # (e.g. foo/bar) and produces a respective list (on stdout) of Go package
   419  # names.
   420  #
   421  # If this cannot find (go list -find -e) one or more inputs, it will emit the
   422  # them on stdout, so callers can at least get a useful error.
   423  kube::golang::normalize_go_targets() {
   424    local targets=()
   425    kube::util::read-array targets < <(kube::golang::best_guess_go_targets "$@")
   426    kube::util::read-array targets < <(kube::golang::dedup "${targets[@]}")
   427    set -- "${targets[@]}"
   428  
   429    for target; do
   430      if [[ "${target}" =~ ".test"$ ]]; then
   431        local dir
   432        dir="$(dirname "${target}")"
   433        local tst
   434        tst="$(basename "${target}")"
   435        local pkg
   436        pkg="$(go list -find -e "${dir}")"
   437        echo "${pkg}/${tst}"
   438        continue
   439      fi
   440      if [[ "${target}" =~ "/..."$ ]]; then
   441        local dir
   442        dir="$(dirname "${target}")"
   443        local pkg
   444        pkg="$(go list -find -e "${dir}")"
   445        echo "${pkg}/..."
   446        continue
   447      fi
   448      go list -find -e "${target}"
   449    done
   450  }
   451  
   452  # Asks golang what it thinks the host platform is. The go tool chain does some
   453  # slightly different things when the target platform matches the host platform.
   454  kube::golang::host_platform() {
   455    echo "$(go env GOHOSTOS)/$(go env GOHOSTARCH)"
   456  }
   457  
   458  # Takes the platform name ($1) and sets the appropriate golang env variables
   459  # for that platform.
   460  kube::golang::set_platform_envs() {
   461    [[ -n ${1-} ]] || {
   462      kube::log::error_exit "!!! Internal error. No platform set in kube::golang::set_platform_envs"
   463    }
   464  
   465    export GOOS=${platform%/*}
   466    export GOARCH=${platform##*/}
   467  
   468    # Do not set CC when building natively on a platform, only if cross-compiling
   469    if [[ $(kube::golang::host_platform) != "$platform" ]]; then
   470      # Dynamic CGO linking for other server architectures than host architecture goes here
   471      # If you want to include support for more server platforms than these, add arch-specific gcc names here
   472      case "${platform}" in
   473        "linux/amd64")
   474          export CGO_ENABLED=1
   475          export CC=${KUBE_LINUX_AMD64_CC:-x86_64-linux-gnu-gcc}
   476          ;;
   477        "linux/arm")
   478          export CGO_ENABLED=1
   479          export CC=${KUBE_LINUX_ARM_CC:-arm-linux-gnueabihf-gcc}
   480          ;;
   481        "linux/arm64")
   482          export CGO_ENABLED=1
   483          export CC=${KUBE_LINUX_ARM64_CC:-aarch64-linux-gnu-gcc}
   484          ;;
   485        "linux/ppc64le")
   486          export CGO_ENABLED=1
   487          export CC=${KUBE_LINUX_PPC64LE_CC:-powerpc64le-linux-gnu-gcc}
   488          ;;
   489        "linux/s390x")
   490          export CGO_ENABLED=1
   491          export CC=${KUBE_LINUX_S390X_CC:-s390x-linux-gnu-gcc}
   492          ;;
   493      esac
   494    fi
   495  
   496    # if CC is defined for platform then always enable it
   497    ccenv=$(echo "$platform" | awk -F/ '{print "KUBE_" toupper($1) "_" toupper($2) "_CC"}')
   498    if [ -n "${!ccenv-}" ]; then 
   499      export CGO_ENABLED=1
   500      export CC="${!ccenv}"
   501    fi
   502  }
   503  
   504  # Ensure the go tool exists and is a viable version.
   505  # Inputs:
   506  #   env-var GO_VERSION is the desired go version to use, downloading it if needed (defaults to content of .go-version)
   507  #   env-var FORCE_HOST_GO set to a non-empty value uses the go version in the $PATH and skips ensuring $GO_VERSION is used
   508  kube::golang::internal::verify_go_version() {
   509    # default GO_VERSION to content of .go-version
   510    GO_VERSION="${GO_VERSION:-"$(cat "${KUBE_ROOT}/.go-version")"}"
   511    if [ "${GOTOOLCHAIN:-auto}" != 'auto' ]; then
   512      # no-op, just respect GOTOOLCHAIN
   513      :
   514    elif [ -n "${FORCE_HOST_GO:-}" ]; then
   515      # ensure existing host version is used, like before GOTOOLCHAIN existed
   516      export GOTOOLCHAIN='local'
   517    else
   518      # otherwise, we want to ensure the go version matches GO_VERSION
   519      GOTOOLCHAIN="go${GO_VERSION}"
   520      export GOTOOLCHAIN
   521      # if go is either not installed or too old to respect GOTOOLCHAIN then use gimme
   522      if ! (command -v go >/dev/null && [ "$(go version | cut -d' ' -f3)" = "${GOTOOLCHAIN}" ]); then
   523        export GIMME_ENV_PREFIX=${GIMME_ENV_PREFIX:-"${KUBE_OUTPUT}/.gimme/envs"}
   524        export GIMME_VERSION_PREFIX=${GIMME_VERSION_PREFIX:-"${KUBE_OUTPUT}/.gimme/versions"}
   525        # eval because the output of this is shell to set PATH etc.
   526        eval "$("${KUBE_ROOT}/third_party/gimme/gimme" "${GO_VERSION}")"
   527      fi
   528    fi
   529  
   530    if [[ -z "$(command -v go)" ]]; then
   531      kube::log::usage_from_stdin <<EOF
   532  Can't find 'go' in PATH, please fix and retry.
   533  See http://golang.org/doc/install for installation instructions.
   534  EOF
   535      return 2
   536    fi
   537  
   538    local go_version
   539    IFS=" " read -ra go_version <<< "$(GOFLAGS='' go version)"
   540    local minimum_go_version
   541    minimum_go_version=go1.22
   542    if [[ "${minimum_go_version}" != $(echo -e "${minimum_go_version}\n${go_version[2]}" | sort -s -t. -k 1,1 -k 2,2n -k 3,3n | head -n1) && "${go_version[2]}" != "devel" ]]; then
   543      kube::log::usage_from_stdin <<EOF
   544  Detected go version: ${go_version[*]}.
   545  Kubernetes requires ${minimum_go_version} or greater.
   546  Please install ${minimum_go_version} or later.
   547  EOF
   548      return 2
   549    fi
   550  }
   551  
   552  # kube::golang::setup_env will check that the `go` commands is available in
   553  # ${PATH}. It will also check that the Go version is good enough for the
   554  # Kubernetes build.
   555  #
   556  # Outputs:
   557  #   env-var GOPATH points to our local output dir
   558  #   env-var GOBIN is unset (we want binaries in a predictable place)
   559  #   env-var PATH includes the local GOPATH
   560  kube::golang::setup_env() {
   561    # Even in module mode, we need to set GOPATH for `go build` and `go install`
   562    # to work.  We build various tools (usually via `go install`) from a lot of
   563    # scripts.
   564    #   * We can't just set GOBIN because that does not work on cross-compiles.
   565    #   * We could always use `go build -o <something>`, but it's subtle wrt
   566    #     cross-compiles and whether the <something> is a file or a directory,
   567    #     and EVERY caller has to get it *just* right.
   568    #   * We could leave GOPATH alone and let `go install` write binaries
   569    #     wherever the user's GOPATH says (or doesn't say).
   570    #
   571    # Instead we set it to a phony local path and process the results ourselves.
   572    # In particular, GOPATH[0]/bin will be used for `go install`, with
   573    # cross-compiles adding an extra directory under that.
   574    export GOPATH="${KUBE_GOPATH}"
   575  
   576    # If these are not set, set them now.  This ensures that any subsequent
   577    # scripts we run (which may call this function again) use the same values.
   578    export GOCACHE="${GOCACHE:-"${KUBE_GOPATH}/cache/build"}"
   579    export GOMODCACHE="${GOMODCACHE:-"${KUBE_GOPATH}/cache/mod"}"
   580  
   581    # Make sure our own Go binaries are in PATH.
   582    export PATH="${KUBE_GOPATH}/bin:${PATH}"
   583  
   584    # Unset GOBIN in case it already exists in the current session.
   585    # Cross-compiles will not work with it set.
   586    unset GOBIN
   587  
   588    # Turn on modules and workspaces (both are default-on).
   589    unset GO111MODULE
   590    unset GOWORK
   591  
   592    # This may try to download our specific Go version.  Do it last so it uses
   593    # the above-configured environment.
   594    kube::golang::internal::verify_go_version
   595  }
   596  
   597  kube::golang::setup_gomaxprocs() {
   598    # GOMAXPROCS by default does not reflect the number of cpu(s) available
   599    # when running in a container, please see https://github.com/golang/go/issues/33803
   600    if [[ -z "${GOMAXPROCS:-}" ]]; then
   601      if ! command -v ncpu >/dev/null 2>&1; then
   602        go -C "${KUBE_ROOT}/hack/tools" install ./ncpu || echo "Will not automatically set GOMAXPROCS"
   603      fi
   604      if command -v ncpu >/dev/null 2>&1; then
   605        GOMAXPROCS=$(ncpu)
   606        export GOMAXPROCS
   607        kube::log::status "Set GOMAXPROCS automatically to ${GOMAXPROCS}"
   608      fi
   609    fi
   610  }
   611  
   612  # This will take binaries from $GOPATH/bin and copy them to the appropriate
   613  # place in ${KUBE_OUTPUT_BIN}
   614  #
   615  # Ideally this wouldn't be necessary and we could just set GOBIN to
   616  # KUBE_OUTPUT_BIN but that won't work in the face of cross compilation.  'go
   617  # install' will place binaries that match the host platform directly in $GOBIN
   618  # while placing cross compiled binaries into `platform_arch` subdirs.  This
   619  # complicates pretty much everything else we do around packaging and such.
   620  kube::golang::place_bins() {
   621    local host_platform
   622    host_platform=$(kube::golang::host_platform)
   623  
   624    V=2 kube::log::status "Placing binaries"
   625  
   626    local platform
   627    for platform in "${KUBE_CLIENT_PLATFORMS[@]}"; do
   628      # The substitution on platform_src below will replace all slashes with
   629      # underscores.  It'll transform darwin/amd64 -> darwin_amd64.
   630      local platform_src="/${platform//\//_}"
   631      if [[ "${platform}" == "${host_platform}" ]]; then
   632        platform_src=""
   633        rm -f "${THIS_PLATFORM_BIN}"
   634        mkdir -p "$(dirname "${THIS_PLATFORM_BIN}")"
   635        ln -s "${KUBE_OUTPUT_BIN}/${platform}" "${THIS_PLATFORM_BIN}"
   636      fi
   637  
   638      V=3 kube::log::status "Placing binaries for ${platform} in ${KUBE_OUTPUT_BIN}/${platform}"
   639      local full_binpath_src="${KUBE_GOPATH}/bin${platform_src}"
   640      if [[ -d "${full_binpath_src}" ]]; then
   641        mkdir -p "${KUBE_OUTPUT_BIN}/${platform}"
   642        find "${full_binpath_src}" -maxdepth 1 -type f -exec \
   643          rsync -pc {} "${KUBE_OUTPUT_BIN}/${platform}" \;
   644      fi
   645    done
   646  }
   647  
   648  # Try and replicate the native binary placement of go install without
   649  # calling go install.
   650  kube::golang::outfile_for_binary() {
   651    local binary=$1
   652    local platform=$2
   653    local output_path="${KUBE_GOPATH}/bin"
   654    local bin
   655    bin=$(basename "${binary}")
   656    if [[ "${platform}" != "${host_platform}" ]]; then
   657      output_path="${output_path}/${platform//\//_}"
   658    fi
   659    if [[ ${GOOS} == "windows" ]]; then
   660      bin="${bin}.exe"
   661    fi
   662    echo "${output_path}/${bin}"
   663  }
   664  
   665  # Argument: the name of a Kubernetes package.
   666  # Returns 0 if the binary can be built with coverage, 1 otherwise.
   667  # NB: this ignores whether coverage is globally enabled or not.
   668  kube::golang::is_instrumented_package() {
   669    if kube::util::array_contains "$1" "${KUBE_COVERAGE_INSTRUMENTED_PACKAGES[@]}"; then
   670      return 0
   671    fi
   672    # Some cases, like `make kubectl`, pass $1 as "./cmd/kubectl" rather than
   673    # "k8s.io/kubernetes/kubectl".  Try to normalize and handle that.  We don't
   674    # do this always because it is a bit slow.
   675    pkg=$(go list -find "$1")
   676    if kube::util::array_contains "${pkg}" "${KUBE_COVERAGE_INSTRUMENTED_PACKAGES[@]}"; then
   677      return 0
   678    fi
   679    return 1
   680  }
   681  
   682  # Argument: the name of a Kubernetes package (e.g. k8s.io/kubernetes/cmd/kube-scheduler)
   683  # Echos the path to a dummy test used for coverage information.
   684  kube::golang::path_for_coverage_dummy_test() {
   685    local package="$1"
   686    local path
   687    path=$(go list -find -f '{{.Dir}}' "${package}")
   688    local name
   689    name=$(basename "${package}")
   690    echo "${path}/zz_generated_${name}_test.go"
   691  }
   692  
   693  # Argument: the name of a Kubernetes package (e.g. k8s.io/kubernetes/cmd/kube-scheduler).
   694  # Creates a dummy unit test on disk in the source directory for the given package.
   695  # This unit test will invoke the package's standard entry point when run.
   696  kube::golang::create_coverage_dummy_test() {
   697    local package="$1"
   698    local name
   699    name="$(basename "${package}")"
   700    cat <<EOF > "$(kube::golang::path_for_coverage_dummy_test "${package}")"
   701  package main
   702  import (
   703    "testing"
   704    "k8s.io/kubernetes/pkg/util/coverage"
   705  )
   706  
   707  func TestMain(m *testing.M) {
   708    // Get coverage running
   709    coverage.InitCoverage("${name}")
   710  
   711    // Go!
   712    main()
   713  
   714    // Make sure we actually write the profiling information to disk, if we make it here.
   715    // On long-running services, or anything that calls os.Exit(), this is insufficient,
   716    // so we also flush periodically with a default period of five seconds (configurable by
   717    // the KUBE_COVERAGE_FLUSH_INTERVAL environment variable).
   718    coverage.FlushCoverage()
   719  }
   720  EOF
   721  }
   722  
   723  # Argument: the name of a Kubernetes package (e.g. k8s.io/kubernetes/cmd/kube-scheduler).
   724  # Deletes a test generated by kube::golang::create_coverage_dummy_test.
   725  # It is not an error to call this for a nonexistent test.
   726  kube::golang::delete_coverage_dummy_test() {
   727    local package="$1"
   728    rm -f "$(kube::golang::path_for_coverage_dummy_test "${package}")"
   729  }
   730  
   731  # Arguments: a list of kubernetes packages to build.
   732  # Expected variables: ${build_args} should be set to an array of Go build arguments.
   733  # In addition, ${package} and ${platform} should have been set earlier, and if
   734  # ${KUBE_BUILD_WITH_COVERAGE} is set, coverage instrumentation will be enabled.
   735  #
   736  # Invokes Go to actually build some packages. If coverage is disabled, simply invokes
   737  # go install. If coverage is enabled, builds covered binaries using go test, temporarily
   738  # producing the required unit test files and then cleaning up after itself.
   739  # Non-covered binaries are then built using go install as usual.
   740  #
   741  # See comments in kube::golang::setup_env regarding where built binaries go.
   742  kube::golang::build_some_binaries() {
   743    if [[ -n "${KUBE_BUILD_WITH_COVERAGE:-}" ]]; then
   744      local -a uncovered=()
   745      for package in "$@"; do
   746        if kube::golang::is_instrumented_package "${package}"; then
   747          V=2 kube::log::info "Building ${package} with coverage..."
   748  
   749          kube::golang::create_coverage_dummy_test "${package}"
   750          kube::util::trap_add "kube::golang::delete_coverage_dummy_test \"${package}\"" EXIT
   751  
   752          go test -c -o "$(kube::golang::outfile_for_binary "${package}" "${platform}")" \
   753            -covermode count \
   754            -coverpkg k8s.io/... \
   755            "${build_args[@]}" \
   756            -tags coverage \
   757            "${package}"
   758        else
   759          uncovered+=("${package}")
   760        fi
   761      done
   762      if [[ "${#uncovered[@]}" != 0 ]]; then
   763        V=2 kube::log::info "Building ${uncovered[*]} without coverage..."
   764        GOPROXY=off go install "${build_args[@]}" "${uncovered[@]}"
   765      else
   766        V=2 kube::log::info "Nothing to build without coverage."
   767      fi
   768    else
   769      V=2 kube::log::info "Coverage is disabled."
   770      GOPROXY=off go install "${build_args[@]}" "$@"
   771    fi
   772  }
   773  
   774  # Args:
   775  #  $1: platform (e.g. darwin/amd64)
   776  kube::golang::build_binaries_for_platform() {
   777    # This is for sanity.  Without it, user umasks can leak through.
   778    umask 0022
   779  
   780    local platform=$1
   781  
   782    local -a statics=()
   783    local -a nonstatics=()
   784    local -a tests=()
   785  
   786    for binary in "${binaries[@]}"; do
   787      if [[ "${binary}" =~ ".test"$ ]]; then
   788        tests+=("${binary}")
   789        kube::log::info "    ${binary} (test)"
   790      elif kube::golang::is_statically_linked "${binary}"; then
   791        statics+=("${binary}")
   792        kube::log::info "    ${binary} (static)"
   793      else
   794        nonstatics+=("${binary}")
   795        kube::log::info "    ${binary} (non-static)"
   796      fi
   797     done
   798  
   799    V=2 kube::log::info "Env for ${platform}: GOPATH=${GOPATH-} GOOS=${GOOS-} GOARCH=${GOARCH-} GOROOT=${GOROOT-} CGO_ENABLED=${CGO_ENABLED-} CC=${CC-}"
   800    V=3 kube::log::info "Building binaries with GCFLAGS=${gogcflags} LDFLAGS=${goldflags}"
   801  
   802    local -a build_args
   803    if [[ "${#statics[@]}" != 0 ]]; then
   804      build_args=(
   805        -installsuffix=static
   806        ${goflags:+"${goflags[@]}"}
   807        -gcflags="${gogcflags}"
   808        -ldflags="${goldflags}"
   809        -tags="${gotags:-}"
   810      )
   811      CGO_ENABLED=0 kube::golang::build_some_binaries "${statics[@]}"
   812    fi
   813  
   814    if [[ "${#nonstatics[@]}" != 0 ]]; then
   815      build_args=(
   816        ${goflags:+"${goflags[@]}"}
   817        -gcflags="${gogcflags}"
   818        -ldflags="${goldflags}"
   819        -tags="${gotags:-}"
   820      )
   821      kube::golang::build_some_binaries "${nonstatics[@]}"
   822    fi
   823  
   824    for test in "${tests[@]:+${tests[@]}}"; do
   825      local outfile testpkg
   826      outfile=$(kube::golang::outfile_for_binary "${test}" "${platform}")
   827      testpkg=$(dirname "${test}")
   828  
   829      mkdir -p "$(dirname "${outfile}")"
   830      go test -c \
   831        ${goflags:+"${goflags[@]}"} \
   832        -gcflags="${gogcflags}" \
   833        -ldflags="${goldflags}" \
   834        -tags="${gotags:-}" \
   835        -o "${outfile}" \
   836        "${testpkg}"
   837    done
   838  }
   839  
   840  # Return approximate physical memory available in gigabytes.
   841  kube::golang::get_physmem() {
   842    local mem
   843  
   844    # Linux kernel version >=3.14, in kb
   845    if mem=$(grep MemAvailable /proc/meminfo | awk '{ print $2 }'); then
   846      echo $(( mem / 1048576 ))
   847      return
   848    fi
   849  
   850    # Linux, in kb
   851    if mem=$(grep MemTotal /proc/meminfo | awk '{ print $2 }'); then
   852      echo $(( mem / 1048576 ))
   853      return
   854    fi
   855  
   856    # OS X, in bytes. Note that get_physmem, as used, should only ever
   857    # run in a Linux container (because it's only used in the multiple
   858    # platform case, which is a Dockerized build), but this is provided
   859    # for completeness.
   860    if mem=$(sysctl -n hw.memsize 2>/dev/null); then
   861      echo $(( mem / 1073741824 ))
   862      return
   863    fi
   864  
   865    # If we can't infer it, just give up and assume a low memory system
   866    echo 1
   867  }
   868  
   869  # Build binaries targets specified
   870  #
   871  # Input:
   872  #   $@ - targets and go flags.  If no targets are set then all binaries targets
   873  #     are built.
   874  #   KUBE_BUILD_PLATFORMS - Incoming variable of targets to build for.  If unset
   875  #     then just the host architecture is built.
   876  kube::golang::build_binaries() {
   877    V=2 kube::log::info "Go version: $(GOFLAGS='' go version)"
   878  
   879    local host_platform
   880    host_platform=$(kube::golang::host_platform)
   881  
   882    # These are "local" but are visible to and relied on by functions this
   883    # function calls.  They are effectively part of the calling API to
   884    # build_binaries_for_platform.
   885    local goflags goldflags gogcflags gotags
   886  
   887    goflags=()
   888    gogcflags="${GOGCFLAGS:-}"
   889    goldflags="all=$(kube::version::ldflags) ${GOLDFLAGS:-}"
   890  
   891    if [[ "${DBG:-}" == 1 ]]; then
   892        # Debugging - disable optimizations and inlining and trimPath
   893        gogcflags="${gogcflags} all=-N -l"
   894    else
   895        # Not debugging - disable symbols and DWARF, trim embedded paths
   896        goldflags="${goldflags} -s -w"
   897        goflags+=("-trimpath")
   898    fi
   899  
   900    # Extract tags if any specified in GOFLAGS
   901    gotags="selinux,notest,$(echo "${GOFLAGS:-}" | sed -ne 's|.*-tags=\([^-]*\).*|\1|p')"
   902  
   903    local -a targets=()
   904    local arg
   905  
   906    for arg; do
   907      if [[ "${arg}" == -* ]]; then
   908        # Assume arguments starting with a dash are flags to pass to go.
   909        goflags+=("${arg}")
   910      else
   911        targets+=("${arg}")
   912      fi
   913    done
   914  
   915    local -a platforms
   916    IFS=" " read -ra platforms <<< "${KUBE_BUILD_PLATFORMS:-}"
   917    if [[ ${#platforms[@]} -eq 0 ]]; then
   918      platforms=("${host_platform}")
   919    fi
   920  
   921    if [[ ${#targets[@]} -eq 0 ]]; then
   922      targets=("${KUBE_ALL_TARGETS[@]}")
   923    fi
   924    kube::util::read-array targets < <(kube::golang::dedup "${targets[@]}")
   925  
   926    local -a binaries
   927    kube::util::read-array binaries < <(kube::golang::normalize_go_targets "${targets[@]}")
   928    kube::util::read-array binaries < <(kube::golang::dedup "${binaries[@]}")
   929  
   930    local parallel=false
   931    if [[ ${#platforms[@]} -gt 1 ]]; then
   932      local gigs
   933      gigs=$(kube::golang::get_physmem)
   934  
   935      if [[ ${gigs} -ge ${KUBE_PARALLEL_BUILD_MEMORY} ]]; then
   936        kube::log::status "Multiple platforms requested and available ${gigs}G >= threshold ${KUBE_PARALLEL_BUILD_MEMORY}G, building platforms in parallel"
   937        parallel=true
   938      else
   939        kube::log::status "Multiple platforms requested, but available ${gigs}G < threshold ${KUBE_PARALLEL_BUILD_MEMORY}G, building platforms in serial"
   940        parallel=false
   941      fi
   942    fi
   943  
   944    if [[ "${parallel}" == "true" ]]; then
   945      kube::log::status "Building go targets for {${platforms[*]}} in parallel (output will appear in a burst when complete):" "${targets[@]}"
   946      local platform
   947      for platform in "${platforms[@]}"; do (
   948          kube::golang::set_platform_envs "${platform}"
   949          kube::log::status "${platform}: build started"
   950          kube::golang::build_binaries_for_platform "${platform}"
   951          kube::log::status "${platform}: build finished"
   952        ) &> "/tmp//${platform//\//_}.build" &
   953      done
   954  
   955      local fails=0
   956      for job in $(jobs -p); do
   957        wait "${job}" || (( fails+=1 ))
   958      done
   959  
   960      for platform in "${platforms[@]}"; do
   961        cat "/tmp//${platform//\//_}.build"
   962      done
   963  
   964      return "${fails}"
   965    else
   966      for platform in "${platforms[@]}"; do
   967        kube::log::status "Building go targets for ${platform}"
   968        (
   969          kube::golang::set_platform_envs "${platform}"
   970          kube::golang::build_binaries_for_platform "${platform}"
   971        )
   972      done
   973    fi
   974  }