#!/bin/bash -eu # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 # # fdb-go-install.sh # # Installs the FoundationDB Go bindings for a client. This will download # the repository from the remote repo either into the go directory # with the appropriate semantic version. It will then build a few # generated files that need to be present for the go build to work. # At the end, it has some advice for flags to modify within your # go environment so that other packages may successfully use this # library. # DESTDIR="${DESTDIR:-}" FDBVER="${FDBVER:-5.2.4}" REMOTE="${REMOTE:-github.com}" FDBREPO="${FDBREPO:-apple/foundationdb}" status=0 platform=$(uname) if [[ "${platform}" == "Darwin" ]] ; then FDBLIBDIR="${FDBLIBDIR:-/usr/local/lib}" libfdbc="libfdb_c.dylib" elif [[ "${platform}" == "Linux" ]] ; then libfdbc="libfdb_c.so" custom_libdir="${FDBLIBDIR:-}" FDBLIBDIR="" if [[ -z "${custom_libdir}" ]]; then search_libdirs=( '/usr/lib' '/usr/lib64' ) else search_libdirs=( "${custom_libdir}" ) fi for libdir in "${search_libdirs[@]}" ; do if [[ -e "${libdir}/${libfdbc}" ]]; then FDBLIBDIR="${libdir}" break fi done if [[ -z "${FDBLIBDIR}" ]]; then echo "The FoundationDB C library could not be found in any of:" for libdir in "${search_libdirs[@]}" ; do echo " ${libdir}" done echo "Your installation may be incomplete, or you need to set a custom FDBLIBDIR." let status="${status} + 1" fi else echo "Unsupported platform ${platform}". echo "At the moment, only macOS and Linux are supported by this script." let status="${status} + 1" fi filedir=$(cd `dirname "${BASH_SOURCE[0]}"` && pwd) destdir="" function printUsage() { echo "Usage: fdb-go-install.sh " echo echo "cmd: One of the commands to run. The options are:" echo " install Download the FDB go bindings and install them" echo " localinstall Install a into the go path a local copy of the repo" echo " download Download but do not prepare the FoundationDB bindings" echo " help Print this help message and then quit" echo echo "Command Line Options:" echo " --fdbver FoundationDB semantic version (default is ${FDBVER})" echo " -d/--dest-dir Local location for the repo (default is to place in go path)" echo echo "Environment Variable Options:" echo " REMOTE Remote repository to download from (currently ${REMOTE})" echo " FDBREPO Repository of FoundationDB library to download (currently ${FDBREPO})" echo " FDBLIBDIR Directory within which should be the FoundationDB c library (currently ${FDBLIBDIR})" } function parseArgs() { local status=0 if [[ "${#}" -lt 0 ]] ; then printUsage let status="${status} + 1" else operation="${1}" shift if [[ "${operation}" != "install" ]] && [[ "${operation}" != "localinstall" ]] && [[ "${operation}" != "download" ]] && [[ "${operation}" != "help" ]] ; then echo "Unknown command: ${operation}" printUsage let status="${status} + 1" fi fi while [[ "${#}" -gt 0 ]] && [[ "${status}" -eq 0 ]] ; do local key="${1}" case "${key}" in --fdbver) if [[ "${#}" -lt 2 ]] ; then echo "No version specified with --fdbver flag" printUsage let status="${status} + 1" else FDBVER="${2}" fi shift ;; -d|--dest-dir) if [[ "${#}" -lt 2 ]] ; then echo "No destination specified with ${key} flag" printUsage let status="${status} + 1" else destdir="${2}" fi shift ;; *) echo "Unrecognized argument ${key}" printUsage let status="${status} + 1" esac shift done return "${status}" } function checkBin() { if [[ "${#}" -lt 1 ]] ; then echo "Usage: checkBin " return 1 else if [[ -n $(which "${1}") ]] ; then return 0 else return 1 fi fi } if [[ "${status}" -gt 0 ]] ; then # We have already failed. : elif [[ "${#}" -lt 1 ]] ; then printUsage else required_bins=( 'go' 'git' 'make' 'mono' ) missing_bins=() for bin in "${required_bins[@]}" ; do if ! checkBin "${bin}" ; then missing_bins+=("${bin}") let status="${status} + 1" fi done if [[ "${status}" -gt 0 ]] ; then echo "Missing binaries: ${missing_bins[*]}" elif ! parseArgs ${@} ; then let status="${status} + 1" elif [[ "${operation}" == "help" ]] ; then printUsage else # Add go-specific environment variables. eval $(go env) golibdir=$(dirname "${GOPATH}/src/${REMOTE}/${FDBREPO}") if [[ -z "${destdir}" ]] ; then if [[ "${operation}" == "localinstall" ]] ; then # Assume its the local directory. destdir=$(cd "${filedir}/../../.." && pwd) else destdir="${golibdir}" fi fi if [[ ! -d "${destdir}" ]] ; then cmd=( 'mkdir' '-p' "${destdir}" ) echo "${cmd[*]}" if ! "${cmd[@]}" ; then let status="${status} + 1" echo "Could not create destination directory ${destdir}." fi fi # Step 1: Make sure repository is present. if [[ "${status}" -eq 0 ]] ; then destdir=$( cd "${destdir}" && pwd ) # Get absolute path of destination dir. fdbdir="${destdir}/foundationdb" if [[ ! -d "${destdir}" ]] ; then cmd=("mkdir" "-p" "${destdir}") echo "${cmd[*]}" if ! "${cmd[@]}" ; then echo "Could not create destination directory ${destdir}." let status="${status} + 1" fi fi fi if [[ "${operation}" == "localinstall" ]] ; then # No download occurs in this case. : else if [[ -d "${fdbdir}" ]] ; then echo "Directory ${fdbdir} already exists ; checking out appropriate tag" cmd1=( 'git' '-C' "${fdbdir}" 'fetch' 'origin' ) cmd2=( 'git' '-C' "${fdbdir}" 'checkout' "${FDBVER}" ) if ! echo "${cmd1[*]}" || ! "${cmd1[@]}" ; then let status="${status} + 1" echo "Could not pull latest changes from origin" elif ! echo "${cmd2[*]}" || ! "${cmd2[@]}" ; then let status="${status} + 1" echo "Could not checkout tag ${FDBVER}." fi else echo "Downloading foundation repository into ${destdir}:" cmd=( 'git' '-C' "${destdir}" 'clone' '--branch' "${FDBVER}" "https://${REMOTE}/${FDBREPO}.git" ) echo "${cmd[*]}" if ! "${cmd[@]}" ; then let status="${status} + 1" echo "Could not download repository." fi fi fi # Step 2: Build generated things. if [[ "${operation}" == "download" ]] ; then # The generated files are not created under a strict download. : elif [[ "${status}" -eq 0 ]] ; then echo "Building generated files." # FoundationDB starting with 6.0 can figure that out on its own if [ -e '/usr/bin/mcs' ]; then MCS_BIN=/usr/bin/mcs else MCS_BIN=/usr/bin/dmcs fi cmd=( 'make' '-C' "${fdbdir}" 'bindings/c/foundationdb/fdb_c_options.g.h' "MCS=$MCS_BIN" ) echo "${cmd[*]}" if ! "${cmd[@]}" ; then let status="${status} + 1" echo "Could not generate required c header" else infile="${fdbdir}/fdbclient/vexillographer/fdb.options" outfile="${fdbdir}/bindings/go/src/fdb/generated.go" cmd=( 'go' 'run' "${fdbdir}/bindings/go/src/_util/translate_fdb_options.go" ) echo "${cmd[*]} < ${infile} > ${outfile}" if ! "${cmd[@]}" < "${infile}" > "${outfile}" ; then let status="${status} + 1" echo "Could not generate generated go file." fi fi fi # Step 3: Add to go path. if [[ "${operation}" == "download" ]] ; then # The files are not moved under a strict download. : elif [[ "${status}" -eq 0 ]] ; then linkpath="${GOPATH}/src/${REMOTE}/${FDBREPO}" if [[ "${linkpath}" == "${fdbdir}" ]] ; then # Downloaded directly into go path. Skip making the link. : elif [[ -e "${linkpath}" ]] ; then echo "Warning: link path (${linkpath}) already exists. Leaving in place." else dirpath=$(dirname "${linkpath}") if [[ ! -d "${dirpath}" ]] ; then cmd=( 'mkdir' '-p' "${dirpath}" ) echo "${cmd[*]}" if ! "${cmd[@]}" ; then let status="${status} + 1" echo "Could not create directory for link." fi fi if [[ "${status}" -eq 0 ]] ; then cmd=( 'ln' '-s' "${fdbdir}" "${linkpath}" ) echo "${cmd[*]}" if ! "${cmd[@]}" ; then let status="${status} + 1" echo "Could not create link within go path." fi fi fi fi # Step 4: Build the binaries. if [[ "${operation}" == "download" ]] ; then # Do not install if only downloading : elif [[ "${status}" -eq 0 ]] ; then cgo_cppflags="-I${linkpath}/bindings/c" cgo_cflags="-g -O2" cgo_ldflags="-L${FDBLIBDIR}" fdb_go_path="${REMOTE}/${FDBREPO}/bindings/go/src" if ! CGO_CPPFLAGS="${cgo_cppflags}" CGO_CFLAGS="${cgo_cflags}" CGO_LDFLAGS="${cgo_ldflags}" go install "${fdb_go_path}/fdb" "${fdb_go_path}/fdb/tuple" "${fdb_go_path}/fdb/subspace" "${fdb_go_path}/fdb/directory" ; then let status="${status} + 1" echo "Could not build FoundationDB go libraries." fi fi # Step 5: Explain CGO flags. if [[ "${status}" -eq 0 && ("${operation}" == "localinstall" || "${operation}" == "install" ) ]] ; then echo echo "The FoundationDB go bindings were successfully installed." echo "To build packages which use the go bindings, you will need to" echo "set the following environment variables:" echo " CGO_CPPFLAGS=\"${cgo_cppflags}\"" echo " CGO_CFLAGS=\"${cgo_cflags}\"" echo " CGO_LDFLAGS=\"${cgo_ldflags}\"" fi fi fi exit "${status}"