github.com/wmuizelaar/kpt@v0.0.0-20221018115725-bd564717b2ed/scripts/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 # The golang package that we are building. 20 readonly KUBE_GO_PACKAGE=k8s.io/kubernetes 21 readonly KUBE_GOPATH="${KUBE_OUTPUT}/go" 22 23 # The server platform we are building on. 24 readonly KUBE_SUPPORTED_SERVER_PLATFORMS=( 25 linux/amd64 26 linux/arm 27 linux/arm64 28 linux/s390x 29 linux/ppc64le 30 ) 31 32 # The node platforms we build for 33 readonly KUBE_SUPPORTED_NODE_PLATFORMS=( 34 linux/amd64 35 linux/arm 36 linux/arm64 37 linux/s390x 38 linux/ppc64le 39 windows/amd64 40 ) 41 42 # If we update this we should also update the set of platforms whose standard 43 # library is precompiled for in build/build-image/cross/Dockerfile 44 readonly KUBE_SUPPORTED_CLIENT_PLATFORMS=( 45 linux/amd64 46 linux/386 47 linux/arm 48 linux/arm64 49 linux/s390x 50 linux/ppc64le 51 darwin/amd64 52 darwin/386 53 windows/amd64 54 windows/386 55 ) 56 57 # Which platforms we should compile test targets for. 58 # Not all client platforms need these tests 59 readonly KUBE_SUPPORTED_TEST_PLATFORMS=( 60 linux/amd64 61 linux/arm 62 linux/arm64 63 linux/s390x 64 linux/ppc64le 65 darwin/amd64 66 windows/amd64 67 ) 68 69 # The set of server targets that we are only building for Linux 70 # If you update this list, please also update build/BUILD. 71 kube::golang::server_targets() { 72 local targets=( 73 cmd/kube-proxy 74 cmd/kube-apiserver 75 cmd/kube-controller-manager 76 cmd/kubelet 77 cmd/kubeadm 78 cmd/kube-scheduler 79 vendor/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 ) 98 echo "${targets[@]}" 99 } 100 101 IFS=" " read -ra KUBE_SERVER_IMAGE_TARGETS <<< "$(kube::golang::server_image_targets)" 102 readonly KUBE_SERVER_IMAGE_TARGETS 103 readonly KUBE_SERVER_IMAGE_BINARIES=("${KUBE_SERVER_IMAGE_TARGETS[@]##*/}") 104 105 # The set of conformance targets we build docker image for 106 kube::golang::conformance_image_targets() { 107 # NOTE: this contains cmd targets for kube::release::build_conformance_image 108 local targets=( 109 vendor/github.com/onsi/ginkgo/ginkgo 110 test/e2e/e2e.test 111 cluster/images/conformance/go-runner 112 cmd/kubectl 113 ) 114 echo "${targets[@]}" 115 } 116 117 IFS=" " read -ra KUBE_CONFORMANCE_IMAGE_TARGETS <<< "$(kube::golang::conformance_image_targets)" 118 readonly KUBE_CONFORMANCE_IMAGE_TARGETS 119 120 # The set of server targets that we are only building for Kubernetes nodes 121 # If you update this list, please also update build/BUILD. 122 kube::golang::node_targets() { 123 local targets=( 124 cmd/kube-proxy 125 cmd/kubeadm 126 cmd/kubelet 127 ) 128 echo "${targets[@]}" 129 } 130 131 IFS=" " read -ra KUBE_NODE_TARGETS <<< "$(kube::golang::node_targets)" 132 readonly KUBE_NODE_TARGETS 133 readonly KUBE_NODE_BINARIES=("${KUBE_NODE_TARGETS[@]##*/}") 134 readonly KUBE_NODE_BINARIES_WIN=("${KUBE_NODE_BINARIES[@]/%/.exe}") 135 136 # ------------ 137 # NOTE: All functions that return lists should use newlines. 138 # bash functions can't return arrays, and spaces are tricky, so newline 139 # separators are the preferred pattern. 140 # To transform a string of newline-separated items to an array, use kube::util::read-array: 141 # kube::util::read-array FOO < <(kube::golang::dups a b c a) 142 # 143 # ALWAYS remember to quote your subshells. Not doing so will break in 144 # bash 4.3, and potentially cause other issues. 145 # ------------ 146 147 # Returns a sorted newline-separated list containing only duplicated items. 148 kube::golang::dups() { 149 # We use printf to insert newlines, which are required by sort. 150 printf "%s\n" "$@" | sort | uniq -d 151 } 152 153 # Returns a sorted newline-separated list with duplicated items removed. 154 kube::golang::dedup() { 155 # We use printf to insert newlines, which are required by sort. 156 printf "%s\n" "$@" | sort -u 157 } 158 159 # Depends on values of user-facing KUBE_BUILD_PLATFORMS, KUBE_FASTBUILD, 160 # and KUBE_BUILDER_OS. 161 # Configures KUBE_SERVER_PLATFORMS, KUBE_NODE_PLATFOMRS, 162 # KUBE_TEST_PLATFORMS, and KUBE_CLIENT_PLATFORMS, then sets them 163 # to readonly. 164 # The configured vars will only contain platforms allowed by the 165 # KUBE_SUPPORTED* vars at the top of this file. 166 declare -a KUBE_SERVER_PLATFORMS 167 declare -a KUBE_CLIENT_PLATFORMS 168 declare -a KUBE_NODE_PLATFORMS 169 declare -a KUBE_TEST_PLATFORMS 170 kube::golang::setup_platforms() { 171 if [[ -n "${KUBE_BUILD_PLATFORMS:-}" ]]; then 172 # KUBE_BUILD_PLATFORMS needs to be read into an array before the next 173 # step, or quoting treats it all as one element. 174 local -a platforms 175 IFS=" " read -ra platforms <<< "${KUBE_BUILD_PLATFORMS}" 176 177 # Deduplicate to ensure the intersection trick with kube::golang::dups 178 # is not defeated by duplicates in user input. 179 kube::util::read-array platforms < <(kube::golang::dedup "${platforms[@]}") 180 181 # Use kube::golang::dups to restrict the builds to the platforms in 182 # KUBE_SUPPORTED_*_PLATFORMS. Items should only appear at most once in each 183 # set, so if they appear twice after the merge they are in the intersection. 184 kube::util::read-array KUBE_SERVER_PLATFORMS < <(kube::golang::dups \ 185 "${platforms[@]}" \ 186 "${KUBE_SUPPORTED_SERVER_PLATFORMS[@]}" \ 187 ) 188 readonly KUBE_SERVER_PLATFORMS 189 190 kube::util::read-array KUBE_NODE_PLATFORMS < <(kube::golang::dups \ 191 "${platforms[@]}" \ 192 "${KUBE_SUPPORTED_NODE_PLATFORMS[@]}" \ 193 ) 194 readonly KUBE_NODE_PLATFORMS 195 196 kube::util::read-array KUBE_TEST_PLATFORMS < <(kube::golang::dups \ 197 "${platforms[@]}" \ 198 "${KUBE_SUPPORTED_TEST_PLATFORMS[@]}" \ 199 ) 200 readonly KUBE_TEST_PLATFORMS 201 202 kube::util::read-array KUBE_CLIENT_PLATFORMS < <(kube::golang::dups \ 203 "${platforms[@]}" \ 204 "${KUBE_SUPPORTED_CLIENT_PLATFORMS[@]}" \ 205 ) 206 readonly KUBE_CLIENT_PLATFORMS 207 208 elif [[ "${KUBE_FASTBUILD:-}" == "true" ]]; then 209 KUBE_SERVER_PLATFORMS=(linux/amd64) 210 readonly KUBE_SERVER_PLATFORMS 211 KUBE_NODE_PLATFORMS=(linux/amd64) 212 readonly KUBE_NODE_PLATFORMS 213 if [[ "${KUBE_BUILDER_OS:-}" == "darwin"* ]]; then 214 KUBE_TEST_PLATFORMS=( 215 darwin/amd64 216 linux/amd64 217 ) 218 readonly KUBE_TEST_PLATFORMS 219 KUBE_CLIENT_PLATFORMS=( 220 darwin/amd64 221 linux/amd64 222 ) 223 readonly KUBE_CLIENT_PLATFORMS 224 else 225 KUBE_TEST_PLATFORMS=(linux/amd64) 226 readonly KUBE_TEST_PLATFORMS 227 KUBE_CLIENT_PLATFORMS=(linux/amd64) 228 readonly KUBE_CLIENT_PLATFORMS 229 fi 230 else 231 KUBE_SERVER_PLATFORMS=("${KUBE_SUPPORTED_SERVER_PLATFORMS[@]}") 232 readonly KUBE_SERVER_PLATFORMS 233 234 KUBE_NODE_PLATFORMS=("${KUBE_SUPPORTED_NODE_PLATFORMS[@]}") 235 readonly KUBE_NODE_PLATFORMS 236 237 KUBE_CLIENT_PLATFORMS=("${KUBE_SUPPORTED_CLIENT_PLATFORMS[@]}") 238 readonly KUBE_CLIENT_PLATFORMS 239 240 KUBE_TEST_PLATFORMS=("${KUBE_SUPPORTED_TEST_PLATFORMS[@]}") 241 readonly KUBE_TEST_PLATFORMS 242 fi 243 } 244 245 kube::golang::setup_platforms 246 247 # The set of client targets that we are building for all platforms 248 # If you update this list, please also update build/BUILD. 249 readonly KUBE_CLIENT_TARGETS=( 250 cmd/kubectl 251 ) 252 readonly KUBE_CLIENT_BINARIES=("${KUBE_CLIENT_TARGETS[@]##*/}") 253 readonly KUBE_CLIENT_BINARIES_WIN=("${KUBE_CLIENT_BINARIES[@]/%/.exe}") 254 255 # The set of test targets that we are building for all platforms 256 # If you update this list, please also update build/BUILD. 257 kube::golang::test_targets() { 258 local targets=( 259 cmd/gendocs 260 cmd/genkubedocs 261 cmd/genman 262 cmd/genyaml 263 cmd/genswaggertypedocs 264 cmd/linkcheck 265 vendor/github.com/onsi/ginkgo/ginkgo 266 test/e2e/e2e.test 267 cluster/images/conformance/go-runner 268 ) 269 echo "${targets[@]}" 270 } 271 IFS=" " read -ra KUBE_TEST_TARGETS <<< "$(kube::golang::test_targets)" 272 readonly KUBE_TEST_TARGETS 273 readonly KUBE_TEST_BINARIES=("${KUBE_TEST_TARGETS[@]##*/}") 274 readonly KUBE_TEST_BINARIES_WIN=("${KUBE_TEST_BINARIES[@]/%/.exe}") 275 # If you update this list, please also update build/BUILD. 276 readonly KUBE_TEST_PORTABLE=( 277 test/e2e/testing-manifests 278 test/kubemark 279 hack/e2e-internal 280 hack/get-build.sh 281 hack/ginkgo-e2e.sh 282 hack/lib 283 ) 284 285 # Test targets which run on the Kubernetes clusters directly, so we only 286 # need to target server platforms. 287 # These binaries will be distributed in the kubernetes-test tarball. 288 # If you update this list, please also update build/BUILD. 289 kube::golang::server_test_targets() { 290 local targets=( 291 cmd/kubemark 292 vendor/github.com/onsi/ginkgo/ginkgo 293 ) 294 295 if [[ "${OSTYPE:-}" == "linux"* ]]; then 296 targets+=( test/e2e_node/e2e_node.test ) 297 fi 298 299 echo "${targets[@]}" 300 } 301 302 IFS=" " read -ra KUBE_TEST_SERVER_TARGETS <<< "$(kube::golang::server_test_targets)" 303 readonly KUBE_TEST_SERVER_TARGETS 304 readonly KUBE_TEST_SERVER_BINARIES=("${KUBE_TEST_SERVER_TARGETS[@]##*/}") 305 readonly KUBE_TEST_SERVER_PLATFORMS=("${KUBE_SERVER_PLATFORMS[@]:+"${KUBE_SERVER_PLATFORMS[@]}"}") 306 307 # Gigabytes necessary for parallel platform builds. 308 # As of January 2018, RAM usage is exceeding 30G 309 # Setting to 40 to provide some headroom 310 readonly KUBE_PARALLEL_BUILD_MEMORY=40 311 312 readonly KUBE_ALL_TARGETS=( 313 "${KUBE_SERVER_TARGETS[@]}" 314 "${KUBE_CLIENT_TARGETS[@]}" 315 "${KUBE_TEST_TARGETS[@]}" 316 "${KUBE_TEST_SERVER_TARGETS[@]}" 317 ) 318 readonly KUBE_ALL_BINARIES=("${KUBE_ALL_TARGETS[@]##*/}") 319 320 readonly KUBE_STATIC_LIBRARIES=( 321 kube-apiserver 322 kube-controller-manager 323 kube-scheduler 324 kube-proxy 325 kubeadm 326 kubectl 327 ) 328 329 # Fully-qualified package names that we want to instrument for coverage information. 330 readonly KUBE_COVERAGE_INSTRUMENTED_PACKAGES=( 331 k8s.io/kubernetes/cmd/kube-apiserver 332 k8s.io/kubernetes/cmd/kube-controller-manager 333 k8s.io/kubernetes/cmd/kube-scheduler 334 k8s.io/kubernetes/cmd/kube-proxy 335 k8s.io/kubernetes/cmd/kubelet 336 ) 337 338 # KUBE_CGO_OVERRIDES is a space-separated list of binaries which should be built 339 # with CGO enabled, assuming CGO is supported on the target platform. 340 # This overrides any entry in KUBE_STATIC_LIBRARIES. 341 IFS=" " read -ra KUBE_CGO_OVERRIDES <<< "${KUBE_CGO_OVERRIDES:-}" 342 readonly KUBE_CGO_OVERRIDES 343 # KUBE_STATIC_OVERRIDES is a space-separated list of binaries which should be 344 # built with CGO disabled. This is in addition to the list in 345 # KUBE_STATIC_LIBRARIES. 346 IFS=" " read -ra KUBE_STATIC_OVERRIDES <<< "${KUBE_STATIC_OVERRIDES:-}" 347 readonly KUBE_STATIC_OVERRIDES 348 349 kube::golang::is_statically_linked_library() { 350 local e 351 # Explicitly enable cgo when building kubectl for darwin from darwin. 352 [[ "$(go env GOHOSTOS)" == "darwin" && "$(go env GOOS)" == "darwin" && 353 "$1" == *"/kubectl" ]] && return 1 354 if [[ -n "${KUBE_CGO_OVERRIDES:+x}" ]]; then 355 for e in "${KUBE_CGO_OVERRIDES[@]}"; do [[ "${1}" == *"/${e}" ]] && return 1; done; 356 fi 357 for e in "${KUBE_STATIC_LIBRARIES[@]}"; do [[ "${1}" == *"/${e}" ]] && return 0; done; 358 if [[ -n "${KUBE_STATIC_OVERRIDES:+x}" ]]; then 359 for e in "${KUBE_STATIC_OVERRIDES[@]}"; do [[ "${1}" == *"/${e}" ]] && return 0; done; 360 fi 361 return 1; 362 } 363 364 # kube::binaries_from_targets take a list of build targets and return the 365 # full go package to be built 366 kube::golang::binaries_from_targets() { 367 local target 368 for target; do 369 # If the target starts with what looks like a domain name, assume it has a 370 # fully-qualified package name rather than one that needs the Kubernetes 371 # package prepended. 372 if [[ "${target}" =~ ^([[:alnum:]]+".")+[[:alnum:]]+"/" ]]; then 373 echo "${target}" 374 else 375 echo "${KUBE_GO_PACKAGE}/${target}" 376 fi 377 done 378 } 379 380 # Asks golang what it thinks the host platform is. The go tool chain does some 381 # slightly different things when the target platform matches the host platform. 382 kube::golang::host_platform() { 383 echo "$(go env GOHOSTOS)/$(go env GOHOSTARCH)" 384 } 385 386 # Takes the platform name ($1) and sets the appropriate golang env variables 387 # for that platform. 388 kube::golang::set_platform_envs() { 389 [[ -n ${1-} ]] || { 390 kube::log::error_exit "!!! Internal error. No platform set in kube::golang::set_platform_envs" 391 } 392 393 export GOOS=${platform%/*} 394 export GOARCH=${platform##*/} 395 396 # Do not set CC when building natively on a platform, only if cross-compiling from linux/amd64 397 if [[ $(kube::golang::host_platform) == "linux/amd64" ]]; then 398 # Dynamic CGO linking for other server architectures than linux/amd64 goes here 399 # If you want to include support for more server platforms than these, add arch-specific gcc names here 400 case "${platform}" in 401 "linux/arm") 402 export CGO_ENABLED=1 403 export CC=arm-linux-gnueabihf-gcc 404 ;; 405 "linux/arm64") 406 export CGO_ENABLED=1 407 export CC=aarch64-linux-gnu-gcc 408 ;; 409 "linux/ppc64le") 410 export CGO_ENABLED=1 411 export CC=powerpc64le-linux-gnu-gcc 412 ;; 413 "linux/s390x") 414 export CGO_ENABLED=1 415 export CC=s390x-linux-gnu-gcc 416 ;; 417 esac 418 fi 419 } 420 421 kube::golang::unset_platform_envs() { 422 unset GOOS 423 unset GOARCH 424 unset GOROOT 425 unset CGO_ENABLED 426 unset CC 427 } 428 429 # Create the GOPATH tree under $KUBE_OUTPUT 430 kube::golang::create_gopath_tree() { 431 local go_pkg_dir="${KUBE_GOPATH}/src/${KUBE_GO_PACKAGE}" 432 local go_pkg_basedir 433 go_pkg_basedir=$(dirname "${go_pkg_dir}") 434 435 mkdir -p "${go_pkg_basedir}" 436 437 # TODO: This symlink should be relative. 438 if [[ ! -e "${go_pkg_dir}" || "$(readlink "${go_pkg_dir}")" != "${KUBE_ROOT}" ]]; then 439 ln -snf "${KUBE_ROOT}" "${go_pkg_dir}" 440 fi 441 442 # Using bazel with a recursive target (e.g. bazel test ...) will abort due to 443 # the symlink loop created in this function, so create this special file which 444 # tells bazel not to follow the symlink. 445 touch "${go_pkg_basedir}/DONT_FOLLOW_SYMLINKS_WHEN_TRAVERSING_THIS_DIRECTORY_VIA_A_RECURSIVE_TARGET_PATTERN" 446 # Additionally, the //:package-srcs glob recursively includes all 447 # subdirectories, and similarly fails due to the symlink loop. By creating a 448 # BUILD.bazel file, we effectively create a dummy package, which stops the 449 # glob from descending further into the tree and hitting the loop. 450 cat >"${KUBE_GOPATH}/BUILD.bazel" <<EOF 451 # This dummy BUILD file prevents Bazel from trying to descend through the 452 # infinite loop created by the symlink at 453 # ${go_pkg_dir} 454 EOF 455 } 456 457 # Ensure the go tool exists and is a viable version. 458 kube::golang::verify_go_version() { 459 if [[ -z "$(command -v go)" ]]; then 460 kube::log::usage_from_stdin <<EOF 461 Can't find 'go' in PATH, please fix and retry. 462 See http://golang.org/doc/install for installation instructions. 463 EOF 464 return 2 465 fi 466 467 local go_version 468 IFS=" " read -ra go_version <<< "$(go version)" 469 local minimum_go_version 470 minimum_go_version=go1.13.4 471 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 472 kube::log::usage_from_stdin <<EOF 473 Detected go version: ${go_version[*]}. 474 Kubernetes requires ${minimum_go_version} or greater. 475 Please install ${minimum_go_version} or later. 476 EOF 477 return 2 478 fi 479 } 480 481 # kube::golang::setup_env will check that the `go` commands is available in 482 # ${PATH}. It will also check that the Go version is good enough for the 483 # Kubernetes build. 484 # 485 # Inputs: 486 # KUBE_EXTRA_GOPATH - If set, this is included in created GOPATH 487 # 488 # Outputs: 489 # env-var GOPATH points to our local output dir 490 # env-var GOBIN is unset (we want binaries in a predictable place) 491 # env-var GO15VENDOREXPERIMENT=1 492 # current directory is within GOPATH 493 kube::golang::setup_env() { 494 kube::golang::verify_go_version 495 496 kube::golang::create_gopath_tree 497 498 export GOPATH="${KUBE_GOPATH}" 499 export GOCACHE="${KUBE_GOPATH}/cache" 500 501 # Append KUBE_EXTRA_GOPATH to the GOPATH if it is defined. 502 if [[ -n ${KUBE_EXTRA_GOPATH:-} ]]; then 503 GOPATH="${GOPATH}:${KUBE_EXTRA_GOPATH}" 504 fi 505 506 # Make sure our own Go binaries are in PATH. 507 export PATH="${KUBE_GOPATH}/bin:${PATH}" 508 509 # Change directories so that we are within the GOPATH. Some tools get really 510 # upset if this is not true. We use a whole fake GOPATH here to collect the 511 # resultant binaries. Go will not let us use GOBIN with `go install` and 512 # cross-compiling, and `go install -o <file>` only works for a single pkg. 513 local subdir 514 subdir=$(kube::realpath . | sed "s|${KUBE_ROOT}||") 515 cd "${KUBE_GOPATH}/src/${KUBE_GO_PACKAGE}/${subdir}" || return 1 516 517 # Set GOROOT so binaries that parse code can work properly. 518 GOROOT=$(go env GOROOT) 519 export GOROOT 520 521 # Unset GOBIN in case it already exists in the current session. 522 unset GOBIN 523 524 # This seems to matter to some tools 525 export GO15VENDOREXPERIMENT=1 526 } 527 528 # This will take binaries from $GOPATH/bin and copy them to the appropriate 529 # place in ${KUBE_OUTPUT_BINDIR} 530 # 531 # Ideally this wouldn't be necessary and we could just set GOBIN to 532 # KUBE_OUTPUT_BINDIR but that won't work in the face of cross compilation. 'go 533 # install' will place binaries that match the host platform directly in $GOBIN 534 # while placing cross compiled binaries into `platform_arch` subdirs. This 535 # complicates pretty much everything else we do around packaging and such. 536 kube::golang::place_bins() { 537 local host_platform 538 host_platform=$(kube::golang::host_platform) 539 540 V=2 kube::log::status "Placing binaries" 541 542 local platform 543 for platform in "${KUBE_CLIENT_PLATFORMS[@]}"; do 544 # The substitution on platform_src below will replace all slashes with 545 # underscores. It'll transform darwin/amd64 -> darwin_amd64. 546 local platform_src="/${platform//\//_}" 547 if [[ "${platform}" == "${host_platform}" ]]; then 548 platform_src="" 549 rm -f "${THIS_PLATFORM_BIN}" 550 ln -s "${KUBE_OUTPUT_BINPATH}/${platform}" "${THIS_PLATFORM_BIN}" 551 fi 552 553 local full_binpath_src="${KUBE_GOPATH}/bin${platform_src}" 554 if [[ -d "${full_binpath_src}" ]]; then 555 mkdir -p "${KUBE_OUTPUT_BINPATH}/${platform}" 556 find "${full_binpath_src}" -maxdepth 1 -type f -exec \ 557 rsync -pc {} "${KUBE_OUTPUT_BINPATH}/${platform}" \; 558 fi 559 done 560 } 561 562 # Try and replicate the native binary placement of go install without 563 # calling go install. 564 kube::golang::outfile_for_binary() { 565 local binary=$1 566 local platform=$2 567 local output_path="${KUBE_GOPATH}/bin" 568 local bin 569 bin=$(basename "${binary}") 570 if [[ "${platform}" != "${host_platform}" ]]; then 571 output_path="${output_path}/${platform//\//_}" 572 fi 573 if [[ ${GOOS} == "windows" ]]; then 574 bin="${bin}.exe" 575 fi 576 echo "${output_path}/${bin}" 577 } 578 579 # Argument: the name of a Kubernetes package. 580 # Returns 0 if the binary can be built with coverage, 1 otherwise. 581 # NB: this ignores whether coverage is globally enabled or not. 582 kube::golang::is_instrumented_package() { 583 kube::util::array_contains "$1" "${KUBE_COVERAGE_INSTRUMENTED_PACKAGES[@]}" 584 return $? 585 } 586 587 # Argument: the name of a Kubernetes package (e.g. k8s.io/kubernetes/cmd/kube-scheduler) 588 # Echos the path to a dummy test used for coverage information. 589 kube::golang::path_for_coverage_dummy_test() { 590 local package="$1" 591 local path="${KUBE_GOPATH}/src/${package}" 592 local name 593 name=$(basename "${package}") 594 echo "${path}/zz_generated_${name}_test.go" 595 } 596 597 # Argument: the name of a Kubernetes package (e.g. k8s.io/kubernetes/cmd/kube-scheduler). 598 # Creates a dummy unit test on disk in the source directory for the given package. 599 # This unit test will invoke the package's standard entry point when run. 600 kube::golang::create_coverage_dummy_test() { 601 local package="$1" 602 local name 603 name="$(basename "${package}")" 604 cat <<EOF > "$(kube::golang::path_for_coverage_dummy_test "${package}")" 605 package main 606 import ( 607 "testing" 608 "k8s.io/kubernetes/pkg/util/coverage" 609 ) 610 611 func TestMain(m *testing.M) { 612 // Get coverage running 613 coverage.InitCoverage("${name}") 614 615 // Go! 616 main() 617 618 // Make sure we actually write the profiling information to disk, if we make it here. 619 // On long-running services, or anything that calls os.Exit(), this is insufficient, 620 // so we also flush periodically with a default period of five seconds (configurable by 621 // the KUBE_COVERAGE_FLUSH_INTERVAL environment variable). 622 coverage.FlushCoverage() 623 } 624 EOF 625 } 626 627 # Argument: the name of a Kubernetes package (e.g. k8s.io/kubernetes/cmd/kube-scheduler). 628 # Deletes a test generated by kube::golang::create_coverage_dummy_test. 629 # It is not an error to call this for a nonexistent test. 630 kube::golang::delete_coverage_dummy_test() { 631 local package="$1" 632 rm -f "$(kube::golang::path_for_coverage_dummy_test "${package}")" 633 } 634 635 # Arguments: a list of kubernetes packages to build. 636 # Expected variables: ${build_args} should be set to an array of Go build arguments. 637 # In addition, ${package} and ${platform} should have been set earlier, and if 638 # ${KUBE_BUILD_WITH_COVERAGE} is set, coverage instrumentation will be enabled. 639 # 640 # Invokes Go to actually build some packages. If coverage is disabled, simply invokes 641 # go install. If coverage is enabled, builds covered binaries using go test, temporarily 642 # producing the required unit test files and then cleaning up after itself. 643 # Non-covered binaries are then built using go install as usual. 644 kube::golang::build_some_binaries() { 645 if [[ -n "${KUBE_BUILD_WITH_COVERAGE:-}" ]]; then 646 local -a uncovered=() 647 for package in "$@"; do 648 if kube::golang::is_instrumented_package "${package}"; then 649 V=2 kube::log::info "Building ${package} with coverage..." 650 651 kube::golang::create_coverage_dummy_test "${package}" 652 kube::util::trap_add "kube::golang::delete_coverage_dummy_test \"${package}\"" EXIT 653 654 go test -c -o "$(kube::golang::outfile_for_binary "${package}" "${platform}")" \ 655 -covermode count \ 656 -coverpkg k8s.io/...,k8s.io/kubernetes/vendor/k8s.io/... \ 657 "${build_args[@]}" \ 658 -tags coverage \ 659 "${package}" 660 else 661 uncovered+=("${package}") 662 fi 663 done 664 if [[ "${#uncovered[@]}" != 0 ]]; then 665 V=2 kube::log::info "Building ${uncovered[*]} without coverage..." 666 go install "${build_args[@]}" "${uncovered[@]}" 667 else 668 V=2 kube::log::info "Nothing to build without coverage." 669 fi 670 else 671 V=2 kube::log::info "Coverage is disabled." 672 go install "${build_args[@]}" "$@" 673 fi 674 } 675 676 kube::golang::build_binaries_for_platform() { 677 local platform=$1 678 679 local -a statics=() 680 local -a nonstatics=() 681 local -a tests=() 682 683 V=2 kube::log::info "Env for ${platform}: GOOS=${GOOS-} GOARCH=${GOARCH-} GOROOT=${GOROOT-} CGO_ENABLED=${CGO_ENABLED-} CC=${CC-}" 684 685 for binary in "${binaries[@]}"; do 686 if [[ "${binary}" =~ ".test"$ ]]; then 687 tests+=("${binary}") 688 elif kube::golang::is_statically_linked_library "${binary}"; then 689 statics+=("${binary}") 690 else 691 nonstatics+=("${binary}") 692 fi 693 done 694 695 local -a build_args 696 if [[ "${#statics[@]}" != 0 ]]; then 697 build_args=( 698 -installsuffix static 699 ${goflags:+"${goflags[@]}"} 700 -gcflags "${gogcflags:-}" 701 -asmflags "${goasmflags:-}" 702 -ldflags "${goldflags:-}" 703 -tags "${gotags:-}" 704 ) 705 CGO_ENABLED=0 kube::golang::build_some_binaries "${statics[@]}" 706 fi 707 708 if [[ "${#nonstatics[@]}" != 0 ]]; then 709 build_args=( 710 ${goflags:+"${goflags[@]}"} 711 -gcflags "${gogcflags:-}" 712 -asmflags "${goasmflags:-}" 713 -ldflags "${goldflags:-}" 714 -tags "${gotags:-}" 715 ) 716 kube::golang::build_some_binaries "${nonstatics[@]}" 717 fi 718 719 for test in "${tests[@]:+${tests[@]}}"; do 720 local outfile testpkg 721 outfile=$(kube::golang::outfile_for_binary "${test}" "${platform}") 722 testpkg=$(dirname "${test}") 723 724 mkdir -p "$(dirname "${outfile}")" 725 go test -c \ 726 ${goflags:+"${goflags[@]}"} \ 727 -gcflags "${gogcflags:-}" \ 728 -asmflags "${goasmflags:-}" \ 729 -ldflags "${goldflags:-}" \ 730 -tags "${gotags:-}" \ 731 -o "${outfile}" \ 732 "${testpkg}" 733 done 734 } 735 736 # Return approximate physical memory available in gigabytes. 737 kube::golang::get_physmem() { 738 local mem 739 740 # Linux kernel version >=3.14, in kb 741 if mem=$(grep MemAvailable /proc/meminfo | awk '{ print $2 }'); then 742 echo $(( mem / 1048576 )) 743 return 744 fi 745 746 # Linux, in kb 747 if mem=$(grep MemTotal /proc/meminfo | awk '{ print $2 }'); then 748 echo $(( mem / 1048576 )) 749 return 750 fi 751 752 # OS X, in bytes. Note that get_physmem, as used, should only ever 753 # run in a Linux container (because it's only used in the multiple 754 # platform case, which is a Dockerized build), but this is provided 755 # for completeness. 756 if mem=$(sysctl -n hw.memsize 2>/dev/null); then 757 echo $(( mem / 1073741824 )) 758 return 759 fi 760 761 # If we can't infer it, just give up and assume a low memory system 762 echo 1 763 } 764 765 # Build binaries targets specified 766 # 767 # Input: 768 # $@ - targets and go flags. If no targets are set then all binaries targets 769 # are built. 770 # KUBE_BUILD_PLATFORMS - Incoming variable of targets to build for. If unset 771 # then just the host architecture is built. 772 kube::golang::build_binaries() { 773 # Create a sub-shell so that we don't pollute the outer environment 774 ( 775 # Check for `go` binary and set ${GOPATH}. 776 kube::golang::setup_env 777 V=2 kube::log::info "Go version: $(go version)" 778 779 local host_platform 780 host_platform=$(kube::golang::host_platform) 781 782 local goflags goldflags goasmflags gogcflags gotags 783 # If GOLDFLAGS is unset, then set it to the a default of "-s -w". 784 # Disable SC2153 for this, as it will throw a warning that the local 785 # variable goldflags will exist, and it suggest changing it to this. 786 # shellcheck disable=SC2153 787 goldflags="${GOLDFLAGS=-s -w} $(kube::version::ldflags)" 788 goasmflags="-trimpath=${KUBE_ROOT}" 789 gogcflags="${GOGCFLAGS:-} -trimpath=${KUBE_ROOT}" 790 791 # extract tags if any specified in GOFLAGS 792 # shellcheck disable=SC2001 793 gotags="selinux,$(echo "${GOFLAGS:-}" | sed -e 's|.*-tags=\([^-]*\).*|\1|')" 794 795 local -a targets=() 796 local arg 797 798 for arg; do 799 if [[ "${arg}" == -* ]]; then 800 # Assume arguments starting with a dash are flags to pass to go. 801 goflags+=("${arg}") 802 else 803 targets+=("${arg}") 804 fi 805 done 806 807 if [[ ${#targets[@]} -eq 0 ]]; then 808 targets=("${KUBE_ALL_TARGETS[@]}") 809 fi 810 811 local -a platforms 812 IFS=" " read -ra platforms <<< "${KUBE_BUILD_PLATFORMS:-}" 813 if [[ ${#platforms[@]} -eq 0 ]]; then 814 platforms=("${host_platform}") 815 fi 816 817 local -a binaries 818 while IFS="" read -r binary; do binaries+=("$binary"); done < <(kube::golang::binaries_from_targets "${targets[@]}") 819 820 local parallel=false 821 if [[ ${#platforms[@]} -gt 1 ]]; then 822 local gigs 823 gigs=$(kube::golang::get_physmem) 824 825 if [[ ${gigs} -ge ${KUBE_PARALLEL_BUILD_MEMORY} ]]; then 826 kube::log::status "Multiple platforms requested and available ${gigs}G >= threshold ${KUBE_PARALLEL_BUILD_MEMORY}G, building platforms in parallel" 827 parallel=true 828 else 829 kube::log::status "Multiple platforms requested, but available ${gigs}G < threshold ${KUBE_PARALLEL_BUILD_MEMORY}G, building platforms in serial" 830 parallel=false 831 fi 832 fi 833 834 if [[ "${parallel}" == "true" ]]; then 835 kube::log::status "Building go targets for {${platforms[*]}} in parallel (output will appear in a burst when complete):" "${targets[@]}" 836 local platform 837 for platform in "${platforms[@]}"; do ( 838 kube::golang::set_platform_envs "${platform}" 839 kube::log::status "${platform}: build started" 840 kube::golang::build_binaries_for_platform "${platform}" 841 kube::log::status "${platform}: build finished" 842 ) &> "/tmp//${platform//\//_}.build" & 843 done 844 845 local fails=0 846 for job in $(jobs -p); do 847 wait "${job}" || (( fails+=1 )) 848 done 849 850 for platform in "${platforms[@]}"; do 851 cat "/tmp//${platform//\//_}.build" 852 done 853 854 exit ${fails} 855 else 856 for platform in "${platforms[@]}"; do 857 kube::log::status "Building go targets for ${platform}:" "${targets[@]}" 858 ( 859 kube::golang::set_platform_envs "${platform}" 860 kube::golang::build_binaries_for_platform "${platform}" 861 ) 862 done 863 fi 864 ) 865 }