github.com/boson-project/source-to-image@v1.2.0/hack/common.sh (about) 1 #!/bin/bash 2 3 # This script provides common script functions for the hacks 4 # Requires S2I_ROOT to be set 5 6 set -o errexit 7 set -o nounset 8 set -o pipefail 9 10 # The root of the build/dist directory 11 S2I_ROOT=$( 12 unset CDPATH 13 sti_root=$(dirname "${BASH_SOURCE}")/.. 14 cd "${sti_root}" 15 pwd 16 ) 17 18 S2I_OUTPUT_SUBPATH="${S2I_OUTPUT_SUBPATH:-_output/local}" 19 S2I_OUTPUT="${S2I_ROOT}/${S2I_OUTPUT_SUBPATH}" 20 S2I_OUTPUT_BINPATH="${S2I_OUTPUT}/bin" 21 S2I_OUTPUT_PKGDIR="${S2I_OUTPUT}/pkgdir" 22 S2I_LOCAL_BINPATH="${S2I_OUTPUT}/go/bin" 23 S2I_LOCAL_RELEASEPATH="${S2I_OUTPUT}/releases" 24 RELEASE_LDFLAGS=${RELEASE_LDFLAGS:-""} 25 26 27 readonly S2I_GO_PACKAGE=github.com/openshift/source-to-image 28 readonly S2I_GOPATH="${S2I_OUTPUT}/go" 29 30 readonly S2I_CROSS_COMPILE_PLATFORMS=( 31 linux/amd64 32 darwin/amd64 33 windows/amd64 34 linux/386 35 ) 36 readonly S2I_CROSS_COMPILE_TARGETS=( 37 cmd/s2i 38 ) 39 readonly S2I_CROSS_COMPILE_BINARIES=("${S2I_CROSS_COMPILE_TARGETS[@]##*/}") 40 41 readonly S2I_ALL_TARGETS=( 42 "${S2I_CROSS_COMPILE_TARGETS[@]}" 43 ) 44 45 readonly S2I_BINARY_SYMLINKS=( 46 sti 47 ) 48 readonly S2I_BINARY_RELEASE_WINDOWS=( 49 sti.exe 50 s2i.exe 51 ) 52 53 # s2i::build::binaries_from_targets take a list of build targets and return the 54 # full go package to be built 55 s2i::build::binaries_from_targets() { 56 local target 57 for target; do 58 echo "${S2I_GO_PACKAGE}/${target}" 59 done 60 } 61 62 # Asks golang what it thinks the host platform is. The go tool chain does some 63 # slightly different things when the target platform matches the host platform. 64 s2i::build::host_platform() { 65 echo "$(go env GOHOSTOS)/$(go env GOHOSTARCH)" 66 } 67 68 69 # Build binaries targets specified 70 # 71 # Input: 72 # $@ - targets and go flags. If no targets are set then all binaries targets 73 # are built. 74 # S2I_BUILD_PLATFORMS - Incoming variable of targets to build for. If unset 75 # then just the host architecture is built. 76 s2i::build::build_binaries() { 77 # Create a sub-shell so that we don't pollute the outer environment 78 ( 79 # Check for `go` binary and set ${GOPATH}. 80 s2i::build::setup_env 81 82 # Fetch the version. 83 local version_ldflags 84 version_ldflags=$(s2i::build::ldflags) 85 86 s2i::build::export_targets "$@" 87 88 local platform 89 for platform in "${platforms[@]}"; do 90 s2i::build::set_platform_envs "${platform}" 91 echo "++ Building go targets for ${platform}:" "${targets[@]}" 92 CGO_ENABLED=0 go install "${goflags[@]:+${goflags[@]}}" \ 93 -pkgdir "${S2I_OUTPUT_PKGDIR}" \ 94 -ldflags "${version_ldflags} ${RELEASE_LDFLAGS}" \ 95 "${binaries[@]}" 96 s2i::build::unset_platform_envs "${platform}" 97 done 98 ) 99 } 100 101 # Generates the set of target packages, binaries, and platforms to build for. 102 # Accepts binaries via $@, and platforms via S2I_BUILD_PLATFORMS, or defaults to 103 # the current platform. 104 s2i::build::export_targets() { 105 # Use eval to preserve embedded quoted strings. 106 local goflags 107 eval "goflags=(${S2I_GOFLAGS:-})" 108 109 targets=() 110 local arg 111 for arg; do 112 if [[ "${arg}" == -* ]]; then 113 # Assume arguments starting with a dash are flags to pass to go. 114 goflags+=("${arg}") 115 else 116 targets+=("${arg}") 117 fi 118 done 119 120 if [[ ${#targets[@]} -eq 0 ]]; then 121 targets=("${S2I_ALL_TARGETS[@]}") 122 fi 123 124 binaries=($(s2i::build::binaries_from_targets "${targets[@]}")) 125 126 platforms=("${S2I_BUILD_PLATFORMS[@]:+${S2I_BUILD_PLATFORMS[@]}}") 127 if [[ ${#platforms[@]} -eq 0 ]]; then 128 platforms=("$(s2i::build::host_platform)") 129 fi 130 } 131 132 133 # Takes the platform name ($1) and sets the appropriate golang env variables 134 # for that platform. 135 s2i::build::set_platform_envs() { 136 [[ -n ${1-} ]] || { 137 echo "!!! Internal error. No platform set in s2i::build::set_platform_envs" 138 exit 1 139 } 140 141 export GOOS=${platform%/*} 142 export GOARCH=${platform##*/} 143 } 144 145 # Takes the platform name ($1) and resets the appropriate golang env variables 146 # for that platform. 147 s2i::build::unset_platform_envs() { 148 unset GOOS 149 unset GOARCH 150 } 151 152 153 # Create the GOPATH tree under $S2I_ROOT 154 s2i::build::create_gopath_tree() { 155 local go_pkg_dir="${S2I_GOPATH}/src/${S2I_GO_PACKAGE}" 156 local go_pkg_basedir=$(dirname "${go_pkg_dir}") 157 158 mkdir -p "${go_pkg_basedir}" 159 rm -f "${go_pkg_dir}" 160 161 # TODO: This symlink should be relative. 162 if [[ "$OSTYPE" == "cygwin" ]]; then 163 S2I_ROOT_cyg=$(cygpath -w ${S2I_ROOT}) 164 go_pkg_dir_cyg=$(cygpath -w ${go_pkg_dir}) 165 cmd /c "mklink ${go_pkg_dir_cyg} ${S2I_ROOT_cyg}" &>/dev/null 166 else 167 ln -s "${S2I_ROOT}" "${go_pkg_dir}" 168 fi 169 } 170 171 172 # s2i::build::setup_env will check that the `go` commands is available in 173 # ${PATH}. If not running on Travis, it will also check that the Go version is 174 # good enough for the Kubernetes build. 175 # 176 # Input Vars: 177 # S2I_EXTRA_GOPATH - If set, this is included in created GOPATH 178 # S2I_NO_GODEPS - If set, we don't add 'vendor' to GOPATH 179 # 180 # Output Vars: 181 # export GOPATH - A modified GOPATH to our created tree along with extra 182 # stuff. 183 # export GOBIN - This is actively unset if already set as we want binaries 184 # placed in a predictable place. 185 s2i::build::setup_env() { 186 s2i::build::create_gopath_tree 187 188 if [[ -z "$(which go)" ]]; then 189 cat <<EOF 190 191 Can't find 'go' in PATH, please fix and retry. 192 See http://golang.org/doc/install for installation instructions. 193 194 EOF 195 exit 2 196 fi 197 198 # For any tools that expect this to be set (it is default in golang 1.6), 199 # force vendor experiment. 200 export GO15VENDOREXPERIMENT=1 201 202 GOPATH=${S2I_GOPATH} 203 204 # Append S2I_EXTRA_GOPATH to the GOPATH if it is defined. 205 if [[ -n ${S2I_EXTRA_GOPATH:-} ]]; then 206 GOPATH="${GOPATH}:${S2I_EXTRA_GOPATH}" 207 fi 208 209 # Append the tree maintained by `godep` to the GOPATH unless S2I_NO_GODEPS 210 # is defined. 211 if [[ -z ${S2I_NO_GODEPS:-} ]]; then 212 GOPATH="${GOPATH}:${S2I_ROOT}/vendor" 213 fi 214 215 if [[ "$OSTYPE" == "cygwin" ]]; then 216 GOPATH=$(cygpath -w -p $GOPATH) 217 fi 218 219 export GOPATH 220 221 # Unset GOBIN in case it already exists in the current session. 222 unset GOBIN 223 } 224 225 # This will take binaries from $GOPATH/bin and copy them to the appropriate 226 # place in ${S2I_OUTPUT_BINDIR} 227 # 228 # If S2I_RELEASE_ARCHIVE is set to a directory, it will have tar archives of 229 # each S2I_RELEASE_PLATFORMS created 230 # 231 # Ideally this wouldn't be necessary and we could just set GOBIN to 232 # S2I_OUTPUT_BINDIR but that won't work in the face of cross compilation. 'go 233 # install' will place binaries that match the host platform directly in $GOBIN 234 # while placing cross compiled binaries into `platform_arch` subdirs. This 235 # complicates pretty much everything else we do around packaging and such. 236 s2i::build::place_bins() { 237 ( 238 local host_platform 239 host_platform=$(s2i::build::host_platform) 240 241 echo "++ Placing binaries" 242 243 if [[ "${S2I_RELEASE_ARCHIVE-}" != "" ]]; then 244 s2i::build::get_version_vars 245 mkdir -p "${S2I_LOCAL_RELEASEPATH}" 246 fi 247 248 s2i::build::export_targets "$@" 249 250 for platform in "${platforms[@]}"; do 251 # The substitution on platform_src below will replace all slashes with 252 # underscores. It'll transform darwin/amd64 -> darwin_amd64. 253 local platform_src="/${platform//\//_}" 254 if [[ $platform == $host_platform ]]; then 255 platform_src="" 256 fi 257 258 # Skip this directory if the platform has no binaries. 259 local full_binpath_src="${S2I_GOPATH}/bin${platform_src}" 260 if [[ ! -d "${full_binpath_src}" ]]; then 261 continue 262 fi 263 264 mkdir -p "${S2I_OUTPUT_BINPATH}/${platform}" 265 266 # Create an array of binaries to release. Append .exe variants if the platform is windows. 267 local -a binaries=() 268 for binary in "${targets[@]}"; do 269 binary=$(basename $binary) 270 if [[ $platform == "windows/amd64" ]]; then 271 binaries+=("${binary}.exe") 272 else 273 binaries+=("${binary}") 274 fi 275 done 276 277 # Move the specified release binaries to the shared S2I_OUTPUT_BINPATH. 278 for binary in "${binaries[@]}"; do 279 mv "${full_binpath_src}/${binary}" "${S2I_OUTPUT_BINPATH}/${platform}/" 280 done 281 282 # If no release archive was requested, we're done. 283 if [[ "${S2I_RELEASE_ARCHIVE-}" == "" ]]; then 284 continue 285 fi 286 287 # Create a temporary bin directory containing only the binaries marked for release. 288 local release_binpath=$(mktemp -d sti.release.${S2I_RELEASE_ARCHIVE}.XXX) 289 for binary in "${binaries[@]}"; do 290 cp "${S2I_OUTPUT_BINPATH}/${platform}/${binary}" "${release_binpath}/" 291 done 292 293 # Create binary copies where specified. 294 local suffix="" 295 if [[ $platform == "windows/amd64" ]]; then 296 suffix=".exe" 297 fi 298 for linkname in "${S2I_BINARY_SYMLINKS[@]}"; do 299 local src="${release_binpath}/s2i${suffix}" 300 if [[ -f "${src}" ]]; then 301 ln -s "s2i${suffix}" "${release_binpath}/${linkname}${suffix}" 302 fi 303 done 304 305 # Create the release archive. 306 local platform_segment="${platform//\//-}" 307 if [[ $platform == "windows/amd64" ]]; then 308 local archive_name="${S2I_RELEASE_ARCHIVE}-${S2I_GIT_VERSION}-${S2I_GIT_COMMIT}-${platform_segment}.zip" 309 echo "++ Creating ${archive_name}" 310 for file in "${S2I_BINARY_RELEASE_WINDOWS[@]}"; do 311 zip "${S2I_LOCAL_RELEASEPATH}/${archive_name}" -qj "${release_binpath}/${file}" 312 done 313 else 314 local archive_name="${S2I_RELEASE_ARCHIVE}-${S2I_GIT_VERSION}-${S2I_GIT_COMMIT}-${platform_segment}.tar.gz" 315 echo "++ Creating ${archive_name}" 316 tar -czf "${S2I_LOCAL_RELEASEPATH}/${archive_name}" -C "${release_binpath}" . 317 fi 318 rm -rf "${release_binpath}" 319 done 320 ) 321 } 322 323 # s2i::build::make_binary_symlinks makes symlinks for the sti 324 # binary in _output/local/go/bin 325 s2i::build::make_binary_symlinks() { 326 platform=$(s2i::build::host_platform) 327 if [[ -f "${S2I_OUTPUT_BINPATH}/${platform}/s2i" ]]; then 328 for linkname in "${S2I_BINARY_SYMLINKS[@]}"; do 329 if [[ $platform == "windows/amd64" ]]; then 330 cp "${S2I_OUTPUT_BINPATH}/${platform}/s2i.exe" "${S2I_OUTPUT_BINPATH}/${platform}/${linkname}.exe" 331 else 332 ln -sf s2i "${S2I_OUTPUT_BINPATH}/${platform}/${linkname}" 333 fi 334 done 335 fi 336 } 337 338 # s2i::build::detect_local_release_tars verifies there is only one primary and one 339 # image binaries release tar in S2I_LOCAL_RELEASEPATH for the given platform specified by 340 # argument 1, exiting if more than one of either is found. 341 # 342 # If the tars are discovered, their full paths are exported to the following env vars: 343 # 344 # S2I_PRIMARY_RELEASE_TAR 345 s2i::build::detect_local_release_tars() { 346 local platform="$1" 347 348 if [[ ! -d "${S2I_LOCAL_RELEASEPATH}" ]]; then 349 echo "There are no release artifacts in ${S2I_LOCAL_RELEASEPATH}" 350 exit 2 351 fi 352 if [[ ! -f "${S2I_LOCAL_RELEASEPATH}/.commit" ]]; then 353 echo "There is no release .commit identifier ${S2I_LOCAL_RELEASEPATH}" 354 exit 2 355 fi 356 local primary=$(find ${S2I_LOCAL_RELEASEPATH} -maxdepth 1 -type f -name source-to-image-*-${platform}*) 357 if [[ $(echo "${primary}" | wc -l) -ne 1 ]]; then 358 echo "There should be exactly one ${platform} primary tar in $S2I_LOCAL_RELEASEPATH" 359 exit 2 360 fi 361 362 export S2I_PRIMARY_RELEASE_TAR="${primary}" 363 export S2I_RELEASE_COMMIT="$(cat ${S2I_LOCAL_RELEASEPATH}/.commit)" 364 } 365 366 367 # s2i::build::get_version_vars loads the standard version variables as 368 # ENV vars 369 s2i::build::get_version_vars() { 370 if [[ -n ${S2I_VERSION_FILE-} ]]; then 371 source "${S2I_VERSION_FILE}" 372 return 373 fi 374 s2i::build::sti_version_vars 375 } 376 377 # s2i::build::sti_version_vars looks up the current Git vars 378 s2i::build::sti_version_vars() { 379 local git=(git --work-tree "${S2I_ROOT}") 380 381 if [[ -n ${S2I_GIT_COMMIT-} ]] || S2I_GIT_COMMIT=$("${git[@]}" rev-parse --short "HEAD^{commit}" 2>/dev/null); then 382 if [[ -z ${S2I_GIT_TREE_STATE-} ]]; then 383 # Check if the tree is dirty. default to dirty 384 if git_status=$("${git[@]}" status --porcelain 2>/dev/null) && [[ -z ${git_status} ]]; then 385 S2I_GIT_TREE_STATE="clean" 386 else 387 S2I_GIT_TREE_STATE="dirty" 388 fi 389 fi 390 391 # Use git describe to find the version based on annotated tags. 392 if [[ -n ${S2I_GIT_VERSION-} ]] || S2I_GIT_VERSION=$("${git[@]}" describe --tags "${S2I_GIT_COMMIT}^{commit}" 2>/dev/null); then 393 if [[ "${S2I_GIT_TREE_STATE}" == "dirty" ]]; then 394 # git describe --dirty only considers changes to existing files, but 395 # that is problematic since new untracked .go files affect the build, 396 # so use our idea of "dirty" from git status instead. 397 S2I_GIT_VERSION+="-dirty" 398 fi 399 400 # Try to match the "git describe" output to a regex to try to extract 401 # the "major" and "minor" versions and whether this is the exact tagged 402 # version or whether the tree is between two tagged versions. 403 if [[ "${S2I_GIT_VERSION}" =~ ^v([0-9]+)\.([0-9]+)([.-].*)?$ ]]; then 404 S2I_GIT_MAJOR=${BASH_REMATCH[1]} 405 S2I_GIT_MINOR=${BASH_REMATCH[2]} 406 if [[ -n "${BASH_REMATCH[3]}" ]]; then 407 S2I_GIT_MINOR+="+" 408 fi 409 fi 410 fi 411 fi 412 } 413 414 # Saves the environment flags to $1 415 s2i::build::save_version_vars() { 416 local version_file=${1-} 417 [[ -n ${version_file} ]] || { 418 echo "!!! Internal error. No file specified in s2i::build::save_version_vars" 419 return 1 420 } 421 422 cat <<EOF >"${version_file}" 423 S2I_GIT_COMMIT='${S2I_GIT_COMMIT-}' 424 S2I_GIT_TREE_STATE='${S2I_GIT_TREE_STATE-}' 425 S2I_GIT_VERSION='${S2I_GIT_VERSION-}' 426 S2I_GIT_MAJOR='${S2I_GIT_MAJOR-}' 427 S2I_GIT_MINOR='${S2I_GIT_MINOR-}' 428 EOF 429 } 430 431 # golang 1.5 wants `-X key=val`, but golang 1.4- REQUIRES `-X key val` 432 s2i::build::ldflag() { 433 local key=${1} 434 local val=${2} 435 436 GO_VERSION=($(go version)) 437 if [[ -n $(echo "${GO_VERSION[2]}" | grep -E 'go1.4') ]]; then 438 echo "-X ${S2I_GO_PACKAGE}/pkg/version.${key} ${val}" 439 else 440 echo "-X ${S2I_GO_PACKAGE}/pkg/version.${key}=${val}" 441 fi 442 } 443 444 # s2i::build::ldflags calculates the -ldflags argument for building STI 445 s2i::build::ldflags() { 446 ( 447 # Run this in a subshell to prevent settings/variables from leaking. 448 set -o errexit 449 set -o nounset 450 set -o pipefail 451 452 cd "${S2I_ROOT}" 453 454 s2i::build::get_version_vars 455 456 declare -a ldflags=() 457 ldflags+=($(s2i::build::ldflag "majorFromGit" "${S2I_GIT_MAJOR}")) 458 ldflags+=($(s2i::build::ldflag "minorFromGit" "${S2I_GIT_MINOR}")) 459 ldflags+=($(s2i::build::ldflag "versionFromGit" "${S2I_GIT_VERSION}")) 460 ldflags+=($(s2i::build::ldflag "commitFromGit" "${S2I_GIT_COMMIT}")) 461 # The -ldflags parameter takes a single string, so join the output. 462 echo "${ldflags[*]-}" 463 ) 464 }