gopkg.in/openshift/source-to-image.v1@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  }