#!/bin/bash # # # mklove base configure module, implements the mklove configure framework # MKL_MODULES="base" MKL_CACHEVARS="CFLAGS LDFLAGS PKG_CONFIG_PATH" MKL_MKVARS="" MKL_DEFINES="" MKL_CHECKS="" MKL_LOAD_STACK="" MKL_IDNEXT=1 # Default mklove directory to PWD/mklove [[ -z "$MKLOVE_DIR" ]] && MKLOVE_DIR="$PWD/mklove" MKL_OUTMK="$PWD/_mklout.mk" MKL_OUTH="$PWD/_mklout.h" MKL_OUTDBG="$PWD/config.log" MKL_GENERATORS="base:mkl_generate_late_vars" MKL_CLEANERS="" MKL_FAILS="" MKL_LATE_VARS="" MKL_OPTS_SET="" MKL_RED="" MKL_GREEN="" MKL_YELLOW="" MKL_BLUE="" MKL_CLR_RESET="" MKL_NO_DOWNLOAD=0 MKL_INSTALL_DEPS=n MKL_SOURCE_DEPS_ONLY=n MKL_DESTDIR_ADDED=n if [[ -z "$MKL_REPO_URL" ]]; then MKL_REPO_URL="http://github.com/edenhill/mklove/raw/master" fi ########################################################################### # # Variable types: # env - Standard environment variables. # var - mklove runtime variable, cached or not. # mkvar - Makefile variables, also sets runvar # define - config.h variables/defines # ########################################################################### # Low level variable assignment # Arguments: # variable name # variable value function mkl_var0_set { export "$1"="$2" } # Sets a runtime variable (only used during configure) # If "cache" is provided these variables are cached to config.cache. # Arguments: # variable name # variable value # [ "cache" ] function mkl_var_set { mkl_var0_set "$1" "$2" if [[ $3 == "cache" ]]; then if ! mkl_in_list "$MKL_CACHEVARS" "$1" ; then MKL_CACHEVARS="$MKL_CACHEVARS $1" fi fi } # Unsets a mkl variable # Arguments: # variable name function mkl_var_unset { unset $1 } # Appends to a mkl variable (space delimited) # Arguments: # variable name # variable value function mkl_var_append { if [[ -z ${!1} ]]; then mkl_var_set "$1" "$2" else mkl_var0_set "$1" "${!1} $2" fi } # Prepends to a mkl variable (space delimited) # Arguments: # variable name # variable value function mkl_var_prepend { if [[ -z ${!1} ]]; then mkl_var_set "$1" "$2" else mkl_var0_set "$1" "$2 ${!1}" fi } # Shift the first word off a variable. # Arguments: # variable name function mkl_var_shift { local n="${!1}" mkl_var0_set "$1" "${n#* }" return 0 } # Returns the contents of mkl variable # Arguments: # variable name function mkl_var_get { echo "${!1}" } # Set environment variable (runtime) # These variables are not cached nor written to any of the output files, # its just simply a helper wrapper for standard envs. # Arguments: # varname # varvalue function mkl_env_set { mkl_var0_set "$1" "$2" } # Append to environment variable # Arguments: # varname # varvalue # [ separator (" ") ] function mkl_env_append { local sep=" " if [[ -z ${!1} ]]; then mkl_env_set "$1" "$2" else [ ! -z ${3} ] && sep="$3" mkl_var0_set "$1" "${!1}${sep}$2" fi } # Prepend to environment variable # Arguments: # varname # varvalue # [ separator (" ") ] function mkl_env_prepend { local sep=" " if [[ -z ${!1} ]]; then mkl_env_set "$1" "$2" else [ ! -z ${3} ] && sep="$3" mkl_var0_set "$1" "$2${sep}${!1}" fi } # Set a make variable (Makefile.config) # Arguments: # config name # variable name # value function mkl_mkvar_set { if [[ ! -z $2 ]]; then mkl_env_set "$2" "$3" mkl_in_list "$MKL_MKVARS" "$2"|| mkl_env_append MKL_MKVARS $2 fi } # Prepends to a make variable (Makefile.config) # Arguments: # config name # variable name # value # [ separator (" ") ] function mkl_mkvar_prepend { if [[ ! -z $2 ]]; then mkl_env_prepend "$2" "$3" "$4" mkl_in_list "$MKL_MKVARS" "$2"|| mkl_env_append MKL_MKVARS $2 fi } # Appends to a make variable (Makefile.config) # Arguments: # config name # variable name # value # [ separator (" ") ] function mkl_mkvar_append { if [[ ! -z $2 ]]; then mkl_env_append "$2" "$3" "$4" mkl_in_list "$MKL_MKVARS" "$2"|| mkl_env_append MKL_MKVARS $2 fi } # Prepends to a make variable (Makefile.config) # Arguments: # config name # variable name # value # [ separator (" ") ] function mkl_mkvar_prepend { if [[ ! -z $2 ]]; then mkl_env_prepend "$2" "$3" "$4" mkl_in_list "$MKL_MKVARS" "$2"|| mkl_env_append MKL_MKVARS $2 fi } # Return mkvar variable value # Arguments: # variable name function mkl_mkvar_get { [[ -z ${!1} ]] && return 1 echo ${!1} return 0 } # Defines a config header define (config.h) # Arguments: # config name # define name # define value (optional, default: 1) # if value starts with code: then no "" are added function mkl_define_set { if [[ -z $2 ]]; then return 0 fi local stmt="" local defid= if [[ $2 = *\(* ]]; then # macro defid="def_${2%%(*}" else # define defid="def_$2" fi [[ -z $1 ]] || stmt="// $1\n" local val="$3" if [[ -z "$val" ]]; then val="$(mkl_def $2 1)" fi # Define as code, string or integer? if [[ $val == code:* ]]; then # Code block, copy verbatim without quotes, strip code: prefix val=${val#code:} elif [[ ! ( "$val" =~ ^[0-9]+([lL]?[lL][dDuU]?)?$ || \ "$val" =~ ^0x[0-9a-fA-F]+([lL]?[lL][dDuU]?)?$ ) ]]; then # String: quote val="\"$val\"" fi # else: unquoted integer/hex stmt="${stmt}#define $2 $val" mkl_env_set "$defid" "$stmt" mkl_env_append MKL_DEFINES "$defid" } # Sets "all" configuration variables, that is: # for name set: Makefile variable, config.h define # Will convert value "y"|"n" to 1|0 for config.h # Arguments: # config name # variable name # value function mkl_allvar_set { mkl_mkvar_set "$1" "$2" "$3" local val=$3 if [[ $3 = "y" ]]; then val=1 elif [[ $3 = "n" ]]; then val=0 fi mkl_define_set "$1" "$2" "$val" } ########################################################################### # # Dependency installation, et.al. # # ########################################################################### # Returns the local dependency directory. function mkl_depdir { local dir="$MKLOVE_DIR/deps" [[ -d $dir ]] || mkdir -p "$dir" if ! grep -q ^deps$ "$MKLOVE_DIR/.gitignore" 2>/dev/null ; then echo "deps" >> "$MKLOVE_DIR/.gitignore" fi echo "$dir" } # Returns the package's installation directory / DESTDIR. function mkl_dep_destdir { echo "$(mkl_depdir)/dest" } # Returns the package's source directory. function mkl_dep_srcdir { echo "$(mkl_depdir)/src/$1" } # Get the static library file name(s) for a package. function mkl_lib_static_fnames { local name=$1 mkl_meta_get $name "static" "" } # Returns true if previous ./configure ran a dep install for this package. function mkl_dep_install_cached { local name=$1 if [[ -n $(mkl_var_get "MKL_STATUS_${1}_INSTALL") ]] || [[ -n $(mkl_var_get "MKL_STATUS_${1}_INSTALL_SRC") ]]; then return 0 # true else return 1 # false fi } # Install an external dependency using the platform's native # package manager. # Should only be called from mkl_dep_install # # Param 1: config name function mkl_dep_install_pkg { local name=$1 local iname="${name}_INSTALL" local retcode=1 # default to fail local method="none" local pkgs local cmd mkl_dbg "Attempting native install of dependency $name on $MKL_DISTRO with effective user $EUID" # Try the platform specific installer first. case ${MKL_DISTRO}-${EUID} in debian-0|ubuntu-0) method=apt pkgs=$(mkl_meta_get $name deb) cmd="apt install -y $pkgs" ;; centos-0|rhel-0|redhat-0|fedora-0) method=yum pkgs=$(mkl_meta_get $name rpm) cmd="yum install -y $pkgs" ;; alpine-0) method=apk pkgs=$(mkl_meta_get $name apk) cmd="apk add $pkgs" ;; osx-*) method=brew pkgs=$(mkl_meta_get $name brew) cmd="brew install $pkgs" ;; *) mkl_dbg "$name: No native installer set for $name on $MKL_DISTRO (euid $EUID)" return 1 ;; esac if [[ -z $pkgs ]]; then mkl_dbg "$name: No packages to install ($method)" return 1 fi mkl_check_begin --verb "installing dependencies ($cmd)" $iname "" no-cache "$name" $cmd >>$MKL_OUTDBG 2>&1 retcode=$? if [[ $retcode -eq 0 ]]; then mkl_dbg "Native install of $name (using $method, $cmd) succeeded" mkl_check_done "$iname" "" cont "using $method" mkl_meta_set $name installed_with "$method" elif [[ $method != "none" ]]; then mkl_dbg "Native install of $name (using $method, $cmd) failed: retcode $retcode" mkl_check_failed "$iname" "" cont "using $method" fi return $retcode } # Returns 0 (yes) if this dependency has a source builder, else 1 (no) function mkl_dep_has_builder { local name=$1 local func="${name}_install_source" mkl_func_exists $func } # Returns 0 (yes) if this dependency has a package installer, else 1 (no) function mkl_dep_has_installer { local name=$1 if mkl_dep_has_builder "$name" || \ [[ -n $(mkl_meta_get $name deb) ]] || \ [[ -n $(mkl_meta_get $name rpm) ]] || \ [[ -n $(mkl_meta_get $name brew) ]] || \ [[ -n $(mkl_meta_get $name apk) ]]; then return 0 else return 1 fi } # Install an external dependency from source. # # The resulting libraries must be installed in $ddir/usr/lib (or lib64), # and include files in $ddir/usr/include. # # Any dependency installed from source will be linked statically # regardless of --enable-static, if the build produced static libraries. # # Param 1: config name function mkl_dep_install_source { local name=$1 local iname="${name}_INSTALL_SRC" local retcode= local func="${name}_install_source" if ! mkl_dep_has_builder $name ; then mkl_dbg "No source builder for $name ($func) available" return 1 fi mkl_check_begin --verb "building dependency" $iname "" no-cache "$name" # Create install directory / DESTDIR local ddir=$(mkl_dep_destdir $name) [[ -d $ddir ]] || mkdir -p "$ddir" # Create and go to source directory local sdir=$(mkl_dep_srcdir $name) [[ -d $sdir ]] || mkdir -p "$sdir" mkl_pushd "$sdir" local ilog="${sdir}/_mkl_install.log" # Build and install mkl_dbg "Building $name from source in $sdir (func $func)" $func $name "$ddir" >$ilog 2>&1 retcode=$? mkl_popd # $sdir if [[ $retcode -eq 0 ]]; then mkl_dbg "Source install of $name succeeded" mkl_check_done "$iname" "" cont "ok" "from source" mkl_meta_set $name installed_with "source" else mkl_dbg "Source install of $name failed" mkl_check_failed "$iname" "" disable "source installer failed (see $ilog)" mkl_err "$name source build failed, see $ilog for details. First 50 and last 50 lines:" head -50 "$ilog" echo " .... and last 50 lines ...." tail -50 "$ilog" fi return $retcode } # Tries to resolve/find full paths to static libraries for a module, # using the provided scan dir path. # Any found libraries are set as STATIC_LIB_.. defines. # # Param 1: config name # Param 2: scandir # # Returns 0 if libraries were found, else 1. function mkl_resolve_static_libs { local name="$1" local scandir="$2" local stlibfnames=$(mkl_lib_static_fnames $name) local stlibvar="STATIC_LIB_${name}" if [[ -z $stlibfnames || -n "${!stlibvar}" ]]; then mkl_dbg "$name: not resolving static libraries (stlibfnames=$stlibfnames, $stlibvar=${!stlibvar})" mkl_allvar_set "$name" "WITH_STATIC_LIB_$name" y return 1 fi local fname= local stlibs="" mkl_dbg "$name: resolving static libraries from $stlibfnames in $scandir" for fname in $stlibfnames ; do local stlib=$(find "${scandir}" -name "$fname" 2>/dev/null | head -1) if [[ -n $stlib ]]; then stlibs="${stlibs} $stlib" fi done # Trim leading whitespaces stlibs=${stlibs# } if [[ -n $stlibs ]]; then mkl_dbg "$name: $stlibvar: found static libs: $stlibs" mkl_var_set $stlibvar "$stlibs" "cache" mkl_allvar_set "$name" "WITH_STATIC_LIB_$name" y return 0 else mkl_dbg "$name: did not find any static libraries for $stlibfnames in ${scandir}" return 1 fi } # Install an external dependecy # # Param 1: config name (e.g zstd) function mkl_dep_install { local name=$1 local retcode= local ddir=$(mkl_dep_destdir $name) if [[ $MKL_SOURCE_DEPS_ONLY != y ]] || ! mkl_dep_has_builder $name ; then # # Try native package manager first, or if no source builder # is available for this dependency. # mkl_dep_install_pkg $name retcode=$? if [[ $retcode -eq 0 ]]; then return $retcode fi fi # # Try source installer. # mkl_dep_install_source $name retcode=$? if [[ $retcode -ne 0 ]]; then if [[ $MKL_SOURCE_DEPS_ONLY == y ]]; then # Require dependencies, regardless of original action, # if --source-deps-only is specified, to ensure # that we do indeed link with the desired library. mkl_fail "$name" "" fail "Failed to install dependency $name" fi return $retcode fi local ddir=$(mkl_dep_destdir $name) # Find the static library(s), if any. if ! mkl_resolve_static_libs "$name" "${ddir}/usr"; then # No static libraries found, set up dynamic linker path mkl_mkvar_prepend LDFLAGS LDFLAGS "-L${ddir}/usr/lib64 -L${ddir}/usr/lib" fi # Add the deps destdir to various build flags so that tools can pick # up the artifacts (.pc files, includes, libs, etc) they need. if [[ $MKL_DESTDIR_ADDED == n ]]; then # Add environment variables so that later built dependencies # can find this one. mkl_env_prepend LDFLAGS "-L${ddir}/usr/lib64 -L${ddir}/usr/lib" mkl_env_prepend CPPFLAGS "-I${ddir}/usr/include" mkl_env_prepend PKG_CONFIG_PATH "${ddir}/usr/lib/pkgconfig" ":" # And tell pkg-config to get static linker flags. mkl_env_set PKG_CONFIG "${PKG_CONFIG} --static" MKL_DESTDIR_ADDED=y fi # Append the package's install path to compiler and linker flags. mkl_dbg "$name: Adding install-deps paths ($ddir) to compiler and linker flags" mkl_mkvar_prepend CPPFLAGS CPPFLAGS "-I${ddir}/usr/include" return $retcode } # Apply patch to a source dependency. # # Param 1: config name (e.g. libssl) # Param 2: patch number (optional, else all) # # Returns 0 on success or 1 on error. function mkl_patch { local name=$1 local patchnr="$2" if [[ -z $patchnr ]]; then patchnr="????" fi local patchfile= local cnt=0 for patchfile in $(echo ${MKLOVE_DIR}/modules/patches/${name}.${patchnr}-*.patch | sort); do mkl_dbg "$1: applying patch $patchfile" patch -p1 < $patchfile local retcode=$? if [[ $retcode != 0 ]]; then mkl_err "mkl_patch: $1: failed to apply patch $patchfile: see source dep build log for details" return 1 fi cnt=$(($cnt + 1)) done if [[ $cnt -lt 1 ]]; then mkl_err "mkl_patch: $1: no patches matchign $patchnr found" return 1 fi return 0 } ########################################################################### # # # Check failure functionality # # ########################################################################### # Summarize all fatal failures and then exits. function mkl_fail_summary { echo " " local pkg_cmd="" local install_pkgs="" mkl_err "###########################################################" mkl_err "### Configure failed ###" mkl_err "###########################################################" mkl_err "### Accumulated failures: ###" mkl_err "###########################################################" local n for n in $MKL_FAILS ; do local conf=$(mkl_var_get MKL_FAIL__${n}__conf) mkl_err " $conf ($(mkl_var_get MKL_FAIL__${n}__define)) $(mkl_meta_get $conf name)" if mkl_meta_exists $conf desc; then mkl_err0 " desc: $MKL_YELLOW$(mkl_meta_get $conf desc)$MKL_CLR_RESET" fi mkl_err0 " module: $(mkl_var_get MKL_FAIL__${n}__module)" mkl_err0 " action: $(mkl_var_get MKL_FAIL__${n}__action)" mkl_err0 " reason: $(mkl_var_get MKL_FAIL__${n}__reason) " # Dig up some metadata to assist the user case $MKL_DISTRO in debian|ubuntu) local debs=$(mkl_meta_get $conf "deb") pkg_cmd="sudo apt install -y" if [[ ${#debs} > 0 ]]; then install_pkgs="$install_pkgs $debs" fi ;; centos|rhel|redhat|fedora) local rpms=$(mkl_meta_get $conf "rpm") pkg_cmd="sudo yum install -y" if [[ ${#rpms} > 0 ]]; then install_pkgs="$install_pkgs $rpms" fi ;; alpine) local apks=$(mkl_meta_get $conf "apk") pkg_cmd="apk add " if [[ ${#apks} > 0 ]]; then install_pkgs="$install_pkgs $apks" fi ;; osx) local pkgs=$(mkl_meta_get $conf "brew") pkg_cmd="brew install" if [[ ${#pkgs} > 0 ]]; then install_pkgs="$install_pkgs $pkgs" fi ;; esac done if [[ ! -z $install_pkgs ]]; then mkl_err "###########################################################" mkl_err "### Installing the following packages might help: ###" mkl_err "###########################################################" mkl_err0 "$pkg_cmd $install_pkgs" mkl_err0 "" fi exit 1 } # Checks if there were failures. # Returns 0 if there were no failures, else calls failure summary and exits. function mkl_check_fails { if [[ ${#MKL_FAILS} = 0 ]]; then return 0 fi mkl_fail_summary } # A check has failed but we want to carry on (and we should!). # We fail it all later. # Arguments: # config name # define name # action # reason function mkl_fail { local n="$(mkl_env_esc "$1")" mkl_var_set "MKL_FAIL__${n}__conf" "$1" mkl_var_set "MKL_FAIL__${n}__module" $MKL_MODULE mkl_var_set "MKL_FAIL__${n}__define" $2 mkl_var_set "MKL_FAIL__${n}__action" "$3" if [[ -z $(mkl_var_get "MKL_FAIL__${n}__reason") ]]; then mkl_var_set "MKL_FAIL__${n}__reason" "$4" else mkl_var_append "MKL_FAIL__${n}__reason" " And also: $4" fi mkl_in_list "$MKL_FAILS" "$n" || mkl_var_append MKL_FAILS "$n" } # A check failed, handle it # Arguments: # config name # define name # action (fail|disable|ignore|cont) # reason function mkl_check_failed { # Override action based on require directives, unless the action is # set to cont (for fallthrough to sub-sequent tests). local action="$3" if [[ $3 != "cont" ]]; then action=$(mkl_meta_get "MOD__$MKL_MODULE" "override_action" $3) fi # --fail-fatal option [[ $MKL_FAILFATAL ]] && action="fail" mkl_check_done "$1" "$2" "$action" "failed" mkl_dbg "Check $1 ($2, action $action (originally $3)) failed: $4" case $action in fail) # Check failed fatally, fail everything eventually mkl_fail "$1" "$2" "$3" "$4" return 1 ;; disable) # Check failed, disable [[ ! -z $2 ]] && mkl_mkvar_set "$1" "$2" "n" return 1 ;; ignore) # Check failed but we ignore the results and set it anyway. [[ ! -z $2 ]] && mkl_define_set "$1" "$2" "1" [[ ! -z $2 ]] && mkl_mkvar_set "$1" "$2" "y" return 1 ;; cont) # Check failed but we ignore the results and do nothing. return 0 ;; esac } ########################################################################### # # # Output generators # # ########################################################################### # Generate late variables. # Late variables are those referenced in command line option defaults # but then never set by --option. function mkl_generate_late_vars { local n for n in $MKL_LATE_VARS ; do local func=${n%:*} local safeopt=${func#opt_} local val=${n#*:} if mkl_in_list "$MKL_OPTS_SET" "$safeopt" ; then # Skip options set explicitly with --option continue fi # Expand variable references "\$foo" by calling eval # and pass it opt_... function. $func "$(eval echo $val)" done } # Generate MKL_DYNAMIC_LIBS and MKL_STATIC_LIBS for Makefile.config # # Params: $LIBS function mkl_generate_libs { while [[ $# -gt 0 ]]; do if [[ $1 == -l* ]]; then mkl_mkvar_append "" MKL_DYNAMIC_LIBS $1 elif [[ $1 == *.a ]]; then mkl_mkvar_append "" MKL_STATIC_LIBS $1 elif [[ $1 == -framework ]]; then mkl_mkvar_append "" MKL_DYNAMIC_LIBS "$1 $2" shift # two args else mkl_dbg "Ignoring arg $1 from LIBS while building STATIC and DYNAMIC lists" fi shift # remove arg done } # Generate output files. # Must be called following a succesful configure run. function mkl_generate { # Generate MKL_STATIC_LIBS and MKL_DYNAMIC_LIBS from LIBS mkl_generate_libs $LIBS local mf= for mf in $MKL_GENERATORS ; do MKL_MODULE=${mf%:*} local func=${mf#*:} $func || exit 1 done # Generate a built-in options define based on WITH_..=y local with_y= for n in $MKL_MKVARS ; do if [[ $n == WITH_* ]] && [[ $n != WITH_STATIC_LIB_* ]] && [[ ${!n} == y ]]; then with_y="$with_y ${n#WITH_}" fi done with_y="${with_y# }" mkl_allvar_set "BUILT_WITH" "BUILT_WITH" "$with_y" mkl_write_mk "# Automatically generated by $0 $*" mkl_write_mk "# Config variables" mkl_write_mk "#" mkl_write_mk "# Generated by:" mkl_write_mk "# $MKL_CONFIGURE_ARGS" mkl_write_mk "" # This variable is used by Makefile.base to avoid multiple inclusions. mkl_write_mk "MKL_MAKEFILE_CONFIG=y" # Export colors to Makefile.config mkl_write_mk "MKL_RED=\t${MKL_RED}" mkl_write_mk "MKL_GREEN=\t${MKL_GREEN}" mkl_write_mk "MKL_YELLOW=\t${MKL_YELLOW}" mkl_write_mk "MKL_BLUE=\t${MKL_BLUE}" mkl_write_mk "MKL_CLR_RESET=\t${MKL_CLR_RESET}" local n= for n in $MKL_MKVARS ; do # Some special variables should be prefixable by the caller, so # define them in the makefile as appends. local op="=" case $n in CFLAGS|CPPFLAGS|CXXFLAGS|LDFLAGS|LIBS) op="+=" ;; esac mkl_write_mk "$n$op\t${!n}" done mkl_write_mk "# End of config variables" MKL_OUTMK_FINAL=Makefile.config mv $MKL_OUTMK $MKL_OUTMK_FINAL echo "Generated $MKL_OUTMK_FINAL" # Generate config.h mkl_write_h "// Automatically generated by $0 $*" mkl_write_h "#ifndef _CONFIG_H_" mkl_write_h "#define _CONFIG_H_" for n in $MKL_DEFINES ; do mkl_write_h "${!n}" done mkl_write_h "#endif /* _CONFIG_H_ */" MKL_OUTH_FINAL=config.h mv $MKL_OUTH $MKL_OUTH_FINAL echo "Generated $MKL_OUTH_FINAL" } # Remove file noisily, if it exists function mkl_rm { if [[ -f $fname ]]; then echo "Removing $fname" rm -f "$fname" fi } # Remove files generated by configure function mkl_clean { for fname in Makefile.config config.h config.cache config.log ; do mkl_rm "$fname" done local mf= for mf in $MKL_CLEANERS ; do MKL_MODULE=${mf%:*} local func=${mf#*:} $func || exit 1 done } # Print summary of succesful configure run function mkl_summary { echo " Configuration summary:" local n= for n in $MKL_MKVARS ; do # Skip the boring booleans if [[ $n == ENABLE_* || $n == WITH_* || $n == WITHOUT_* || $n == HAVE_* || $n == def_* ]]; then continue fi printf " %-24s %s\n" "$n" "${!n}" done } # Write to mk file # Argument: # string .. function mkl_write_mk { echo -e "$*" >> $MKL_OUTMK } # Write to header file # Argument: # string .. function mkl_write_h { echo -e "$*" >> $MKL_OUTH } ########################################################################### # # # Logging and debugging # # ########################################################################### # Debug print # Only visible on terminal if MKL_DEBUG is set. # Always written to config.log # Argument: # string .. function mkl_dbg { if [[ ! -z $MKL_DEBUG ]]; then echo -e "${MKL_BLUE}DBG:$$: $*${MKL_CLR_RESET}" 1>&2 fi echo "DBG $$: $*" >> $MKL_OUTDBG } # Error print (with color) # Always printed to terminal and config.log # Argument: # string .. function mkl_err { echo -e "${MKL_RED}$*${MKL_CLR_RESET}" 1>&2 echo "$*" >> $MKL_OUTDBG } # Same as mkl_err but without coloring # Argument: # string .. function mkl_err0 { echo -e "$*" 1>&2 echo "$*" >> $MKL_OUTDBG } # Standard print # Always printed to terminal and config.log # Argument: # string .. function mkl_info { echo -e "$*" 1>&2 echo -e "$*" >> $MKL_OUTDBG } ########################################################################### # # # Misc helpers # # ########################################################################### # Returns the absolute path (but not necesarily canonical) of the first argument function mkl_abspath { echo $1 | sed -e "s|^\([^/]\)|$PWD/\1|" } # Returns true (0) if function $1 exists, else false (1) function mkl_func_exists { declare -f "$1" > /dev/null return $? } # Rename function. # Returns 0 on success or 1 if old function (origname) was not defined. # Arguments: # origname # newname function mkl_func_rename { if ! mkl_func_exists $1 ; then return 1 fi local orig=$(declare -f $1) local new="$2${orig#$1}" eval "$new" unset -f "$1" return 0 } # Push module function for later call by mklove. # The function is renamed to an internal name. # Arguments: # list variable name # module name # function name function mkl_func_push { local newfunc="__mkl__f_${2}_$(( MKL_IDNEXT++ ))" if mkl_func_rename "$3" "$newfunc" ; then mkl_var_append "$1" "$2:$newfunc" fi } # Returns value, or the default string if value is empty. # Arguments: # value # default function mkl_def { if [[ ! -z $1 ]]; then echo $1 else echo $2 fi } # Render a string (e.g., evaluate its $varrefs) # Arguments: # string function mkl_render { if [[ $* == *\$* ]]; then eval "echo $*" else echo "$*" fi } # Escape a string so that it becomes suitable for being an env variable. # This is a destructive operation and the original string cannot be restored. function mkl_env_esc { echo $* | LC_ALL=C sed -e 's/[^a-zA-Z0-9_]/_/g' } # Convert arguments to upper case function mkl_upper { echo "$*" | tr '[:lower:]' '[:upper:]' } # Convert arguments to lower case function mkl_lower { echo "$*" | tr '[:upper:]' '[:lower:]' } # Checks if element is in list # Arguments: # list # element function mkl_in_list { local n for n in $1 ; do [[ $n == $2 ]] && return 0 done return 1 } # Silent versions of pushd and popd function mkl_pushd { pushd "$1" >/dev/null } function mkl_popd { popd >/dev/null } ########################################################################### # # # Cache functionality # # ########################################################################### # Write cache file function mkl_cache_write { [[ ! -z "$MKL_NOCACHE" ]] && return 0 echo "# mklove configure cache file generated at $(date)" > config.cache for n in $MKL_CACHEVARS ; do echo "$n=${!n}" >> config.cache done echo "Generated config.cache" } # Read cache file function mkl_cache_read { [[ ! -z "$MKL_NOCACHE" ]] && return 0 [ -f config.cache ] || return 1 echo "using cache file config.cache" local ORIG_IFS=$IFS IFS="$IFS=" while read -r n v ; do [[ -z $n || $n = \#* || -z $v ]] && continue # Don't let cache overwrite variables [[ -n ${n+r} ]] || mkl_var_set $n $v cache done < config.cache IFS=$ORIG_IFS } ########################################################################### # # # Config name meta data # # ########################################################################### # Set metadata for config name # This metadata is used by mkl in various situations # Arguments: # config name # metadata key # metadata value (appended) function mkl_meta_set { local metaname="mkl__$1__$2" eval "$metaname=\"\$$metaname $3\"" } # Returns metadata for config name # Arguments: # config name # metadata key # default (optional) function mkl_meta_get { local metaname="mkl__$1__$2" if [[ ! -z ${!metaname} ]]; then echo ${!metaname} else echo "$3" fi } # Checks if metadata exists # Arguments: # config name # metadata key function mkl_meta_exists { local metaname="mkl__$1__$2" if [[ ! -z ${!metaname} ]]; then return 0 else return 1 fi } ########################################################################### # # # Check framework # # ########################################################################### # Print that a check is beginning to run # Returns 0 if a cached result was used (do not continue with your tests), # else 1. # # If the check should not be cachable then specify argument 3 as "no-cache", # this is useful when a check not only checks but actually sets config # variables itself (which is not recommended, but desired sometimes). # # Arguments: # [ --verb "verb.." ] (replace "checking for") # config name # define name # action (fail,cont,disable or no-cache) # [ display name ] function mkl_check_begin { local verb="checking for" if [[ $1 == "--verb" ]]; then verb="$2" shift shift fi local name=$(mkl_meta_get $1 name "$4") [[ -z $name ]] && name="$1" echo -n "$verb $name..." if [[ $3 != "no-cache" ]]; then local status=$(mkl_var_get "MKL_STATUS_$1") # Check cache (from previous run or this one). # Only used cached value if the cached check succeeded: # it is more likely that a failed check has been fixed than the other # way around. if [[ ! -z $status && ( $status = "ok" ) ]]; then mkl_check_done "$1" "$2" "$3" $status "cached" return 0 fi fi return 1 } # Calls the manual_checks function for the given module. # Use this for modules that provide check hooks that require # certain call ordering, such as dependent library checks. # # Param 1: module name function mkl_check { local modname=$1 local func="${modname}_manual_checks" if ! mkl_func_exists "$func" ; then mkl_fail "Check function for module $modname not found: missing mkl_require $modname ?" return 1 fi $func return $? } # Print that a check is done # Arguments: # config name # define name # action # status (ok|failed) # extra-info (optional) function mkl_check_done { # Clean up configname to be a safe varname local cname=${1//-/_} mkl_var_set "MKL_STATUS_$cname" "$4" cache mkl_dbg "Setting $1 ($cname) status to $4 (action $3)" local extra="" if [[ $4 = "failed" ]]; then local clr=$MKL_YELLOW extra=" ($3)" case "$3" in fail) clr=$MKL_RED ;; cont) extra="" ;; esac echo -e " $clr$4$MKL_CLR_RESET${extra}" else [[ ! -z $2 ]] && mkl_define_set "$cname" "$2" "1" [[ ! -z $2 ]] && mkl_mkvar_set "$cname" "$2" "y" [ ! -z "$5" ] && extra=" ($5)" echo -e " $MKL_GREEN$4${MKL_CLR_RESET}$extra" fi } # Perform configure check by compiling source snippet # Arguments: # [--sub] (run checker as a sub-check, not doing begin/fail/ok) # [--ldflags="..." ] (appended after "compiler arguments" below) # config name # define name # action (fail|disable) # compiler (CC|CXX) # compiler arguments (optional "", example: "-lzookeeper") # source snippet function mkl_compile_check { local sub=0 if [[ $1 == --sub ]]; then sub=1 shift fi local ldf= if [[ $1 == --ldflags=* ]]; then ldf=${1#*=} shift fi if [[ $sub -eq 0 ]]; then mkl_check_begin "$1" "$2" "$3" "$1 (by compile)" && return $? fi local cflags= if [[ $4 = "CXX" ]]; then local ext=cpp cflags="$(mkl_mkvar_get CXXFLAGS)" else local ext=c cflags="$(mkl_mkvar_get CFLAGS)" fi local srcfile=$(mktemp _mkltmpXXXXXX) mv "$srcfile" "${srcfile}.$ext" srcfile="$srcfile.$ext" echo "$6" > $srcfile echo " int main () { return 0; } " >> $srcfile local cmd="${!4} $cflags $(mkl_mkvar_get CPPFLAGS) -Wall -Werror $srcfile -o ${srcfile}.o $ldf $(mkl_mkvar_get LDFLAGS) $5 $(mkl_mkvar_get LIBS)"; mkl_dbg "Compile check $1 ($2) (sub=$sub): $cmd" local output output=$($cmd 2>&1) if [[ $? != 0 ]] ; then mkl_dbg "compile check for $1 ($2) failed: $cmd: $output" [[ $sub -eq 0 ]] && mkl_check_failed "$1" "$2" "$3" "compile check failed: CC: $4 flags: $5 $cmd: $output source: $6" local ret=1 else [[ $sub -eq 0 ]] && mkl_check_done "$1" "$2" "$3" "ok" local ret=0 fi # OSX XCode toolchain creates dSYM directories when -g is set, # delete them specifically. rm -rf "$srcfile" "${srcfile}.o" "$srcfile*dSYM" return $ret } # Low-level: Try to link with a library. # Arguments: # linker flags (e.g. "-lpthreads") function mkl_link_check0 { local libs=$1 local srcfile=$(mktemp _mktmpXXXXXX) echo "#include int main () { FILE *fp = stderr; return fp ? 0 : 0; }" > ${srcfile}.c local cmd="${CC} $(mkl_mkvar_get CFLAGS) $(mkl_mkvar_get LDFLAGS) ${srcfile}.c -o ${srcfile}_out $libs"; mkl_dbg "Link check for $1: $cmd" local output output=$($cmd 2>&1) local retcode=$? if [[ $retcode -ne 0 ]] ; then mkl_dbg "Link check for $1 failed: $output" fi rm -f $srcfile* return $retcode } # Try to link with a library. # Arguments: # config name # define name # action (fail|disable) # linker flags (e.g. "-lpthreads") function mkl_link_check { mkl_check_begin "$1" "$2" "$3" "$1 (by linking)" && return $? if mkl_link_check0 "$4" ; then mkl_check_done "$1" "$2" "$3" "ok" "$4" return 0 else mkl_dbg "link check for $1 ($2) failed: $output" mkl_check_failed "$1" "$2" "$3" "compile check failed: $output" return 1 fi } # Tries to figure out if we can use a static library or not. # # WARNING: This function must not emit any stdout output other than the # updated list of libs. Do not use any stdout-printing checker. # # Arguments: # config name (e.g., zstd) # compiler flags (optional "", e.g: "-lzstd") # Returns/outputs: # New list of compiler flags function mkl_lib_check_static { local configname=$1 local libs=$2 local arfile_var=STATIC_LIB_${configname} local stfnames=$(mkl_lib_static_fnames $configname) mkl_dbg "$configname: Check for static library (libs $libs, arfile variable $arfile_var=${!arfile_var}, static filenames $stfnames)" # If STATIC_LIB_ specifies .a file(s) we use that instead. if [[ -n ${!arfile_var} ]]; then libs="${!arfile_var}" elif [[ $WITH_STATIC_LINKING != y ]]; then # Static linking not enabled echo "" return elif [[ $HAS_LDFLAGS_STATIC == y ]] && [[ -n $stfnames ]]; then local libname local stlibs= for libname in $stfnames; do # Convert the static filename to a linker flag: # libzstd.a -> -lzstd libname=${libname#lib} libname="-l${libname%.a}" stlibs="${stlibs}${libname} " done libs="${LDFLAGS_STATIC} $stlibs ${LDFLAGS_DYNAMIC}" mkl_dbg "$configname: after replacing libs: $libs" elif [[ $libs == *-L* ]]; then # Try to resolve full static paths using any -Lpaths in $libs local lpath for lpath in $libs; do [[ $lpath == -L* ]] || continue lpath="${lpath#-L}" [[ -d $lpath ]] || continue if mkl_resolve_static_libs "$configname" "$lpath"; then break fi done libs="${!arfile_var}" mkl_dbg "$configname: after -L resolve, libs is $libs" else mkl_dbg "$configname: Neither $arfile_var=/path/to/libname.a specified nor static linker flags supported: static linking probably won't work" libs="" fi if [[ -z $libs ]]; then echo "" return fi # Attempt to link a small program with these static libraries mkl_dbg "$configname: verifying that linking \"$libs\" works" if ! mkl_link_check0 "$libs" ; then mkl_dbg "$configname: Could not use static libray flags: $libs" echo "" return fi mkl_allvar_set "$configname" "${configname}_STATIC" "y" echo $libs } # Checks that the specified lib is available through a number of methods. # compiler flags are automatically appended to "LIBS" mkvar on success. # # If STATIC_LIB_ is set to the path of an .a file # it will be used instead of -l. # # _STATIC will be automatically defined (for both Makefile.config # and config.h) if the library is to be linked statically, or was installed # with a source dependency installer. # # Arguments: # [--override-action=] (internal use, overrides action argument) # [--no-static] (do not attempt to link the library statically) # [--libname=] (library name if different from config name, such as # when the libname includes a dash) # config name (library name (for pkg-config)) # define name # action (fail|disable|cont) # compiler (CC|CXX) # compiler flags (optional "", e.g: "-lyajl") # source snippet function mkl_lib_check0 { local override_action= local nostaticopt= local libnameopt= local libname= while [[ $1 == --* ]]; do if [[ $1 == --override-action=* ]]; then override_action=${1#*=} elif [[ $1 == --no-static ]]; then nostaticopt=$1 elif [[ $1 == --libname* ]]; then libnameopt=$1 libname="${libnameopt#*=}" else mkl_err "mkl_lib_check: invalid option $1" exit 1 fi shift done if [[ -z $libname ]]; then libname=$1 fi local action=$3 if [[ -n $override_action ]]; then action=$override_action fi # pkg-config result (0=ok) local pkg_conf_failed=1 if [[ $WITH_PKGCONFIG == "y" ]]; then # Let pkg-config populate CFLAGS, et.al. # Return on success. mkl_pkg_config_check $nostaticopt $libnameopt "$1" "$2" cont "$4" "$6" && return $? fi local libs="$5" local is_static=0 if [[ -z $nostaticopt ]]; then local stlibs=$(mkl_lib_check_static $1 "$libs") if [[ -n $stlibs ]]; then libs=$stlibs is_static=1 fi fi if ! mkl_compile_check "$1" "$2" "$action" "$4" "$libs" "$6"; then return 1 fi if [[ -n $libs ]]; then # Add libraries in reverse order to make sure inter-dependencies # are resolved in the correct order. # E.g., check for crypto and then ssl should result in -lssl -lcrypto mkl_dbg "$1: from lib_check: LIBS: prepend $libs" mkl_mkvar_prepend "$1" LIBS "$libs" if [[ $is_static == 0 ]]; then # Static libraries are automatically bundled with # librdkafka-static.a so there is no need to add them as an # external linkage dependency. mkl_mkvar_prepend "$1" MKL_PKGCONFIG_LIBS_PRIVATE "$libs" fi fi return 0 } # Wrapper for mkl_lib_check0 which attempts dependency installation # if --install-deps is specified. # # See mkl_lib_check0 for arguments and details. function mkl_lib_check { local arg= local name= # Find config name parameter (first non-option (--...)) for arg in $* ; do if [[ $arg == --* ]]; then continue fi name=$arg break done if [[ $MKL_INSTALL_DEPS != y ]] || ! mkl_dep_has_installer "$name" ; then mkl_lib_check0 "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" return $? fi # Automatic dependency installation mode: # First pass is lib check with cont, # if it fails, attempt dependency installation, # and then make second with caller's fail-action. local retcode= # With --source-deps-only we want to make sure the dependency # being used is in-fact from the dependency builder (if supported), # rather than a system installed alternative, so skip the pre-check and # go directly to dependency installation/build below. if [[ $MKL_SOURCE_DEPS_ONLY != y ]] || ! mkl_dep_has_builder $name ; then mkl_lib_check0 --override-action=cont "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" retcode=$? if [[ $retcode -eq 0 ]]; then # Successful on first pass return $retcode fi else mkl_dbg "$name: skipping dependency pre-check in favour of --source-deps-only" fi # Install dependency if ! mkl_dep_install "$name" ; then return 1 fi # Second pass: check again, this time fail hard mkl_lib_check0 --override-action=fail "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" return $? } # Check for library with pkg-config # Automatically sets CFLAGS and LIBS from pkg-config information. # Arguments: # [--no-static] (do not attempt to link the library statically) # [--libname=] (library name if different from config name, such as # when the libname includes a dash) # config name # define name # action (fail|disable|ignore|cont) # compiler (CC|CXX) # source snippet function mkl_pkg_config_check { local nostaticopt= if [[ $1 == --no-static ]]; then nostaticopt=$1 shift fi local libname=$1 if [[ $1 == --libname* ]]; then libname="${libnameopt#*=}" shift fi local cname="${1}_PKGCONFIG" mkl_check_begin "$cname" "$2" "no-cache" "$1 (by pkg-config)" && return $? local cflags= local cmd="${PKG_CONFIG} --short-errors --cflags $libname" mkl_dbg "pkg-config check $libname for CFLAGS ($2): $cmd" cflags=$($cmd 2>&1) if [[ $? != 0 ]]; then mkl_dbg "'$cmd' failed: $cflags" # Clear define name ($2): caller may have additional checks mkl_check_failed "$cname" "" "$3" "'$cmd' failed: $cflags" return 1 fi if [[ $(mkl_meta_get $1 installed_with) == "source" && \ $WITH_STATIC_LINKING == y && \ $MKL_SOURCE_DEPS_ONLY == y ]]; then # If attempting static linking and we're using source-only # dependencies, then there is no need for pkg-config since # the source installer will have set the required flags. mkl_check_failed "$cname" "" "ignore" "pkg-config ignored for static build" return 1 fi local libs= cmd="${PKG_CONFIG} --short-errors --libs $libname" mkl_dbg "pkg-config check $libname for LIBS ($2): $cmd" libs=$($cmd 2>&1) if [[ $? != 0 ]]; then mkl_dbg "${PKG_CONFIG} --libs $libname failed: $libs" # Clear define name ($2): caller may have additional checks mkl_check_failed "$cname" "" "$3" "pkg-config --libs failed" return 1 fi mkl_dbg "$1: from pkg-config: CFLAGS '$CFLAGS', LIBS '$LIBS'" local snippet="$5" if [[ -n $snippet ]]; then mkl_dbg "$1: performing compile check using pkg-config info" if ! mkl_compile_check --sub "$1" "$2" "no-cache" "$4" "$cflags $libs" "$snippet"; then mkl_check_failed "$cname" "" "$3" "compile check failed" return 1 fi fi mkl_mkvar_append $1 "MKL_PKGCONFIG_REQUIRES_PRIVATE" "$libname" mkl_mkvar_append $1 "CFLAGS" "$cflags" if [[ -z $nostaticopt ]]; then local stlibs=$(mkl_lib_check_static $1 "$libs") if [[ -n $stlibs ]]; then libs=$stlibs else # if we don't find a static library to bundle into the # -static.a, we need to export a pkgconfig dependency # so it can be resolved when linking downstream packages mkl_mkvar_append $1 "MKL_PKGCONFIG_REQUIRES" "$libname" fi fi mkl_dbg "$1: from pkg-config: LIBS: prepend $libs" mkl_mkvar_prepend "$1" LIBS "$libs" mkl_check_done "$1" "$2" "$3" "ok" return 0 } # Check that a command runs and exits succesfully. # Arguments: # config name # define name (optional, can be empty) # action # command function mkl_command_check { mkl_check_begin "$1" "$2" "$3" "$1 (by command)" && return $? local out= out=$($4 2>&1) if [[ $? != 0 ]]; then mkl_dbg "$1: $2: $4 failed: $out" mkl_check_failed "$1" "$2" "$3" "command '$4' failed: $out" return 1 fi mkl_check_done "$1" "$2" "$3" "ok" return 0 } # Check that a program is executable, but will not execute it. # Arguments: # config name # define name (optional, can be empty) # action # program name (e.g, objdump) function mkl_prog_check { mkl_check_begin --verb "checking executable" "$1" "$2" "$3" "$1" && return $? local out= out=$(command -v "$4" 2>&1) if [[ $? != 0 ]]; then mkl_dbg "$1: $2: $4 is not executable: $out" mkl_check_failed "$1" "$2" "$3" "$4 is not executable" return 1 fi mkl_check_done "$1" "$2" "$3" "ok" return 0 } # Checks that the check for the given config name passed. # This does not behave like the other checks, if the given config name passed # its test then nothing is printed. Else the configure will fail. # Arguments: # checked config name function mkl_config_check { local status=$(mkl_var_get "MKL_STATUS_$1") [[ $status = "ok" ]] && return 0 mkl_fail $1 "" "fail" "$MKL_MODULE requires $1" return 1 } # Checks that all provided config names are set. # Arguments: # config name # define name # action # check_config_name1 # check_config_name2.. function mkl_config_check_all { local cname= local res="ok" echo start this now for $1 for cname in ${@:4}; do local st=$(mkl_var_get "MKL_STATUS_$cname") [[ $status = "ok" ]] && continue mkl_fail $1 $2 $3 "depends on $cname" res="failed" done echo "try res $res" mkl_check_done "$1" "$2" "$3" $res } # Check environment variable # Arguments: # config name # define name # action # environment variable function mkl_env_check { mkl_check_begin "$1" "$2" "$3" "$1 (by env $4)" && return $? if [[ -z ${!4} ]]; then mkl_check_failed "$1" "$2" "$3" "environment variable $4 not set" return 1 fi mkl_check_done "$1" "$2" "$3" "ok" "${!4}" return 0 } # Run all checks function mkl_checks_run { # Set up common variables mkl_allvar_set "" MKL_APP_NAME $(mkl_meta_get description name) mkl_allvar_set "" MKL_APP_DESC_ONELINE "$(mkl_meta_get description oneline)" # Call checks functions in dependency order local mf for mf in $MKL_CHECKS ; do MKL_MODULE=${mf%:*} local func=${mf#*:} if mkl_func_exists $func ; then $func else mkl_err "Check function $func from $MKL_MODULE disappeared ($mf)" fi unset MKL_MODULE done } # Check for color support in terminal. # If the terminal supports colors, the function will alter # MKL_RED # MKL_GREEN # MKL_YELLOW # MKL_BLUE # MKL_CLR_RESET function mkl_check_terminal_color_support { local use_color=false local has_tput=false if [[ -z ${TERM} ]]; then # tput and dircolors require $TERM mkl_dbg "\$TERM is not set! Cannot check for color support in terminal." return 1 elif hash tput 2>/dev/null; then has_tput=true [[ $(tput colors 2>/dev/null) -ge 8 ]] && use_color=true mkl_dbg "tput reports color support: ${use_color}" elif hash dircolors 2>/dev/null; then # Enable color support only on colorful terminals. # dircolors --print-database uses its own built-in database # instead of using /etc/DIR_COLORS. Try to use the external file # first to take advantage of user additions. local safe_term=${TERM//[^[:alnum:]]/?} local match_lhs="" [[ -f ~/.dir_colors ]] && match_lhs="${match_lhs}$(<~/.dir_colors)" [[ -f /etc/DIR_COLORS ]] && match_lhs="${match_lhs}$(&1) if [[ $? -ne 0 ]]; then rm -f "$tmpfile" mkl_err "Failed to download $modname:" mkl_err0 $out return 1 fi # Move downloaded file into place replacing the old file. mv "$tmpfile" "$fname" || return 1 # "Return" filename echo "$fname" return 0 } # Load module by name or filename # Arguments: # "require"|"try" # filename # [ module arguments ] function mkl_module_load { local try=$1 shift local fname=$1 shift local modname=${fname#*configure.} local bypath=1 # Check if already loaded if mkl_in_list "$MKL_MODULES" "$modname"; then return 0 fi if [[ $fname = $modname ]]; then # Module specified by name, find the file. bypath=0 for fname in configure.$modname \ ${MKLOVE_DIR}/modules/configure.$modname ; do [[ -s $fname ]] && break done fi # Calling module local cmod=$MKL_MODULE [[ -z $cmod ]] && cmod="base" if [[ ! -s $fname ]]; then # Attempt to download module, if permitted if [[ $MKL_NO_DOWNLOAD != 0 || $bypath == 1 ]]; then mkl_err "Module $modname not found at $fname (required by $cmod) and downloads disabled" if [[ $try = "require" ]]; then mkl_fail "$modname" "none" "fail" \ "Module $modname not found (required by $cmod) and downloads disabled" fi return 1 fi fname=$(mkl_module_download "$modname") if [[ $? -ne 0 ]]; then mkl_err "Module $modname not found (required by $cmod)" if [[ $try = "require" ]]; then mkl_fail "$modname" "none" "fail" \ "Module $modname not found (required by $cmod)" return 1 fi fi # Now downloaded, try loading the module again. mkl_module_load $try "$fname" "$@" return $? fi # Set current module local save_MKL_MODULE=$MKL_MODULE MKL_MODULE=$modname mkl_dbg "Loading module $modname (required by $cmod) from $fname" # Source module file (positional arguments are available to module) source $fname # Restore current module (might be recursive) MKL_MODULE=$save_MKL_MODULE # Add module to list of modules mkl_var_append MKL_MODULES $modname # Rename module's special functions so we can call them separetely later. mkl_func_rename "options" "${modname}_options" mkl_func_rename "install_source" "${modname}_install_source" mkl_func_rename "manual_checks" "${modname}_manual_checks" mkl_func_push MKL_CHECKS "$modname" "checks" mkl_func_push MKL_GENERATORS "$modname" "generate" mkl_func_push MKL_CLEANERS "$modname" "clean" } # Require and load module # Must only be called from module file outside any function. # Arguments: # [ --try ] Dont fail if module doesn't exist # module1 # [ "must" "pass" ] # [ module arguments ... ] function mkl_require { local try="require" if [[ $1 = "--try" ]]; then local try="try" shift fi local mod=$1 shift local override_action= # Check for cyclic dependencies if mkl_in_list "$MKL_LOAD_STACK" "$mod"; then mkl_err "Cyclic dependency detected while loading $mod module:" local cmod= local lmod=$mod for cmod in $MKL_LOAD_STACK ; do mkl_err " $lmod required by $cmod" lmod=$cmod done mkl_fail base "" fail "Cyclic dependency detected while loading module $mod" return 1 fi mkl_var_prepend MKL_LOAD_STACK "$mod" if [[ "$1 $2" == "must pass" ]]; then shift shift override_action="fail" fi if [[ ! -z $override_action ]]; then mkl_meta_set "MOD__$mod" "override_action" "$override_action" fi mkl_module_load $try $mod "$@" local ret=$? mkl_var_shift MKL_LOAD_STACK return $ret } ########################################################################### # # # Usage options # # ########################################################################### MKL_USAGE="Usage: ./configure [OPTIONS...] mklove configure script - mklove, not autoconf Copyright (c) 2014-2023, Magnus Edenhill - https://github.com/edenhill/mklove " function mkl_usage { echo "$MKL_USAGE" local name=$(mkl_meta_get description name) if [[ ! -z ${name} ]]; then echo " $name - $(mkl_meta_get description oneline) $(mkl_meta_get description copyright) " fi local og for og in $MKL_USAGE_GROUPS ; do og="MKL_USAGE_GROUP__$og" echo "${!og}" done echo "Honoured environment variables: CC, CPP, CXX, CFLAGS, CPPFLAGS, CXXFLAGS, LDFLAGS, LIBS, LD, NM, OBJDUMP, STRIP, RANLIB, PKG_CONFIG, PKG_CONFIG_PATH, STATIC_LIB_=.../libname.a " } # Add usage option informative text # Arguments: # text function mkl_usage_info { MKL_USAGE="$MKL_USAGE $1" } # Add option to usage output # Arguments: # option group ("Standard", "Cross-Compilation", etc..) # variable name # option ("--foo", "--foo=*", "--foo=args_required") # help # default (optional) # assignvalue (optional, default:"y") # function block (optional) # # If option takes the form --foo=* then arguments are optional. function mkl_option { local optgroup=$1 local varname=$2 # Fixed width between option name and help in usage output local pad=" " if [[ ${#3} -lt ${#pad} ]]; then pad=${pad:0:$(expr ${#pad} - ${#3})} else pad="" fi # Add to usage output local optgroup_safe=$(mkl_env_esc $optgroup) if ! mkl_in_list "$MKL_USAGE_GROUPS" "$optgroup_safe" ; then mkl_env_append MKL_USAGE_GROUPS "$optgroup_safe" mkl_env_set "MKL_USAGE_GROUP__$optgroup_safe" "$optgroup options: " fi local defstr="" [[ ! -z $5 ]] && defstr=" [$5]" mkl_env_append "MKL_USAGE_GROUP__$optgroup_safe" " $3 $pad $4$defstr " local optname="${3#--}" local safeopt= local optval="" if [[ $3 == *=* ]]; then optname="${optname%=*}" optval="${3#*=}" if [[ $optval == '*' ]]; then # Avoid globbing of --foo=* optional arguments optval='\*' fi fi safeopt=$(mkl_env_esc $optname) mkl_meta_set "MKL_OPT_ARGS" "$safeopt" "$optval" # # Optional variable scoping by prefix: "env:", "mk:", "def:" # local setallvar="mkl_allvar_set ''" local setmkvar="mkl_mkvar_set ''" if [[ $varname = env:* ]]; then # Set environment variable (during configure runtime only) varname=${varname#*:} setallvar=mkl_env_set setmkvar=mkl_env_set elif [[ $varname = mk:* ]]; then # Set Makefile.config variable varname=${varname#*:} setallvar="mkl_mkvar_append ''" setmkvar="mkl_mkvar_append ''" elif [[ $varname = def:* ]]; then # Set config.h define varname=${varname#*:} setallvar="mkl_define_set ''" setmkvar="mkl_define_set ''" fi if [[ ! -z $7 ]]; then # Function block specified. eval "function opt_$safeopt { $7 }" else # Add default implementation of function simply setting the value. # Application may override this by redefining the function after calling # mkl_option. if [[ $optval = "PATH" ]]; then # PATH argument: make it an absolute path. # Only set the make variable (not config.h) eval "function opt_$safeopt { $setmkvar $varname \"\$(mkl_abspath \$(mkl_render \$1))\"; }" else # Standard argument: simply set the value if [[ -z "$6" ]]; then eval "function opt_$safeopt { $setallvar $varname \"\$1\"; }" else eval "function opt_$safeopt { $setallvar $varname \"$6\"; }" fi fi fi # If default value is provided and does not start with "$" (variable ref) # then set it right away. # $ variable refs are set after all checks have run during the # generating step. if [[ ${#5} != 0 ]] ; then if [[ $5 = *\$* ]]; then mkl_var_append "MKL_LATE_VARS" "opt_$safeopt:$5" else opt_$safeopt $5 fi fi if [[ ! -z $varname ]]; then # Add variable to list MKL_CONFVARS="$MKL_CONFVARS $varname" fi } # Adds a toggle (--enable-X, --disable-X) option. # Arguments: # option group ("Standard", ..) # variable name (WITH_FOO) # option (--enable-foo, --enable-foo=*, or --enable-foo=req) # help ("foo.." ("Enable" and "Disable" will be prepended)) # default (y or n) function mkl_toggle_option { # Add option argument mkl_option "$1" "$2" "$3" "$4" "$5" # Add corresponding "--disable-foo" option for "--enable-foo". local disname="${3/--enable/--disable}" local dishelp="${4/Enable/Disable}" mkl_option "$1" "$2" "$disname" "$dishelp" "" "n" } # Adds a toggle (--enable-X, --disable-X) option with builtin checker. # This is the library version. # Arguments: # option group ("Standard", ..) # config name (foo, must be same as pkg-config name) # variable name (WITH_FOO) # action (fail or disable) # option (--enable-foo) # help (defaults to "Enable ") # linker flags (-lfoo) # default (y or n) function mkl_toggle_option_lib { local help="$6" [[ -z "$help" ]] && help="Enable $2" # Add option argument mkl_option "$1" "$3" "$5" "$help" "$8" # Add corresponding "--disable-foo" option for "--enable-foo". local disname="${5/--enable/--disable}" local dishelp="${help/Enable/Disable}" mkl_option "$1" "$3" "$disname" "$dishelp" "" "n" # Create checks eval "function _tmp_func { mkl_lib_check \"$2\" \"$3\" \"$4\" CC \"$7\"; }" mkl_func_push MKL_CHECKS "$MKL_MODULE" _tmp_func } # Downloads, verifies checksum, and extracts an archive to # the current directory. # # Arguments: # url Archive URL # shabits The SHA algorithm bit count used to verify the checksum. E.g., "256". # checksum Expected checksum of archive (use "" to not perform check) function mkl_download_archive { local url="$1" local shabits="$2" local exp_checksum="$3" local tmpfile=$(mktemp _mkltmpXXXXXX) # Try both wget and curl if ! wget -nv -O "$tmpfile" "$url" ; then if ! curl -fLsS -o "$tmpfile" "$url" ; then rm -f "$tmpfile" echo -e "ERROR: Download of $url failed" 1>&2 return 1 fi fi if [[ -n $exp_checksum ]]; then # Verify checksum local checksum_tool="" # OSX has shasum by default, on Linux it is typically in # some Perl package that may or may not be installed. if $(which shasum >/dev/null 2>&1); then checksum_tool="shasum -b -a ${shabits}" else # shaXsum is available in Linux coreutils checksum_tool="sha${shabits}sum" fi local checksum=$($checksum_tool "$tmpfile" | cut -d' ' -f1) if [[ $? -ne 0 ]]; then rm -f "$tmpfile" echo "ERROR: Failed to verify checksum of $url with $checksum_tool" 1>&2 return 1 fi if [[ $checksum != $exp_checksum ]]; then rm -f "$tmpfile" echo "ERROR: $url: $checksum_tool: Checksum mismatch: expected $exp_checksum, calculated $checksum" 1>&2 return 1 fi echo "### Checksum of $url verified ($checksum_tool):" echo "### Expected: $exp_checksum" echo "### Calculated: $checksum" fi tar xzf "$tmpfile" --strip-components 1 if [[ $? -ne 0 ]]; then rm -f "$tmpfile" echo "ERROR: $url: failed to extract archive" 1>&2 return 1 fi rm -f "$tmpfile" return 0 }