#!/bin/sh # A script-only build utility like autotools # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http:##www.apache.org#licenses#LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Copyright (C) 2022-present, Xmake Open Source Community. # # @author ruki # @homepage https://github.com/xmake-io/xmake.sh # #----------------------------------------------------------------------------- # some constants # xmake_sh_projectdir=$(X= cd -- "$(dirname -- "$0")" && pwd -P) xmake_sh_builddir="build" xmake_sh_version="1.0.5" xmake_sh_verbose=false xmake_sh_diagnosis=false xmake_sh_copyright="Copyright (C) 2022-present Ruki Wang, https://xmake.io." xmake_sh_makefile="${xmake_sh_projectdir}/Makefile" xmake_sh_ninjafile="${xmake_sh_projectdir}/build.ninja" #----------------------------------------------------------------------------- # some helper functions # raise() { echo "$@" 1>&2 ; exit 1 } vprint() { if "${xmake_sh_verbose}"; then echo "$@" fi } dprint() { if "${xmake_sh_diagnosis}"; then echo "$@" fi } # show and escape string instead of `echo -e`, because sh does not support it print() { printf "${@}\n" } wprint() { if "${xmake_sh_verbose}"; then printf "warning: ${@}\n" fi } # test empty string test_z() { if test "x${1}" = "x"; then return 0 fi return 1 } # test non-empty string test_nz() { if test "x${1}" != "x"; then return 0 fi return 1 } # test string is equal test_eq() { if test "x${1}" = "x${2}"; then return 0 fi return 1 } # test string is not equal test_nq() { if test "x${1}" != "x${2}"; then return 0 fi return 1 } string_toupper() { _ret=$(echo "$1" | tr '[a-z]' '[A-Z]') } string_tolower() { _ret=$(echo "$1" | tr '[A-Z]' '[a-z]') } string_replace() { local src="${1}" local search="${2}" local replace="${3}" if test_z "${search}"; then _ret="${src}" return fi local rest="${src}" local result="" local prefix="" while :; do case "${rest}" in *"${search}"*) prefix="${rest%%"${search}"*}" result="${result}${prefix}${replace}" rest="${rest#*"${search}"}" ;; *) result="${result}${rest}" break ;; esac done _ret="${result}" } # escape value for writing into ninja files _ninja_escape() { local rest="${1}" case "${rest}" in *\$*) local result="" while :; do case "${rest}" in *\$*) result="${result}${rest%%\$*}\$\$" rest="${rest#*\$}" ;; *) result="${result}${rest}" break ;; esac done _ret="${result}" ;; *) _ret="${rest}" ;; esac } _shell_escape_single_quotes() { local value="${1}" case "${value}" in *"'"*) local escaped="" while :; do case "${value}" in *"'"*) escaped="${escaped}${value%%\'*}'\"'\"'" value="${value#*\'}" ;; *) escaped="${escaped}${value}" break ;; esac done _ret="'${escaped}'" ;; *) _ret="'${value}'" ;; esac } # we avoid use `cut` command, because it's slow string_split() { local str="${1}" local sep="${2}" local idx="${3}" local oldifs="${IFS}" IFS="${sep}" set -- ${str} if test_nz "${idx}"; then case "${idx}" in 1) _ret="$1";; 2) _ret="$2";; 3) _ret="$3";; 4) _ret="$4";; 5) _ret="$5";; 6) _ret="$6";; esac else _ret="$1" _ret2="$2" _ret3="$3" _ret4="$4" _ret5="$5" _ret6="$6" fi IFS="${oldifs}" } # does contain sub-string? # e.g. # str="src/*.cpp" # string_contains "$str" "src" string_contains() { case "${1}" in *${2}*) return 0;; *) return 1;; esac return 1 } # does contain "*"? string_contains_star() { case "${1}" in *\**) return 0;; # bash *'*'*) return 0;; # csh *) return 1;; esac return 1 } # does contain "**"? string_contains_star2() { case "${1}" in *\*\**) return 0;; # bash *'**'*) return 0;; # csh *) return 1;; esac return 1 } # does startswith sub-string? # e.g. # str="src/*.cpp" # string_startswith "$str" "src" string_startswith() { case "${1}" in ${2}*) return 0;; *) return 1;; esac return 1 } # duplicate characters # e.g. string_dupch 10 "." => ........... string_dupch() { local count=${1} local ch=${2} local result="" while [ "${count:-0}" -gt 0 ]; do result="${result}${ch}" count=$((count - 1)) done printf '%s' "${result}" } # replace file content _io_replace_file() { local infile="${1}" local outfile="${2}" local patterns="${3}" sed "/./ {${patterns}}" "${infile}" > "${outfile}" } # try remove file or directory _os_tryrm() { if test -f "${1}"; then rm "${1}" elif test -d "${1}"; then rm -r "${1}" fi } # get temporary file # https://github.com/xmake-io/xmake/issues/5464 _os_tmpfile() { _ret=$(mktemp "${TMPDIR-/tmp}/tmp.XXXXXXXX") } # try run program _os_runv() { if ${xmake_sh_diagnosis}; then ${@} else ${@} >/dev/null 2>&1 fi local ok=$? if test "${ok}" -ne "0"; then return 1 fi return 0 } # try run program and get output _os_iorunv() { _os_tmpfile local tmpfile="${_ret}" ${@} >"${tmpfile}" 2>&1 local ok=$? if test "${ok}" -ne "0"; then _ret="" else local result=$(cat "${tmpfile}") _ret="${result}" fi _os_tryrm "${tmpfile}" } # find file in the given directory # e.g. _os_find . xmake.sh [depth] _os_find() { local dir="${1}" local name="${2}" local depth="${3}" if test_nz "${depth}"; then # Solaris doesn't support -maxdepth, use shell to filter depth if is_host "solaris"; then _ret="" local file="" for file in $(find "${dir}" -type f -name "${name}" | LC_ALL=C sort); do local relpath="${file#${dir}/}" local slash_count=$(echo "${relpath}" | tr -cd '/' | wc -c | tr -d ' ') if test "${slash_count}" = "$((${depth} - 1))"; then if test -z "${_ret}"; then _ret="${file}" else _ret="${_ret} ${file}" fi fi done else _ret=$(find "${dir}" -maxdepth "${depth}" -mindepth "${depth}" -type f -name "${name}" | LC_ALL=C sort) fi else _ret=$(find "${dir}" -type f -name "${name}" | LC_ALL=C sort) fi } # get date, "%Y%m%d%H%M" -> 202212072222 # Use deterministic timestamp from SOURCE_DATE_EPOCH if available # https://reproducible-builds.org/docs/source-date-epoch/ _os_date() { if test_z "${SOURCE_DATE_EPOCH}"; then _ret=$(date +"${1}") else # Use GNU date options first, then fallback to BSD's, and finally fallback to current time. _ret=$(date -u -d "@$SOURCE_DATE_EPOCH" +"${1}" 2>/dev/null || date -u -r "$SOURCE_DATE_EPOCH" +"${1}" 2>/dev/null || date +"${1}") fi } # we avoid use `basename`, because it's slow path_filename() { local path="${1}" if test_eq "${path}" "/"; then _ret="/" else _ret="${path##*/}" fi } path_extension() { path_filename "${1}"; local filename="${_ret}" _ret=".${filename##*.}" } path_basename() { path_filename "${1}"; local filename="${_ret}" _ret="${filename%.*}" } # we avoid use `dirname -- ${1}`, because it's too slow path_directory() { local path="${1}" if test_z "${path}"; then raise "invalid empty path in path_directory()." fi path="${path%/}" local dir="${path%/*}" if string_startswith "${path}" "/"; then if test_z "${dir}"; then dir="/" fi else dir="${dir#/}" if test_z "${dir}"; then dir="." fi fi _ret="${dir}" } # e.g. path_filename_fromdir "/tmp/file" "/tmp" -> "file" path_filename_fromdir() { _ret="${1#${2}/}" } path_is_absolute() { if string_startswith "${1}" "/"; then return 0 fi return 1 } # get relative path, e.g $(path_relative ${rootdir} ${absolute_path}` path_relative() { local source="${1}" local target="${2}" if test_z "${source}" || test_z "${target}"; then raise "invalid empty path in path_relative()" fi # patch missing "./" source=${source#./} source=${source#.} target=${target#./} target=${target#.} if test_z "${source}"; then _ret="${target}" return fi # find common path local result="" local common_part=$source while test_eq "${target#$common_part}" "${target}"; do # no match, means that candidate common part is not correct # go up one level (reduce common part) path_directory "${common_part}"; common_part="${_ret}" # and record that we went back, with correct / handling if test_z "${result}"; then result=".." else result="../${result}" fi done if test_eq "${common_part}" "/"; then # special case for root (no common path) result="${result}/" fi # since we now have identified the common part, # compute the non-common part local forward_part="${target#$common_part}" # and now stick all parts together if test_nz "${result}" && test_nz "${forward_part}"; then result="${result}${forward_part}" elif test_nz "${forward_part}"; then result="${forward_part#*/}" fi # same directory? if test_z "${result}" && test_eq "${source}" "${target}"; then result="." fi _ret="${result}" } path_sourcekind() { local sourcekind="" case "${1}" in *.cpp) sourcekind="cxx";; *.cc) sourcekind="cxx";; *.c) sourcekind="cc";; *.ixx) sourcekind="cxx";; *.mm) sourcekind="mxx";; *.m) sourcekind="mm";; *.S) sourcekind="as";; *.s) sourcekind="as";; *.asm) sourcekind="as";; *) raise "unknown sourcekind for ${1}" ;; esac _ret="${sourcekind}" } path_toolname() { local toolname="" case "${1}" in *-gcc) toolname="gcc";; */gcc) toolname="gcc";; gcc) toolname="gcc";; gcc-*) toolname="gcc";; */gcc-*) toolname="gcc";; *-g++) toolname="gxx";; */g++) toolname="gxx";; g++) toolname="gxx";; g++-*) toolname="gxx";; */g++-*) toolname="gxx";; xcrun*clang++) toolname="clangxx";; xcrun*clang) toolname="clang";; *-clang++) toolname="clangxx";; */clang++) toolname="clangxx";; clang++) toolname="clangxx";; clang++-*) toolname="clangxx";; */clang++-*) toolname="clangxx";; *-clang) toolname="clang";; */clang) toolname="clang";; clang) toolname="clang";; clang-*) toolname="clang";; */clang-*) toolname="clang";; */emcc) toolname="emcc";; emcc) toolname="emcc";; */em++) toolname="emxx";; em++) toolname="emxx";; */cosmocc) toolname="cosmocc";; cosmocc) toolname="cosmocc";; */cosmoc++) toolname="cosmocxx";; cosmoc++) toolname="cosmocxx";; *-ar) toolname="ar";; */ar) toolname="ar";; ar) toolname="ar";; */emar) toolname="emar";; emar) toolname="emar";; */cosmoar) toolname="cosmoar";; cosmoar) toolname="cosmoar";; cc) toolname="gcc";; */cc) toolname="gcc";; c++) toolname="gxx";; */c++) toolname="gxx";; tcc) toolname="tcc";; */tcc) toolname="tcc";; *) raise "unknown tool ${1}";; esac _ret="${toolname}" } # get flag name from toolkind, e.g. cc => cflags, cxx => cxxflags _get_flagname() { local toolkind="${1}" local flagname="" case "${toolkind}" in cc) flagname="cflags";; cxx) flagname="cxxflags";; as) flagname="asflags";; mm) flagname="mflags";; mxx) flagname="mxxflags";; ar) flagname="arflags";; sh) flagname="shflags";; ld) flagname="ldflags";; *) raise "unknown toolkind(${toolkind})!" ;; esac _ret="${flagname}" } # is enabled? true, yes, y _is_enabled() { local value=${1} if test_eq "${value}" "true"; then return 0 elif test_eq "${value}" "yes"; then return 0 elif test_eq "${value}" "y"; then return 0 fi return 1 } # deduplicate string list # .e.g "hello world hello how are you world" -> hello world how are you _dedup() { _ret=$(echo "${1}" | awk '{for (i = 1; i <= NF; ++i) if (!seen[$i]++) printf $i " "}') } # deduplicate string list from the reverse order # .e.g "hello world hello how are you world" -> hello how are you world _dedup_reverse() { local result="" local list="" local item="" list=$(echo "${1}" | awk '{for (i = NF; i > 0; --i) if (!seen[$i]++) printf $i " "}') for item in ${list}; do result="${item} ${result}" done _ret="${result}" } #----------------------------------------------------------------------------- # map functions # # define map, @note we can not use bash/declare to define map, because sh does not support it. # # _map "options" # _map_set "options" "key1" "value1" # _map_set "options" "key2" "value2" # _map_set "options" "key2" "value3" # _map_set "options" "key3" "value3" # _map_set "options" "key4" "__empty__" # _map_set "options" "key4" "__empty__" # _map_count "options"; _count="${_ret}" # _map_keys "options"; _keys="${_ret}" # echo ${_count} # for key in ${_keys}; do # _map_get "options" ${key}; value="{_ret}" # echo ${key} "->" ${value} # done # # echo "------" # _map_remove "options" "key3" # _map_count "options"; _count="${_ret}" # _map_keys "options"; _keys="${_ret}" # echo ${_count} # for key in ${_keys}; do # _map_get "options" ${key}; value="{_ret}" # echo ${key} "->" ${value} # done # _map() { local name=${1} # eval _map_${name}_count=0 # eval _map_${name}_keys="" } # because the shell is slow, we have to temporarily # disable some of the map features for performance. # #_map_count() { # local name=${1} # local count=$(eval echo \$_map_${name}_count) # _ret="${count}" #} _map_get() { local name="${1}" local key="${2}" _ret=$(eval echo \$_map_${name}_value_${key}) if test_eq "${_ret}" "__empty__"; then _ret="" fi } _map_has() { local name="${1}" local key="${2}" local value="" value=$(eval echo \$_map_${name}_value_${key}) if test_nz "${value}"; then return 0 fi return 1 } _map_set() { local name="${1}" local key="${2}" local value="${3}" # if ! _map_has ${name} ${key}; then # _map_count "options"; local count="${_ret}" # eval _map_${name}_count=$((${count} + 1)) # local keys=$(eval echo \$_map_${name}_keys) # keys="${keys} ${key}" # eval _map_${name}_keys=\${keys} # fi eval _map_${name}_value_${key}=\${value} } #_map_remove() { # local name="${1}" # local key="${2}" # if _map_has ${name} ${key}; then # _map_count "options"; local count="${_ret}" # eval _map_${name}_count=$((${count} - 1)) # eval _map_${name}_value_${key}="" # local keys=$(eval echo \$_map_${name}_keys) # local keys_new="" # local k="" # for k in ${keys}; do # if test_nq "${k}" "${key}"; then # keys_new="${keys_new} ${k}" # fi # done # eval _map_${name}_keys=\${keys_new} # fi #} #_map_keys() { # local name="${1}" # local keys=$(eval echo \$_map_${name}_keys) # _ret="${keys}" #} #----------------------------------------------------------------------------- # detect default environments # # detect hosts os_host=`uname` string_tolower ${os_host}; os_host="${_ret}" case "${os_host}" in *cygwin*) os_host="cygwin" ;; *mingw*|*msys*) os_host="msys" ;; *darwin*) os_host="macosx" ;; *linux*) os_host="linux" ;; *freebsd*) os_host="freebsd" ;; *netbsd*) os_host="netbsd" ;; *openbsd*) os_host="openbsd" ;; *dragonfly*) os_host="dragonflybsd" ;; *bsd*) os_host="bsd" ;; *sunos*) os_host="solaris" ;; *haiku*) os_host="haiku" ;; esac # determining host # e.g. # if is_host "linux" "macosx"; then # ... # fi is_host() { local host="" for host in $@; do if test_eq "${os_host}" "${host}"; then return 0 fi done return 1 } # detect host architecture os_arch=`uname -m | tr '[A-Z]' '[a-z]'` if test_eq "${os_arch}" "i686" || test_eq "${os_arch}" "i86pc"; then os_arch="i386" elif test_eq "${os_arch}" "amd64"; then os_arch="x86_64" elif test_eq "${os_arch}" "aarch64" || test_eq "${os_arch}" "arm64"; then os_arch="arm64" elif string_contains "${os_arch}" "armv7"; then os_arch="armv7" elif string_contains "${os_arch}" "arm"; then os_arch="arm" elif string_contains "${os_arch}" "power macintosh"; then os_arch="ppc" fi # set the default target platform _target_plat_default=${os_host} if is_host "msys"; then _target_plat_default="mingw" elif is_host "freebsd" "openbsd" "dragonflybsd" "netbsd"; then _target_plat_default="bsd" elif test_nz "${EMSDK}"; then _target_plat_default="wasm" fi # set the default target architecture _target_arch_default=${os_arch} if is_host "msys" && test_nz "${MSYSTEM_CARCH}"; then _target_arch_default="${MSYSTEM_CARCH}" elif test_nz "${TERMUX_ARCH}"; then _target_arch_default="${TERMUX_ARCH}" elif test_nz "${EMSDK}"; then _target_arch_default="wasm32" fi if test_eq "${_target_arch_default}" "i686"; then _target_arch_default="i386" elif test_eq "${_target_arch_default}" "aarch64" || test_eq "${_target_arch_default}" "arm64"; then _target_arch_default="arm64" elif string_contains "${_target_arch_default}" "armv7"; then _target_arch_default="armv7" elif string_contains "${_target_arch_default}" "arm"; then _target_arch_default="arm" fi # set the default target mode _target_mode_default="release" # set the default target kind _target_kind_default="static" # set the default project generator and build program if is_host "freebsd" "netbsd" "openbsd" "dragonflybsd" "bsd" "solaris"; then project_generator="gmake" _make_program_default="gmake" _ninja_program_default="ninja" elif is_host "msys" "cygwin"; then project_generator="gmake" _make_program_default="make.exe" _ninja_program_default="ninja.exe" else project_generator="gmake" _make_program_default="make" _ninja_program_default="ninja" fi # set the default directories if test -d "/usr/local"; then _install_prefix_default="/usr/local" elif test -d "/usr"; then _install_prefix_default="/usr" fi _install_bindir_default="\${prefix}/bin" _install_libdir_default="\${prefix}/lib" _install_includedir_default="\${prefix}/include" # determining target platform # e.g. # if is_plat "linux" "macosx"; then # ... # fi is_plat() { local plat="" for plat in $@; do if test_eq "${_target_plat}" "${plat}"; then return 0 fi done return 1 } # determining target architecture # e.g. # if is_arch "x86_64" "i386"; then # ... # fi is_arch() { local arch="" for arch in $@; do if test_eq "${_target_arch}" "${arch}"; then return 0 fi done return 1 } # determining target mode # e.g. # if is_mode "release"; then # ... # fi is_mode() { local mode="" for mode in $@; do if test_eq "${_target_mode}" "${mode}"; then return 0 fi done return 1 } # determining target kind # e.g. # if is_kind "release"; then # ... # fi is_kind() { local kind="" for kind in $@; do if test_eq "${_target_kind}" "${kind}"; then return 0 fi done return 1 } # determining target toolchain # e.g. # if is_toolchain "clang"; then # ... # fi is_toolchain() { local toolchain="" for toolchain in $@; do if test_eq "${_target_toolchain}" "${toolchain}"; then return 0 fi done return 1 } #----------------------------------------------------------------------------- # project configuration apis # # set project name set_project() { _xmake_sh_project_name="${1}" } # include the given xmake.sh file or directory # e.g. includes "src" "tests" includes() { local path="" for path in $@; do if test -f "${path}"; then path_directory "${path}"; xmake_sh_scriptdir="${_ret}" . "${path}" else local xmake_sh_scriptdir_cur=${xmake_sh_scriptdir} if test "x${xmake_sh_scriptdir}" != "x"; then xmake_sh_scriptdir="${xmake_sh_scriptdir_cur}/${path}" . "${xmake_sh_scriptdir}/xmake.sh" else . "${xmake_sh_projectdir}/${path}/xmake.sh" fi xmake_sh_scriptdir=${xmake_sh_scriptdir_cur} fi done } #----------------------------------------------------------------------------- # some helper functions # # split flags _split_flags() { string_replace "${1}" ":" " " } # get abstract flag for gcc/clang _get_abstract_flag_for_gcc_clang() { local toolkind="${1}" local toolname="${2}" local itemname="${3}" local value="${4}" local flag="" case "${itemname}" in defines) string_replace "${value}" '"' '\"'; value="${_ret}" flag="-D${value}" ;; undefines) flag="-U${value}";; includedirs) flag="-I${value}";; linkdirs) flag="-L${value}";; links) flag="-l${value}";; syslinks) flag="-l${value}";; frameworks) flag="-framework ${value}";; frameworkdirs) flag="-F${value}";; rpathdirs) if is_plat "macosx"; then string_replace "${value}" "\$ORIGIN" "@loader_path"; value="${_ret}" flag="-Xlinker -rpath -Xlinker ${value}" else # escape $ORIGIN in makefile, TODO we need also handle it for ninja string_replace "${value}" "@loader_path" '$$ORIGIN'; value="${_ret}" if is_plat "bsd"; then flag="-Wl,-zorigin -Wl,-rpath='${value}'" else flag="-Wl,-rpath='${value}'" fi fi ;; symbols) if test_eq "${value}" "debug"; then flag="-g" elif test_eq "${value}" "hidden"; then flag="-fvisibility=hidden" fi ;; strip) if test_eq "${value}" "debug"; then flag="-Wl,-S" elif test_eq "${value}" "all"; then if is_plat "macosx"; then flag="-Wl,-x -Wl,-dead_strip" else flag="-s" fi fi ;; warnings) if test_eq "${value}" "all" || test_eq "${value}" "more" || test_eq "${value}" "less"; then flag="-Wall" elif test_eq "${value}" "allextra"; then flag="-Wall -Wextra" elif test_eq "${value}" "error"; then flag="-Werror" elif test_eq "${value}" "everything"; then flag="-Wall -Wextra" elif test_eq "${value}" "none"; then flag="-w" fi ;; optimizes) if test_eq "${value}" "fast"; then flag="-O1" elif test_eq "${value}" "faster"; then flag="-O2" elif test_eq "${value}" "fastest"; then flag="-O3" elif test_eq "${value}" "smallest"; then if test_eq "${toolname}" "clang" || test_eq "${toolname}" "clangxx"; then flag="-Oz" else flag="-Os" fi elif test_eq "${value}" "aggressive"; then flag="-Ofast" elif test_eq "${value}" "none"; then flag="-O0" fi ;; languages) if test_eq "${toolkind}" "cc" || test_eq "${toolkind}" "mm"; then case "${value}" in ansi) flag="-ansi";; c89) flag="-std=c89";; gnu89) flag="-std=gnu89";; c99) flag="-std=c99";; gnu99) flag="-std=gnu99";; c11) flag="-std=c11";; gnu11) flag="-std=gnu11";; c17) flag="-std=c17";; gnu17) flag="-std=gnu17";; esac elif test_eq "${toolkind}" "cxx" || test_eq "${toolkind}" "mxx"; then case "${value}" in cxx98) flag="-std=c++98";; c++98) flag="-std=c++98";; gnuxx98) flag="-std=gnu++98";; gnu++98) flag="-std=gnu++98";; cxx11) flag="-std=c++11";; c++11) flag="-std=c++11";; gnuxx11) flag="-std=gnu++11";; gnu++11) flag="-std=gnu++11";; cxx14) flag="-std=c++14";; c++14) flag="-std=c++14";; gnuxx14) flag="-std=gnu++14";; gnu++14) flag="-std=gnu++14";; cxx17) flag="-std=c++17";; c++17) flag="-std=c++17";; gnuxx17) flag="-std=gnu++17";; gnu++17) flag="-std=gnu++17";; cxx1z) flag="-std=c++1z";; c++1z) flag="-std=c++1z";; gnuxx1z) flag="-std=gnu++1z";; gnu++1z) flag="-std=gnu++1z";; cxx2a) flag="-std=c++2a";; c++2a) flag="-std=c++2a";; gnuxx2a) flag="-std=gnu++2a";; gnu++2a) flag="-std=gnu++2a";; cxx20) flag="-std=c++20";; c++20) flag="-std=c++20";; gnuxx20) flag="-std=gnu++20";; gnu++20) flag="-std=gnu++20";; cxx*) raise "unknown language value(${value})!" ;; c++*) raise "unknown language value(${value})!" ;; esac fi ;; *) raise "unknown itemname(${itemname})!" ;; esac _ret="${flag}" } # get abstract flags _get_abstract_flags() { local toolkind="${1}" local toolname="${2}" local itemname="${3}" local values="${4}" local flags="" local value="" for value in ${values}; do local flag="" case "${toolname}" in gcc) _get_abstract_flag_for_gcc_clang "${toolkind}" "${toolname}" "${itemname}" "${value}"; flag="${_ret}";; gxx) _get_abstract_flag_for_gcc_clang "${toolkind}" "${toolname}" "${itemname}" "${value}"; flag="${_ret}";; clang) _get_abstract_flag_for_gcc_clang "${toolkind}" "${toolname}" "${itemname}" "${value}"; flag="${_ret}";; clangxx) _get_abstract_flag_for_gcc_clang "${toolkind}" "${toolname}" "${itemname}" "${value}"; flag="${_ret}";; emcc) _get_abstract_flag_for_gcc_clang "${toolkind}" "${toolname}" "${itemname}" "${value}"; flag="${_ret}";; emxx) _get_abstract_flag_for_gcc_clang "${toolkind}" "${toolname}" "${itemname}" "${value}"; flag="${_ret}";; cosmocc) _get_abstract_flag_for_gcc_clang "${toolkind}" "${toolname}" "${itemname}" "${value}"; flag="${_ret}";; cosmocxx) _get_abstract_flag_for_gcc_clang "${toolkind}" "${toolname}" "${itemname}" "${value}"; flag="${_ret}";; tcc) _get_abstract_flag_for_gcc_clang "${toolkind}" "${toolname}" "${itemname}" "${value}"; flag="${_ret}";; *) raise "unknown toolname(${toolname})!" ;; esac if test_nz "${flag}"; then flags="${flags} ${flag}" fi done _ret="${flags}" } #----------------------------------------------------------------------------- # option configuration apis # # define option option() { local name="${1}" local description="${2}" local default=${3} _xmake_sh_option_current="${name}" if ! ${_loading_options}; then if test_nz "${description}"; then _xmake_sh_option_current="" fi return fi if ! _map_has "options" "${name}_name"; then _xmake_sh_options="${_xmake_sh_options} ${name}" fi _map_set "options" "${name}_name" "${name}" _map_set "options" "${name}_description" "${description}" _map_set "options" "${name}_default" "${default}" # we end option if it's just one line if test_nz "${description}"; then _xmake_sh_option_current="" fi return 0 } option_end() { _xmake_sh_option_current="" } _map "options" # has the given option? _has_option() { local name=${1} if _map_has "options" "${name}_name"; then return 0 fi return 1 } # get the given option item _get_option_item() { local name=${1} local key=${2} _map_get "options" "${name}_${key}" } # set the given option item _set_option_item() { local name=${1} local key=${2} shift shift if test_nz "${name}"; then _map_set "options" "${name}_${key}" "${@}" else raise "please call set_${key}(${@}) in the option scope!" fi } # add values to the given option item _add_option_item() { local name=${1} local key=${2} shift shift if test_nz "${name}"; then _map_get "options" "${name}_${key}"; local values="${_ret}" values="${values} ${@}" _map_set "options" "${name}_${key}" "${values}" else raise "please call add_${key}(${@}) in the option scope!" fi } # get the give option value _get_option_value() { local name=${1} _get_option_item "${name}" "value" if test "x${_ret}" = "x"; then _get_option_item "${name}" "default" fi } # set the give option value _set_option_value() { local name=${1} local value=${2} _set_option_item "${name}" "value" "${value}" } # this option need checking? _option_need_checking() { local name="${1}" _get_option_item "${name}" "default"; local default="${_ret}" if test_nz "${default}"; then return 1 fi _get_option_item "${name}" "cfuncs"; local cfuncs="${_ret}" _get_option_item "${name}" "cxxfuncs"; local cxxfuncs="${_ret}" _get_option_item "${name}" "cincludes"; local cincludes="${_ret}" _get_option_item "${name}" "cxxincludes"; local cxxincludes="${_ret}" _get_option_item "${name}" "ctypes"; local ctypes="${_ret}" _get_option_item "${name}" "cxxtypes"; local cxxtypes="${_ret}" _get_option_item "${name}" "csnippets"; local csnippets="${_ret}" _get_option_item "${name}" "cxxsnippets"; local cxxsnippets="${_ret}" _get_option_item "${name}" "links"; local links="${_ret}" _get_option_item "${name}" "syslinks"; local syslinks="${_ret}" if test_nz "${cfuncs}" || test_nz "${cxxfuncs}" || test_nz "${cincludes}" || test_nz "${cxxincludes}" || test_nz "${ctypes}" || test_nz "${cxxtypes}" || test_nz "${csnippets}" || test_nz "${cxxsnippets}" || test_nz "${links}" || test_nz "${syslinks}"; then return 0 fi return 1 } # get options for the help menu _get_options_for_menu() { local options="" local name="" for name in ${_xmake_sh_options}; do _get_option_item "${name}" "showmenu"; local showmenu="${_ret}" if _is_enabled "${showmenu}"; then options="${options} ${name}" elif test_z "${showmenu}" && ! _option_need_checking "${name}"; then options="${options} ${name}" fi done _ret="${options}" } # get options for checking _get_options_for_checking() { local options="" local name="" for name in ${_xmake_sh_options}; do _get_option_item "${name}" "showmenu"; local showmenu="${_ret}" if test_z "${showmenu}" && _option_need_checking "${name}"; then options="${options} ${name}" fi done _ret="${options}" } # get abstract flags in option _get_option_abstract_flags() { local name="${1}" local toolkind="${2}" local toolname="${3}" local itemname="${4}" local values="${5}" if test_z "${values}"; then _get_option_item "${name}" "${itemname}"; values="${_ret}" fi _get_abstract_flags "${toolkind}" "${toolname}" "${itemname}" "${values}" } # is config for option is_config() { if ! ${_loading_targets}; then return 1 fi local name=${1} local value=${2} _get_option_value "${name}"; local value_cur="${_ret}" if test_eq "${value_cur}" "${value}"; then return 0 fi return 1 } # has config for option has_config() { if ! ${_loading_targets}; then return 1 fi local name=${1} _get_option_value "${name}"; local value_cur="${_ret}" if _is_enabled ${value_cur}; then return 0 fi return 1 } # set config for option, we can use it to modify option status when loading targets set_config() { local name=${1} local value=${2} _set_option_value "${name}" "${value}" } # set showmenu in option set_showmenu() { if ! ${_loading_options}; then return fi local show="${1}" _set_option_item "${_xmake_sh_option_current}" "showmenu" "${show}" } # set description in option set_description() { if ! ${_loading_options}; then return fi local description="${1}" _set_option_item "${_xmake_sh_option_current}" "description" "${description}" } # add cfuncs in option add_cfuncs() { if ! ${_loading_options}; then return fi _add_option_item "${_xmake_sh_option_current}" "cfuncs" "${@}" } # add cxxfuncs in option add_cxxfuncs() { if ! ${_loading_options}; then return fi _add_option_item "${_xmake_sh_option_current}" "cxxfuncs" "${@}" } # add cincludes in option add_cincludes() { if ! ${_loading_options}; then return fi _add_option_item "${_xmake_sh_option_current}" "cincludes" "${@}" } # add cxxincludes in option add_cxxincludes() { if ! ${_loading_options}; then return fi _add_option_item "${_xmake_sh_option_current}" "cxxincludes" "${@}" } # add ctypes in option add_ctypes() { if ! ${_loading_options}; then return fi _add_option_item "${_xmake_sh_option_current}" "ctypes" "${@}" } # add cxxtypes in option add_cxxtypes() { if ! ${_loading_options}; then return fi _add_option_item "${_xmake_sh_option_current}" "cxxtypes" "${@}" } # add csnippets in option add_csnippets() { if ! ${_loading_options}; then return fi local csnippets="${1}" _add_option_item "${_xmake_sh_option_current}" "csnippets" "${csnippets}" } # add cxxsnippets in option add_cxxsnippets() { if ! ${_loading_options}; then return fi local cxxsnippets="${1}" _add_option_item "${_xmake_sh_option_current}" "cxxsnippets" "${cxxsnippets}" } # before_check in option before_check() { if ! ${_loading_options}; then return fi local funcname="${1}" _add_option_item "${_xmake_sh_option_current}" "before_check" "${funcname}" } #----------------------------------------------------------------------------- # target configuration apis # # define target target() { local name="${1}" _xmake_sh_target_current="${name}" if ! ${_loading_targets}; then return fi if ! _map_has "targets" "${name}_name"; then _xmake_sh_targets="${_xmake_sh_targets} ${name}" fi _map_set "targets" "${name}_name" "${name}" return 0 } target_end() { _xmake_sh_target_current="" } _map "targets" # has the given target? _has_target() { local name=${1} if _map_has "targets" "${name}_name"; then return 0 fi return 1 } # has the given target item _has_target_item() { local name=${1} local key=${2} if _map_has "targets" "${name}_${key}"; then return 0 elif _map_has "targets" "__root_${key}"; then return 0 fi return 1 } # get the given target item _get_target_item() { local name=${1} local key=${2} _map_get "targets" "${name}_${key}" local values="${_ret}" if _map_has "targets" "__root_${key}"; then _map_get "targets" "__root_${key}"; local root_values="${_ret}" if test_nz "${values}"; then values="${root_values} ${values}" else values="${root_values}" fi fi _ret="${values}" } # set the given target item _set_target_item() { local name=${1} local key=${2} shift shift if test_nz "${name}"; then _map_set "targets" "${name}_${key}" "${@}" else _map_set "targets" "__root_${key}" "${@}" fi } # add values to the given target item _add_target_item() { local name=${1} local key=${2} shift shift if test_nz "${name}"; then _map_get "targets" "${name}_${key}"; local values="${_ret}" values="${values} ${@}" _map_set "targets" "${name}_${key}" "${values}" else _map_get "targets" "__root_${key}"; local values="${_ret}" values="${values} ${@}" _map_set "targets" "__root_${key}" "${values}" fi } # is default? _is_target_default() { local name="${1}" if _has_target_item "${name}" "default"; then _get_target_item "${target}" "default"; local default="${_ret}" if _is_enabled ${default}; then return 0 fi return 1 fi return 0 } # get target basename _get_target_basename() { local name="${1}" _get_target_item "${name}" "basename"; local basename="${_ret}" if test_z "${basename}"; then basename="${name}" fi _ret="${basename}" } # get target extension _get_target_extension() { local name="${1}" local extension="" if _has_target_item "${name}" "extension"; then _get_target_item "${name}" "extension"; extension="${_ret}" elif is_plat "mingw"; then _get_target_item "${name}" "kind"; local kind="${_ret}" if test "x${kind}" = "xbinary"; then extension=".exe" elif test "x${kind}" = "xstatic"; then extension=".a" elif test "x${kind}" = "xshared"; then extension=".dll" fi else _get_target_item "${name}" "kind"; local kind="${_ret}" if test "x${kind}" = "xstatic"; then extension=".a" elif test "x${kind}" = "xshared"; then if is_plat "macosx"; then extension=".dylib" else extension=".so" fi fi fi _ret="${extension}" } # get target prefixname _get_target_prefixname() { local name="${1}" local prefixname="" if _has_target_item "${name}" "prefixname"; then _get_target_item "${name}" "prefixname"; prefixname="${_ret}" elif is_plat "mingw"; then _get_target_item "${name}" "kind"; local kind="${_ret}" if test "x${kind}" = "xstatic"; then prefixname="lib" elif test "x${kind}" = "xshared"; then prefixname="lib" fi else _get_target_item "${name}" "kind"; local kind="${_ret}" if test "x${kind}" = "xstatic"; then prefixname="lib" elif test "x${kind}" = "xshared"; then prefixname="lib" fi fi _ret="${prefixname}" } # get target filename _get_target_filename() { local name="${1}" _get_target_item "${name}" "filename"; local filename="${_ret}" if test_z "${filename}"; then _get_target_basename "${name}"; local basename="${_ret}" _get_target_extension "${name}"; local extension="${_ret}" _get_target_prefixname "${name}"; local prefixname="${_ret}" filename="${prefixname}${basename}${extension}" fi _ret="${filename}" } # get target soname # @see https://github.com/tboox/tbox/issues/214 # # set_version "1.0.1" "" "1" -> libfoo.so.1, libfoo.1.dylib # set_version "1.0.1" "" "A" -> libfoo.so.A, libfoo.A.dylib _get_target_soname() { local soname="" local name="${1}" _get_target_item "${name}" "kind"; local targetkind="${_ret}" if test_eq "${targetkind}" "shared" && is_plat "macosx" "linux" "bsd"; then _get_target_item "${name}" "version"; local version="${_ret}" _get_target_item "${name}" "version_soname"; local version_soname="${_ret}" if test_nz "${version}" && test_nz "${version_soname}"; then _get_target_filename "${name}"; soname="${_ret}" _get_target_extension "${name}"; local extension="${_ret}" if test_eq "${extension}" ".dylib"; then path_basename "${soname}"; local basename="${_ret}" soname="${basename}.${version_soname}${extension}" else soname="${soname}.${version_soname}" fi fi fi _ret="${soname}" } # get target directory _get_targetdir() { local name="${1}" _get_target_item "${name}" "targetdir"; local targetdir="${_ret}" if test_z "${targetdir}"; then targetdir="${xmake_sh_builddir}/${_target_plat}/${_target_arch}/${_target_mode}" fi _ret="${targetdir}" } # get target object directory _get_target_objectdir() { local name="${1}" _get_target_item "${name}" "objectdir"; local objectdir="${_ret}" if test_z "${objectdir}"; then objectdir="${xmake_sh_builddir}/.objs/${name}/${_target_plat}/${_target_arch}/${_target_mode}" fi _ret="${objectdir}" } # get target file path _get_target_file() { local name="${1}" _get_target_filename "${name}"; local filename="${_ret}" _get_targetdir "${name}"; local targetdir="${_ret}" local targetfile="${targetdir}/${filename}" _ret="${targetfile}" } # get target librarydeps _get_target_librarydeps_impl() { local name="${1}" local librarydeps="" local dep="" _get_target_item "${name}" "deps"; local deps="${_ret}" for dep in ${deps}; do _get_target_item "${dep}" "kind"; local dep_kind="${_ret}" if test_eq "${dep_kind}" "static" || test_eq "${dep_kind}" "shared"; then librarydeps="${librarydeps} ${dep}" _get_target_librarydeps_impl "${dep}"; local dep_librarydeps="${_ret}" if test_nz "${dep_librarydeps}"; then librarydeps="${librarydeps} ${dep_librarydeps}" fi fi done _ret="${librarydeps}" } _get_target_librarydeps() { local name="${1}" _get_target_item "${name}" "librarydeps"; local librarydeps="${_ret}" if test_z "${librarydeps}" && test_nq "${librarydeps}" "__none__"; then _get_target_librarydeps_impl "${name}"; librarydeps="${_ret}" if test_nz "${librarydeps}"; then _dedup_reverse "${librarydeps}"; librarydeps="${_ret}" _set_target_item "${name}" "librarydeps" "${librarydeps}" else _set_target_item "${name}" "librarydeps" "__none__" fi fi if test_eq "${librarydeps}" "__none__"; then librarydeps="" fi _ret="${librarydeps}" } # get sourcefiles in target _get_target_sourcefiles() { local name="${1}" _get_target_item "${name}" "files" } # get objectfile in target _get_target_objectfile() { local name="${1}" local sourcefile="${2}" local extension=".o" if is_plat "mingw"; then extension=".obj" fi _get_target_objectdir "${name}"; local objectdir="${_ret}" local objectfile="${objectdir}/${sourcefile}${extension}" _ret="${objectfile}" } # get objectfiles in target _get_target_objectfiles() { local name="${1}" _get_target_sourcefiles "${name}"; local sourcefiles="${_ret}" local objectfiles="" local sourcefile="" for sourcefile in ${sourcefiles}; do _get_target_objectfile "${name}" "${sourcefile}"; local objectfile="${_ret}" objectfiles="${objectfiles} ${objectfile}" done _ret="${objectfiles}" } # get abstract flags in target _get_target_abstract_flags() { local name="${1}" local toolkind="${2}" local toolname="${3}" local itemname="${4}" local values="${5}" if test_z "${values}"; then # get values from target _get_target_item "${name}" "${itemname}"; values="${_ret}" # get values from target deps _get_target_librarydeps "${name}"; local deps="${_ret}" local dep="" for dep in ${deps}; do _get_target_item "${dep}" "kind"; local dep_kind="${_ret}" if test_eq "${dep_kind}" "static" || test_eq "${dep_kind}" "shared"; then _get_target_item "${dep}" "${itemname}_public"; local depvalues="${_ret}" if test_nz "${depvalues}"; then values="${values} ${depvalues}" fi fi done fi if test_nz "${values}"; then _get_abstract_flags "${toolkind}" "${toolname}" "${itemname}" "${values}" else _ret="" fi } # get toolchain flags for ar in target _get_target_toolchain_flags_for_ar() { _ret="-cr" } # get toolchain flags for gcc in target _get_target_toolchain_flags_for_gcc() { local name="${1}" local toolkind="${2}" local flags="" if is_arch "i386"; then flags="${flags} -m32" fi _get_target_item "${name}" "kind"; local targetkind="${_ret}" if test_eq "${targetkind}" "shared"; then if test_eq "${toolkind}" "sh"; then flags="${flags} -shared -fPIC" elif test_eq "${toolkind}" "cc" || test_eq "${toolkind}" "cxx"; then flags="${flags} -fPIC" fi # @see https://github.com/tboox/tbox/issues/214 if test_eq "${toolkind}" "sh"; then _get_target_soname "${name}"; local soname="${_ret}" if test_nz "${soname}"; then if is_plat "macosx"; then flags="${flags} -Wl,-install_name,${soname}" else flags="${flags} -Wl,-soname,${soname}" fi fi fi fi _ret="${flags}" } # get toolchain flags for clang in target _get_target_toolchain_flags_for_clang() { local name="${1}" local toolkind="${2}" local flags="-Qunused-arguments" if is_arch "i386"; then flags="${flags} -m32" fi _get_target_item "${name}" "kind"; local targetkind="${_ret}" if test_eq "${targetkind}" "shared"; then if test_eq "${toolkind}" "sh"; then flags="${flags} -shared -fPIC" elif test_eq "${toolkind}" "cc" || test_eq "${toolkind}" "cxx"; then flags="${flags} -fPIC" fi # @see https://github.com/tboox/tbox/issues/214 if test_eq "${toolkind}" "sh"; then _get_target_soname "${name}"; local soname="${_ret}" if test_nz "${soname}"; then if is_plat "macosx"; then flags="${flags} -Wl,-install_name,${soname}" else flags="${flags} -Wl,-soname,${soname}" fi fi fi fi # set target and sysroot if is_plat "macosx"; then _os_iorunv "xcrun" "-sdk" "macosx" "--show-sdk-path"; local sdkdir="${_ret}" if test_nz "${sdkdir}"; then flags="${flags} -isysroot \"${sdkdir}\"" # get target minver and set target flags path_basename "${sdkdir}"; local basename="${_ret}" if test_nz "${basename}" && string_startswith "${basename}" "MacOSX"; then string_replace "${basename}" "MacOSX" ""; local target_minver="${_ret}" if test_nz "${target_minver}"; then flags="${flags} -target ${arch}-apple-macos${target_minver}" fi fi fi fi _ret="${flags}" } # get toolchain flags for tcc in target _get_target_toolchain_flags_for_tcc() { local name="${1}" local toolkind="${2}" local flags="" if is_arch "i386"; then flags="${flags} -m32" fi _get_target_item "${name}" "kind"; local targetkind="${_ret}" if test_eq "${targetkind}" "shared"; then if test_eq "${toolkind}" "sh"; then flags="${flags} -shared -fPIC" elif test_eq "${toolkind}" "cc" || test_eq "${toolkind}" "cxx"; then flags="${flags} -fPIC" fi # @see https://github.com/tboox/tbox/issues/214 if test_eq "${toolkind}" "sh"; then _get_target_soname "${name}"; local soname="${_ret}" if test_nz "${soname}"; then if is_plat "macosx"; then flags="${flags} -Wl,-install_name,${soname}" else flags="${flags} -Wl,-soname,${soname}" fi fi fi fi # set target and sysroot if is_plat "macosx"; then _os_iorunv "xcrun" "-sdk" "macosx" "--show-sdk-path"; local sdkdir="${_ret}" if test_nz "${sdkdir}"; then flags="${flags} -isysroot \"${sdkdir}\"" # get target minver and set target flags path_basename "${sdkdir}"; local basename="${_ret}" if test_nz "${basename}" && string_startswith "${basename}" "MacOSX"; then string_replace "${basename}" "MacOSX" ""; local target_minver="${_ret}" if test_nz "${target_minver}"; then flags="${flags} -target ${arch}-apple-macos${target_minver}" fi fi fi fi _ret="${flags}" } # get toolchain flags in target _get_target_toolchain_flags() { local name="${1}" local toolkind="${2}" local toolname="${3}" local flags="" case "${toolname}" in gcc) _get_target_toolchain_flags_for_gcc "${name}" "${toolkind}"; flags="${_ret}";; gxx) _get_target_toolchain_flags_for_gcc "${name}" "${toolkind}"; flags="${_ret}";; clang) _get_target_toolchain_flags_for_clang "${name}" "${toolkind}"; flags="${_ret}";; clangxx) _get_target_toolchain_flags_for_clang "${name}" "${toolkind}"; flags="${_ret}";; emcc) _get_target_toolchain_flags_for_clang "${name}" "${toolkind}"; flags="${_ret}";; emxx) _get_target_toolchain_flags_for_clang "${name}" "${toolkind}"; flags="${_ret}";; cosmocc) _get_target_toolchain_flags_for_gcc "${name}" "${toolkind}"; flags="${_ret}";; cosmocxx) _get_target_toolchain_flags_for_gcc "${name}" "${toolkind}"; flags="${_ret}";; tcc) _get_target_toolchain_flags_for_tcc "${name}" "${toolkind}"; flags="${_ret}";; ar) _get_target_toolchain_flags_for_ar "${name}" "${toolkind}"; flags="${_ret}";; emar) _get_target_toolchain_flags_for_ar "${name}" "${toolkind}"; flags="${_ret}";; cosmoar) _get_target_toolchain_flags_for_ar "${name}" "${toolkind}"; flags="${_ret}";; *) raise "unknown toolname(${toolname})!" ;; esac _ret="${flags}" } # get compiler flags in target _get_target_compiler_flags() { local name="${1}" local toolkind="${2}" _get_toolchain_toolset "${_target_toolchain}" "${toolkind}"; local program="${_ret}" path_toolname "${program}"; local toolname="${_ret}" local result="" # get toolchain flags _get_target_toolchain_flags "${name}" "${toolkind}" "${toolname}"; local toolchain_flags="${_ret}" if test_nz "${toolchain_flags}"; then result="${result} ${toolchain_flags}" fi # get abstract flags local itemnames="symbols optimizes warnings languages defines undefines includedirs frameworkdirs frameworks" local itemname="" for itemname in ${itemnames}; do _get_target_abstract_flags "${name}" "${toolkind}" "${toolname}" "${itemname}"; local flags="${_ret}" if test_nz "${flags}"; then result="${result} ${flags}" fi done # get raw flags, e.g. add_cflags, add_cxxflags _get_flagname "${toolkind}"; local flagname="${_ret}" _get_target_item "${name}" "${flagname}"; local flags="${_ret}" # get flags from target deps _get_target_librarydeps "${name}"; local deps="${_ret}" local dep="" for dep in ${deps}; do _get_target_item "${dep}" "kind"; local dep_kind="${_ret}" if test_eq "${dep_kind}" "static" || test_eq "${dep_kind}" "shared"; then _get_target_item "${dep}" "${flagname}_public"; local depflags="${_ret}" if test_nz "${depflags}"; then flags="${flags} ${depflags}" fi fi done if test_nz "${flags}"; then result="${result} ${flags}" fi if test_eq "${flagname}" "cflags" || test_eq "${flagname}" "cxxflags"; then _get_target_item "${name}" "cxflags"; flags="${_ret}" if test_nz "${flags}"; then result="${result} ${flags}" fi elif test_eq "${flagname}" "mflags" || test_eq "${flagname}" "mxxflags"; then _get_target_item "${name}" "mxflags"; flags="${_ret}" if test_nz "${flags}"; then result="${result} ${flags}" fi fi # get flags from environments, e.g. $CFLAGS, $CXXFLAGS if test_nz "${CPPFLAGS}"; then result="${result} ${CPPFLAGS}" fi if test_eq "${flagname}" "cflags" && test_nz "${CFLAGS}"; then result="${result} ${CFLAGS}" fi if test_eq "${flagname}" "cxxflags" && test_nz "${CXXFLAGS}"; then result="${result} ${CXXFLAGS}" fi if test_eq "${flagname}" "mflags" && test_nz "${MFLAGS}"; then result="${result} ${MFLAGS}" fi if test_eq "${flagname}" "mxxflags" && test_nz "${MXXFLAGS}"; then result="${result} ${MXXFLAGS}" fi _ret="${result}" } # get linker flags in target _get_target_linker_flags() { local name="${1}" local toolkind="${2}" _get_toolchain_toolset "${_target_toolchain}" "${toolkind}"; local program="${_ret}" path_toolname "${program}"; local toolname="${_ret}" local result="" # get toolchain flags _get_target_toolchain_flags "${name}" "${toolkind}" "${toolname}"; local toolchain_flags="${_ret}" if test_nz "${toolchain_flags}"; then result="${result} ${toolchain_flags}" fi # get flags from target deps _get_target_librarydeps "${name}"; local deps="${_ret}" local dep="" for dep in ${deps}; do _get_target_item "${dep}" "kind"; local dep_kind="${_ret}" if test_eq "${dep_kind}" "static" || test_eq "${dep_kind}" "shared"; then _get_targetdir "${dep}"; local dep_targetdir="${_ret}" _get_target_basename "${dep}"; local dep_basename="${_ret}" _get_target_abstract_flags "${dep}" "${toolkind}" "${toolname}" "linkdirs" "${dep_targetdir}"; local linkdirs_flags="${_ret}" _get_target_abstract_flags "${dep}" "${toolkind}" "${toolname}" "links" "${dep_basename}"; local links_flags="${_ret}" if test_eq "${dep_kind}" "shared"; then local rpathdir="@loader_path" _get_targetdir "${name}"; local targetdir="${_ret}" path_relative "${targetdir}" "${dep_targetdir}"; local subdir="${_ret}" if test_nz "${subdir}"; then rpathdir="${rpathdir}/${subdir}" fi _get_target_abstract_flags "${dep}" "${toolkind}" "${toolname}" "rpathdirs" "${rpathdir}"; local rpathdirs_flags="${_ret}" result="${result} ${rpathdirs_flags}" fi result="${result} ${linkdirs_flags} ${links_flags}" fi done # get abstract flags local itemnames="strip frameworkdirs linkdirs links rpathdirs frameworks syslinks" local itemname="" for itemname in ${itemnames}; do _get_target_abstract_flags "${name}" "${toolkind}" "${toolname}" "${itemname}"; local flags="${_ret}" if test_nz "${flags}"; then result="${result} ${flags}" fi done # get raw flags, e.g. add_ldflags, add_shflags _get_flagname "${toolkind}"; local flagname="${_ret}" _get_target_item "${name}" "${flagname}"; local flags="${_ret}" # get flags from target deps for dep in ${deps}; do _get_target_item "${dep}" "kind"; local dep_kind="${_ret}" if test_eq "${dep_kind}" "static" || test_eq "${dep_kind}" "shared"; then _get_target_item "${dep}" "${flagname}_public"; local depflags="${_ret}" if test_nz "${depflags}"; then flags="${flags} ${depflags}" fi fi done if test_nz "${flags}"; then result="${result} ${flags}" fi # get flags from environments, e.g. $LDFLAGS if test_eq "${flagname}" "ldflags" && test_nz "${LDFLAGS}"; then result="${result} ${LDFLAGS}" fi if test_eq "${flagname}" "shflags" && test_nz "${LDFLAGS}"; then result="${result} ${LDFLAGS}" fi _ret="${result}" } # get archiver flags in target _get_target_archiver_flags() { local name="${1}" local toolkind="${2}" _get_toolchain_toolset "${_target_toolchain}" "${toolkind}"; local program="${_ret}" path_toolname "${program}"; local toolname="${_ret}" local result="" # get toolchain flags _get_target_toolchain_flags "${name}" "${toolkind}" "${toolname}"; local toolchain_flags="${_ret}" if test_nz "${toolchain_flags}"; then result="${result} ${toolchain_flags}" fi # get raw flags, e.g. add_arflags _get_flagname "${toolkind}"; local flagname="${_ret}" _get_target_item "${name}" "${flagname}"; local flags="${_ret}" if test_nz "${flags}"; then result="${result} ${flags}" fi _ret="${result}" } # get target flags _get_target_flags() { local name="${1}" local toolkind="${2}" local flags="" if test_eq "${toolkind}" "sh"; then _get_target_linker_flags "${name}" "${toolkind}"; flags="${_ret}" elif test_eq "${toolkind}" "ld"; then _get_target_linker_flags "${name}" "${toolkind}"; flags="${_ret}" elif test_eq "${toolkind}" "ar"; then _get_target_archiver_flags "${name}" "${toolkind}"; flags="${_ret}" else _get_target_compiler_flags "${name}" "${toolkind}"; flags="${_ret}" fi _ret="${flags}" } # add file paths in target _add_target_filepaths() { local key="$1" shift # we need avoid escape `*` automatically in for-loop local file="" string_replace "${@}" "\*" "?"; local list="${_ret}" if test_eq "${key}" "files"; then for file in ${list}; do path_sourcekind "${file}"; local sourcekind="${_ret}" _targets_toolkinds="${_targets_toolkinds} ${sourcekind}" done fi for file in ${list}; do string_replace "${file}" "?" "*"; file="${_ret}" if ! path_is_absolute "${file}"; then file="${xmake_sh_scriptdir}/${file}" fi local files="" if string_contains_star2 "${file}"; then path_directory "${file}"; local dir="${_ret}" path_filename_fromdir "${file}" "${dir}"; local name="${_ret}" _os_find "${dir}" "${name}"; files="${_ret}" elif string_contains_star "${file}"; then path_directory "${file}"; local dir="${_ret}" path_filename_fromdir "${file}" "${dir}"; local name="${_ret}" _os_find "${dir}" "${name}" 1; files="${_ret}" else files="${file}" fi for file in ${files}; do path_relative "${xmake_sh_projectdir}" "${file}"; file="${_ret}" _add_target_item "${_xmake_sh_target_current}" "${key}" "${file}" done done } # add install paths in target _add_target_installpaths() { local key="$1" local filepattern="${2}" local prefixdir="${3}" local filename=${4} # get root directory, e.g. "src/foo/(*.h)" -> "src/foo" local rootdir="" if string_contains "${filepattern}" "("; then string_split "${filepattern}" "(" 1; rootdir="${_ret}" rootdir=${rootdir%/} if ! path_is_absolute "${rootdir}"; then rootdir="${xmake_sh_scriptdir}/${rootdir}" fi path_relative "${xmake_sh_projectdir}" "${rootdir}"; rootdir="${_ret}" rootdir=${rootdir%/} fi # remove (), e.g. "src/(*.h)" -> "src/*.h" string_replace ${filepattern} "(" ""; filepattern="${_ret}" string_replace ${filepattern} ")" ""; filepattern="${_ret}" # get real path if ! path_is_absolute "${filepattern}"; then filepattern="${xmake_sh_scriptdir}/${filepattern}" fi local files="" if string_contains_star2 "${filepattern}"; then path_directory "${filepattern}"; local dir="${_ret}" path_filename_fromdir "${filepattern}" "${dir}"; local name="${_ret}" _os_find "${dir}" "${name}"; files="${_ret}" elif string_contains_star "${filepattern}"; then path_directory "${filepattern}"; local dir="${_ret}" path_filename_fromdir "${filepattern}" "${dir}"; local name="${_ret}" _os_find "${dir}" "${name}" 1; files="${_ret}" else files="${filepattern}" fi for file in ${files}; do path_relative "${xmake_sh_projectdir}" "${file}"; file="${_ret}" _add_target_item "${_xmake_sh_target_current}" "${key}" "${file}:${rootdir}:${prefixdir}:${filename}" done } # set target file path _set_target_filepath() { local key="${1}" local path="${2}" if ! path_is_absolute "${path}"; then path="${xmake_sh_scriptdir}/${path}" fi path_relative ${xmake_sh_projectdir} "${path}"; path="${_ret}" _set_target_item "${_xmake_sh_target_current}" "${key}" "${path}" } # set kind in target set_kind() { if ! ${_loading_targets}; then return fi local kind=${1} _set_target_item "${_xmake_sh_target_current}" "kind" "${kind}" case "${kind}" in binary) _targets_toolkinds="${_targets_toolkinds} ld";; static) _targets_toolkinds="${_targets_toolkinds} ar";; shared) _targets_toolkinds="${_targets_toolkinds} sh";; *) raise "unknown target kind ${kind}";; esac } # set version in target set_version() { if ! ${_loading_targets}; then return fi local version="${1}" local version_build="${2}" local version_soname="${3}" _set_target_item "${_xmake_sh_target_current}" "version" "${version}" _set_target_item "${_xmake_sh_target_current}" "version_build" "${version_build}" _set_target_item "${_xmake_sh_target_current}" "version_soname" "${version_soname}" } # set default in target set_default() { local default=${1} if ${_loading_targets} && test_z "${_xmake_sh_option_current}"; then _set_target_item "${_xmake_sh_target_current}" "default" "${default}" elif ${_loading_options} && test_nz "${_xmake_sh_option_current}"; then _set_option_item "${_xmake_sh_option_current}" "default" "${default}" fi } # set configvar in target set_configvar() { if ! ${_loading_targets}; then return fi local name="${1}" local value="${2}" _set_target_item "${_xmake_sh_target_current}" "configvar_${name}" "${value}" _add_target_item "${_xmake_sh_target_current}" "configvars" "${name}" } # set filename in target set_filename() { if ! ${_loading_targets}; then return fi local filename="${1}" _set_target_item "${_xmake_sh_target_current}" "filename" "${filename}" } # set basename in target set_basename() { if ! ${_loading_targets}; then return fi local basename="${1}" _set_target_item "${_xmake_sh_target_current}" "basename" "${basename}" } # set extension in target set_extension() { if ! ${_loading_targets}; then return fi local extension=${1} _set_target_item "${_xmake_sh_target_current}" "extension" "${extension}" } # set prefixname in target set_prefixname() { if ! ${_loading_targets}; then return fi local prefixname=${1} _set_target_item "${_xmake_sh_target_current}" "prefixname" "${prefixname}" } # set target directory set_targetdir() { if ! ${_loading_targets}; then return fi _set_target_filepath "targetdir" "${1}" } # set target object directory set_objectdir() { if ! ${_loading_targets}; then return fi _set_target_filepath "objectdir" "${1}" } # set target config directory set_configdir() { if ! ${_loading_targets}; then return fi _set_target_filepath "configdir" "${1}" } # set target install directory set_installdir() { if ! ${_loading_targets}; then return fi _set_target_filepath "installdir" "${1}" } # add deps in target add_deps() { if ! ${_loading_targets}; then return fi _add_target_item "${_xmake_sh_target_current}" "deps" "${@}" } # add files in target add_files() { if ! ${_loading_targets}; then return fi _add_target_filepaths "files" "$@" } # add install files in target add_installfiles() { if ! ${_loading_targets}; then return fi _add_target_installpaths "installfiles" "$@" } # add header files in target add_headerfiles() { if ! ${_loading_targets}; then return fi _add_target_installpaths "headerfiles" "$@" } # add config files in target add_configfiles() { if ! ${_loading_targets}; then return fi _add_target_filepaths "configfiles" "$@" } # add defines in target add_defines() { local define="" if ${_loading_targets} && test_z "${_xmake_sh_option_current}"; then local public=false for define in $@; do if test_nq "${define}" "{public}"; then _add_target_item "${_xmake_sh_target_current}" "defines" "${define}" else public=true fi done if ${public}; then for define in $@; do if test_nq "${define}" "{public}"; then _add_target_item "${_xmake_sh_target_current}" "defines_public" "${define}" fi done fi elif ${_loading_options} && test_nz "${_xmake_sh_option_current}"; then _add_option_item "${_xmake_sh_option_current}" "defines" "${@}" fi } # add undefines in target add_undefines() { local undefine="" if ${_loading_targets} && test_z "${_xmake_sh_option_current}"; then local public=false for undefine in $@; do if test_nq "${undefine}" "{public}"; then _add_target_item "${_xmake_sh_target_current}" "undefines" "${undefine}" else public=true fi done if ${public}; then for undefine in $@; do if test_nq "${undefine}" "{public}"; then _add_target_item "${_xmake_sh_target_current}" "undefines_public" "${undefine}" fi done fi elif ${_loading_options} && test_nz "${_xmake_sh_option_current}"; then _add_option_item "${_xmake_sh_option_current}" "undefines" "${@}" fi } # add includedirs in target add_includedirs() { local public=false local dir="" for dir in $@; do if test_nq "${dir}" "{public}"; then if ! path_is_absolute "${dir}"; then dir="${xmake_sh_scriptdir}/${dir}" fi if string_startswith "${dir}" "${xmake_sh_projectdir}"; then path_relative ${xmake_sh_projectdir} "${dir}"; dir="${_ret}" fi if ${_loading_targets} && test_z "${_xmake_sh_option_current}"; then _add_target_item "${_xmake_sh_target_current}" "includedirs" "${dir}" elif ${_loading_options} && test_nz "${_xmake_sh_option_current}"; then _add_option_item "${_xmake_sh_option_current}" "includedirs" "${dir}" fi else public=true fi done if ${public}; then for dir in $@; do if test_nq "${dir}" "{public}"; then if ! path_is_absolute "${dir}"; then dir="${xmake_sh_scriptdir}/${dir}" fi if string_startswith "${dir}" "${xmake_sh_projectdir}"; then path_relative ${xmake_sh_projectdir} "${dir}"; dir="${_ret}" fi if ${_loading_targets} && test_z "${_xmake_sh_option_current}"; then _add_target_item "${_xmake_sh_target_current}" "includedirs_public" "${dir}" fi fi done fi } # add links in target add_links() { local link="" if ${_loading_targets} && test_z "${_xmake_sh_option_current}"; then local public=false for link in $@; do if test_nq "${link}" "{public}"; then _add_target_item "${_xmake_sh_target_current}" "links" "${link}" else public=true fi done if ${public}; then for link in $@; do if test_nq "${link}" "{public}"; then _add_target_item "${_xmake_sh_target_current}" "links_public" "${link}" fi done fi elif ${_loading_options} && test_nz "${_xmake_sh_option_current}"; then _add_option_item "${_xmake_sh_option_current}" "links" "${@}" fi } # add syslinks in target add_syslinks() { local syslink="" if ${_loading_targets} && test_z "${_xmake_sh_option_current}"; then local public=false for syslink in $@; do if test_nq "${syslink}" "{public}"; then _add_target_item "${_xmake_sh_target_current}" "syslinks" "${syslink}" else public=true fi done if ${public}; then for syslink in $@; do if test_nq "${syslink}" "{public}"; then _add_target_item "${_xmake_sh_target_current}" "syslinks_public" "${syslink}" fi done fi elif ${_loading_options} && test_nz "${_xmake_sh_option_current}"; then _add_option_item "${_xmake_sh_option_current}" "syslinks" "${@}" fi } # add linkdirs in target add_linkdirs() { local dir="" local public=false for dir in $@; do if test_nq "${dir}" "{public}"; then if ! path_is_absolute "${dir}"; then dir="${xmake_sh_scriptdir}/${dir}" fi if string_startswith "${dir}" "${xmake_sh_projectdir}"; then path_relative ${xmake_sh_projectdir} "${dir}"; dir="${_ret}" fi if ${_loading_targets} && test_z "${_xmake_sh_option_current}"; then _add_target_item "${_xmake_sh_target_current}" "linkdirs" "${dir}" elif ${_loading_options} && test_nz "${_xmake_sh_option_current}"; then _add_option_item "${_xmake_sh_option_current}" "linkdirs" "${dir}" fi else public=true fi done if ${public}; then for dir in $@; do if test_nq "${dir}" "{public}"; then if ! path_is_absolute "${dir}"; then dir="${xmake_sh_scriptdir}/${dir}" fi if string_startswith "${dir}" "${xmake_sh_projectdir}"; then path_relative ${xmake_sh_projectdir} "${dir}"; dir="${_ret}" fi if ${_loading_targets} && test_z "${_xmake_sh_option_current}"; then _add_target_item "${_xmake_sh_target_current}" "linkdirs_public" "${dir}" fi fi done fi } # add rpathdirs in target add_rpathdirs() { if ! ${_loading_targets}; then return fi local dir="" for dir in $@; do if ! path_is_absolute "${dir}"; then dir="${xmake_sh_scriptdir}/${dir}" fi path_relative ${xmake_sh_projectdir} "${dir}"; dir="${_ret}" _add_target_item "${_xmake_sh_target_current}" "rpathdirs" "${dir}" done } # add frameworks in target add_frameworks() { local framework="" if ${_loading_targets} && test_z "${_xmake_sh_option_current}"; then local public=false for framework in $@; do if test_nq "${framework}" "{public}"; then _add_target_item "${_xmake_sh_target_current}" "frameworks" "${framework}" else public=true fi done if ${public}; then for framework in $@; do if test_nq "${framework}" "{public}"; then _add_target_item "${_xmake_sh_target_current}" "frameworks_public" "${framework}" fi done fi elif ${_loading_options} && test_nz "${_xmake_sh_option_current}"; then _add_option_item "${_xmake_sh_option_current}" "frameworks" "${@}" fi } # add frameworkdirs in target add_frameworkdirs() { local dir="" for dir in $@; do if ! path_is_absolute "${dir}"; then dir="${xmake_sh_scriptdir}/${dir}" fi path_relative ${xmake_sh_projectdir} "${dir}"; dir="${_ret}" if ${_loading_targets} && test_z "${_xmake_sh_option_current}"; then _add_target_item "${_xmake_sh_target_current}" "frameworkdirs" "${dir}" elif ${_loading_options} && test_nz "${_xmake_sh_option_current}"; then _add_option_item "${_xmake_sh_option_current}" "frameworkdirs" "${dir}" fi done } # set strip in target set_strip() { if ! ${_loading_targets}; then return fi local strip=${1} _set_target_item "${_xmake_sh_target_current}" "strip" "${strip}" } # set symbols in target set_symbols() { if ! ${_loading_targets}; then return fi local symbols="${1}" _set_target_item "${_xmake_sh_target_current}" "symbols" "${symbols}" } # set languages in target set_languages() { if ${_loading_targets} && test_z "${_xmake_sh_option_current}"; then _set_target_item "${_xmake_sh_target_current}" "languages" "${@}" elif ${_loading_options} && test_nz "${_xmake_sh_option_current}"; then _set_option_item "${_xmake_sh_option_current}" "languages" "${@}" fi } # set warnings in target set_warnings() { if ${_loading_targets} && test_z "${_xmake_sh_option_current}"; then _set_target_item "${_xmake_sh_target_current}" "warnings" "${@}" elif ${_loading_options} && test_nz "${_xmake_sh_option_current}"; then _set_option_item "${_xmake_sh_option_current}" "warnings" "${@}" fi } # set optimizes in target set_optimizes() { if ${_loading_targets} && test_z "${_xmake_sh_option_current}"; then _set_target_item "${_xmake_sh_target_current}" "optimizes" "${@}" elif ${_loading_options} && test_nz "${_xmake_sh_option_current}"; then _set_option_item "${_xmake_sh_option_current}" "optimizes" "${@}" fi } # add cflags in target add_cflags() { if ${_loading_targets} && test_z "${_xmake_sh_option_current}"; then _add_target_item "${_xmake_sh_target_current}" "cflags" "${@}" elif ${_loading_options} && test_nz "${_xmake_sh_option_current}"; then _add_option_item "${_xmake_sh_option_current}" "cflags" "${@}" fi } # add cxflags in target add_cxflags() { if ${_loading_targets} && test_z "${_xmake_sh_option_current}"; then _add_target_item "${_xmake_sh_target_current}" "cxflags" "${@}" elif ${_loading_options} && test_nz "${_xmake_sh_option_current}"; then _add_option_item "${_xmake_sh_option_current}" "cxflags" "${@}" fi } # add cxxflags in target add_cxxflags() { if ${_loading_targets} && test_z "${_xmake_sh_option_current}"; then _add_target_item "${_xmake_sh_target_current}" "cxxflags" "${@}" elif ${_loading_options} && test_nz "${_xmake_sh_option_current}"; then _add_option_item "${_xmake_sh_option_current}" "cxxflags" "${@}" fi } # add asflags in target add_asflags() { if ! ${_loading_targets}; then return fi _add_target_item "${_xmake_sh_target_current}" "asflags" "${@}" } # add mflags in target add_mflags() { if ! ${_loading_targets}; then return fi _add_target_item "${_xmake_sh_target_current}" "mflags" "${@}" } # add mxflags in target add_mxflags() { if ! ${_loading_targets}; then return fi _add_target_item "${_xmake_sh_target_current}" "mxflags" "${@}" } # add mxxflags in target add_mxxflags() { if ! ${_loading_targets}; then return fi _add_target_item "${_xmake_sh_target_current}" "mxxflags" "${@}" } # add ldflags in target add_ldflags() { if ${_loading_targets} && test_z "${_xmake_sh_option_current}"; then _add_target_item "${_xmake_sh_target_current}" "ldflags" "${@}" elif ${_loading_options} && test_nz "${_xmake_sh_option_current}"; then _add_option_item "${_xmake_sh_option_current}" "ldflags" "${@}" fi } # add shflags in target add_shflags() { if ! ${_loading_targets}; then return fi _add_target_item "${_xmake_sh_target_current}" "shflags" "${@}" } # add arflags in target add_arflags() { if ! ${_loading_targets}; then return fi _add_target_item "${_xmake_sh_target_current}" "arflags" "${@}" } # add options in target add_options() { if ! ${_loading_targets}; then return fi local name="" local public=false for name in $@; do if test_nq "${name}" "{public}"; then public=true break fi done for name in $@; do if has_config "${name}"; then local itemname="" local itemnames="includedirs linkdirs links defines cflags cxflags cxxflags ldflags" for itemname in ${itemnames}; do _get_option_item "${name}" "${itemname}"; local values="${_ret}" if test_nz "${values}"; then _add_target_item "${_xmake_sh_target_current}" "${itemname}" "${values}" if $public; then _add_target_item "${_xmake_sh_target_current}" "${itemname}_public" "${values}" fi fi done fi done } # before_install in target before_install() { if ! ${_loading_targets}; then return fi local funcname="${1}" _add_target_item "${_xmake_sh_target_current}" "before_install" "${funcname}" } # after_install in target after_install() { if ! ${_loading_targets}; then return fi local funcname="${1}" _add_target_item "${_xmake_sh_target_current}" "after_install" "${funcname}" } #----------------------------------------------------------------------------- # toolchain configuration apis # # define toolchain toolchain() { local name="${1}" _xmake_sh_toolchain_current="${name}" if ! ${_loading_toolchains}; then return fi _xmake_sh_toolchains="${_xmake_sh_toolchains} ${name}" _map_set "toolchains" "${name}_name" "${name}" return 0 } toolchain_end() { _xmake_sh_toolchain_current="" } _map "toolchains" # has the given toolchain? _has_toolchain() { local name=${1} if _map_has "toolchains" "${name}_name"; then return 0 fi return 1 } # get the given toolchain item _get_toolchain_item() { local name=${1} local key=${2} _map_get "toolchains" "${name}_${key}" } # set the given toolchain item _set_toolchain_item() { local name=${1} local key=${2} local value="${3}" if test_nz "${name}"; then _map_set "toolchains" "${name}_${key}" "${value}" else raise "please set toolchain in the toolchain scope!" fi } # get the give toolchain toolset _get_toolchain_toolset() { local name=${1} local kind=${2} _get_toolchain_item "${name}" "toolset_${kind}" } # set the give toolchain toolset _set_toolchain_toolset() { local name=${1} local kind=${2} local programs="${3}" _set_toolchain_item "${name}" "toolset_${kind}" "${programs}" } # add the give toolchain toolset _add_toolchain_toolset() { local name=${1} local kind=${2} local program="${3}" _get_toolchain_item "${name}" "toolset_${kind}"; local programs="${_ret}" if test_nz "${programs}"; then programs="${programs}:${program}" else programs="${program}" fi _set_toolchain_item "${name}" "toolset_${kind}" "${programs}" } # set toolset in toolchain set_toolset() { if ! ${_loading_toolchains}; then return fi local kind=${1} shift local idx=0 while test $# != 0; do local program="${1}" local key="${kind}" if test_nq "${idx}" "0"; then key="${key}_${idx}" fi _set_toolchain_toolset "${_xmake_sh_toolchain_current}" "${key}" "${program}" idx=$((idx+1)) shift done } #----------------------------------------------------------------------------- # load options # # load options and toolchains _load_options_and_toolchains() { _loading_options=true _loading_toolchains=true _loading_targets=false local file=${xmake_sh_projectdir}/xmake.sh if test -f "${file}"; then includes "${file}" else # include all xmake.sh files in next sub-directories _os_find "${xmake_sh_projectdir}" "xmake.sh" "2" local files="${_ret}" for file in ${files}; do includes "${file}" done fi } _load_options_and_toolchains # show option usage _show_options_usage() { _get_options_for_menu; local options="${_ret}" for name in ${options}; do _get_option_item "${name}" "description"; local description="${_ret}" _get_option_item "${name}" "default"; local default="${_ret}" string_toupper ${name}; local head="--${name}="${_ret}"" local headsize=${#head} local tail="${description}" if test "x${default}" != "x"; then local defval=${default} if test "x${defval}" = "xtrue"; then defval="yes" elif test "x${defval}" = "xfalse"; then defval="no" fi tail="${tail} (default: ${defval})" fi local width=24 local padding_width=$((${width} - ${headsize})) local padding=$(string_dupch ${padding_width} " ") echo " ${head}${padding}${tail}" done } # show configure usage _show_usage() { echo ' Usage: '"$0"' [...] Options: [defaults in brackets after descriptions] Common options: --help Print this message. --version Only print version information. --verbose Display more information. --diagnosis Display lots of diagnosis information. --generator=GENERATOR Set the project generator. (default: '"${project_generator}"') - gmake - ninja --make=MAKE Set the make program. (default: '"${_make_program_default}"') --ninja=NINJA Set the Ninja program. (default: '"${_ninja_program_default}"') --plat=PLAT Compile for the given platform. (default: '"${_target_plat_default}"') - msys - cross - bsd - mingw - macosx - linux - wasm --arch=ARCH Compile for the given architecture. (default: '"${_target_arch_default}"') - msys: i386 x86_64 - cross: i386 x86_64 arm arm64 mips mips64 riscv riscv64 loong64 s390x ppc ppc64 sh4 - bsd: i386 x86_64 - mingw: i386 x86_64 arm arm64 - macosx: x86_64 arm64 - linux: i386 x86_64 armv7 armv7s arm64-v8a mips mips64 mipsel mips64el --mode=MODE Set the given compilation mode. (default: '"${_target_mode_default}"') - release - debug --kind=KIND Set the given target kind. (default: '"${_target_kind_default}"') - static - shared - binary --toolchain=TOOLCHAIN Set toolchain name. - clang - gcc - emcc - tinycc - cosmocc --builddir=DIR Set build directory. (default: '"${xmake_sh_builddir}"') Autoconf options: --build=BUILD Configure for building on BUILD [guessed] --host=HOST Cross-compile to build programs to run on HOST [BUILD] --prefix=PREFIX Set install files directory in tree rooted at PREFIX. (default: '"${_install_prefix_default}"') --bindir=DIR Set install binaries directory in PREFIX/DIR. (default: '"${_install_bindir_default}"') --libdir=DIR Set install libraries directory in PREFIX/DIR. (default: '"${_install_libdir_default}"') --includedir=DIR Set install includes directory in PREFIX/DIR. (default: '"${_install_includedir_default}"') Project options: '"$(_show_options_usage)"' ' exit 1 } # show xmake.sh version _show_version() { echo "xmake.sh v${xmake_sh_version}, A script-only build utility like autotools" echo "${xmake_sh_copyright}" echo ' _ _ ' echo " __ ___ __ __ __ _| | ______ ___| |__ " echo " \ \/ / | \/ |/ _ | |/ / __ \ / __| '_ \ " echo " > < | \__/ | /_| | < ___/_\__ \ | | | " echo " /_/\_\_|_| |_|\__ \|_|\_\____(_)___/_| |_| " echo ' by ruki, xmake.io' echo ' ' echo ' 👉 Manual: https://xmake.io/guide/quick-start ' echo ' 🙏 Donate: https://xmake.io/about/sponsor ' echo ' ' exit 2 } # --foo=yes => foo _parse_argument_name() { local arg="${1#--}" case "${arg}" in *=*) arg="${arg%%=*}" ;; esac string_replace "${arg}" "-" "_" } # --foo=yes => yes _parse_argument_value() { case "${1}" in *=*) _ret="${1#*=}" ;; *) _ret="" ;; esac } # parse input arguments _handle_option() { _parse_argument_name ${1}; local name="${_ret}" _parse_argument_value ${1}; local value="${_ret}" if test_eq "${name}" "help"; then _show_usage return 0 elif test_eq "${name}" "version"; then _show_version return 0 elif test_eq "${name}" "verbose"; then xmake_sh_verbose=true return 0 elif test_eq "${name}" "diagnosis"; then xmake_sh_diagnosis=true return 0 elif test_eq "${name}" "plat"; then _target_plat=${value} return 0 elif test_eq "${name}" "arch"; then _target_arch=${value} return 0 elif test_eq "${name}" "mode"; then _target_mode=${value} return 0 elif test_eq "${name}" "kind"; then _target_kind=${value} return 0 elif test_eq "${name}" "toolchain"; then _target_toolchain=${value} return 0 elif test_eq "${name}" "generator"; then project_generator=${value} return 0 elif test_eq "${name}" "make"; then _make_program=${value} return 0 elif test_eq "${name}" "ninja"; then _ninja_program=${value} return 0 elif test_eq "${name}" "prefix"; then _install_prefix_default="${value}" return 0 elif test_eq "${name}" "bindir"; then _install_bindir_default="${value}" return 0 elif test_eq "${name}" "libdir"; then _install_libdir_default="${value}" return 0 elif test_eq "${name}" "includedir"; then _install_includedir_default="${value}" return 0 elif test_eq "${name}" "builddir" || test_eq "${name}" "buildir"; then xmake_sh_builddir="${value}" return 0 elif test_eq "${name}" "build"; then _autoconf_build_type="${value}" return 0 elif test_eq "${name}" "host"; then _autoconf_host_type="${value}" return 0 elif _has_option "${name}"; then _set_option_value "${name}" "${value}" return 0 fi return 1 } while test $# != 0; do if ! _handle_option ${1}; then wprint "unknown option: $1" fi shift done #----------------------------------------------------------------------------- # handle some autoconf configurations # # parse triplet # https://github.com/xmake-io/xmake/issues/3869 # e.g. i686-linux-gnu, aarch64-apple-darwin, x86_64-w64-mingw32, i686-redhat-linux-gnu _parse_triplet() { local triplet="${1}" string_split "${triplet}" "-" } _get_arch_from_cpu() { local cpu="${1}" case "${cpu}" in i686) _ret="i386";; i386) _ret="i386";; x86_64) _ret="x86_64";; aarch64) _ret="arm64";; arm64) _ret="arm64";; arm*) _ret="arm";; *) _ret="${cpu}";; esac } _get_plat_from_vendor_os() { local vendor="${1}" local os="${2}" case "${vendor}" in linux) if string_contains "${os}" "android"; then _ret="android" else _ret="linux" fi ;; apple) if test_eq "${os}" "darwin"; then _ret="macosx" fi ;; w64) _ret="mingw";; *) _ret="${os}";; esac } _handle_autoconf_configs() { if test_z "${_autoconf_host_type}"; then _autoconf_host_type="${_autoconf_build_type}" fi if test_nz "${_autoconf_build_type}"; then _parse_triplet "${_autoconf_build_type}"; local cpu="${_ret}"; local vendor="${_ret2}"; local os="${_ret3}" _get_arch_from_cpu "${cpu}" if test_nz "${_ret}"; then os_arch="${_ret}" else wprint "unknown cpu: ${cpu} in --build=${value}" fi _get_plat_from_vendor_os "${vendor}" "${os}" if test_nz "${_ret}"; then os_host="${_ret}" else wprint "unknown vendor-os: ${vendor}-${os} in --build=${value}" fi fi if test_nz "${_autoconf_host_type}"; then _parse_triplet "${_autoconf_host_type}"; local cpu="${_ret}"; local vendor="${_ret2}"; local os="${_ret3}" _get_arch_from_cpu "${cpu}" if test_nz "${_ret}"; then _target_arch_default="${_ret}" else wprint "unknown cpu: ${cpu} in --host=${value}" fi _get_plat_from_vendor_os "${vendor}" "${os}" if test_nz "${_ret}"; then _target_plat_default="${_ret}" else wprint "unknown vendor-os: ${vendor}-${os} in --build=${value}" fi fi } _handle_autoconf_configs #----------------------------------------------------------------------------- # detect platform and toolchains # # envs toolchain toolchain "envs" set_toolset "as" "$CC" "$CXX" "$AS" set_toolset "cc" "$CC" set_toolset "cxx" "$CC" "$CXX" set_toolset "mm" "$CC" "$CXX" set_toolset "mxx" "$CC" "$CXX" set_toolset "ld" "$CXX" "$CC" "$LD" set_toolset "sh" "$CXX" "$CC" "$LD" set_toolset "ar" "$AR" "ar" toolchain_end # clang toolchain toolchain "clang" set_toolset "as" "clang" set_toolset "cc" "clang" set_toolset "cxx" "clang" "clang++" set_toolset "mm" "clang" set_toolset "mxx" "clang" "clang++" set_toolset "ld" "clang++" "clang" set_toolset "sh" "clang++" "clang" set_toolset "ar" "ar" toolchain_end # gcc toolchain toolchain "gcc" set_toolset "as" "gcc" set_toolset "cc" "gcc" set_toolset "cxx" "gcc" "g++" set_toolset "mm" "gcc" set_toolset "mxx" "gcc" "g++" set_toolset "ld" "g++" "gcc" set_toolset "sh" "g++" "gcc" set_toolset "ar" "ar" toolchain_end # mingw toolchain (x86_64) toolchain "x86_64_w64_mingw32" set_toolset "as" "x86_64-w64-mingw32-gcc" set_toolset "cc" "x86_64-w64-mingw32-gcc" set_toolset "cxx" "x86_64-w64-mingw32-gcc" "x86_64-w64-mingw32-g++" set_toolset "mm" "x86_64-w64-mingw32-gcc" set_toolset "mxx" "x86_64-w64-mingw32-gcc" "x86_64-w64-mingw32-g++" set_toolset "ld" "x86_64-w64-mingw32-g++" "x86_64-w64-mingw32-gcc" set_toolset "sh" "x86_64-w64-mingw32-g++" "x86_64-w64-mingw32-gcc" set_toolset "ar" "x86_64-w64-mingw32-ar" "ar" toolchain_end # mingw toolchain (i686) toolchain "i686_w64_mingw32" set_toolset "as" "i686-w64-mingw32-gcc" set_toolset "cc" "i686-w64-mingw32-gcc" set_toolset "cxx" "i686-w64-mingw32-gcc" "i686-w64-mingw32-g++" set_toolset "mm" "i686-w64-mingw32-gcc" set_toolset "mxx" "i686-w64-mingw32-gcc" "i686-w64-mingw32-g++" set_toolset "ld" "i686-w64-mingw32-g++" "i686-w64-mingw32-gcc" set_toolset "sh" "i686-w64-mingw32-g++" "i686-w64-mingw32-gcc" set_toolset "ar" "i686-w64-mingw32-ar" "ar" toolchain_end # aarch64 toolchain (aarch64) toolchain "aarch64_linux_gnu" set_toolset "as" "aarch64-linux-gnu-gcc" set_toolset "cc" "aarch64-linux-gnu-gcc" set_toolset "cxx" "aarch64-linux-gnu-gcc" "aarch64-linux-gnu-g++" set_toolset "mm" "aarch64-linux-gnu-gcc" set_toolset "mxx" "aarch64-linux-gnu-gcc" "aarch64-linux-gnu-g++" set_toolset "ld" "aarch64-linux-gnu-g++" "aarch64-linux-gnu-gcc" set_toolset "sh" "aarch64-linux-gnu-g++" "aarch64-linux-gnu-gcc" set_toolset "ar" "aarch64-linux-gnu-ar" "ar" toolchain_end # emcc toolchain (wasm32) toolchain "emcc" set_toolset "as" "emcc" set_toolset "cc" "emcc" set_toolset "cxx" "emcc" "em++" set_toolset "mm" "emcc" set_toolset "mxx" "emcc" "em++" set_toolset "ld" "em++" "emcc" set_toolset "sh" "em++" "emcc" set_toolset "ar" "emar" "ar" toolchain_end # cosmocc toolchain, e.g. ./configure --plat=linux --toolchain=cosmocc toolchain "cosmocc" set_toolset "as" "cosmocc" set_toolset "cc" "cosmocc" set_toolset "cxx" "cosmocc" "cosmoc++" set_toolset "mm" "cosmocc" set_toolset "mxx" "cosmocc" "cosmoc++" set_toolset "ld" "cosmoc++" "cosmocc" set_toolset "sh" "cosmoc++" "cosmocc" set_toolset "ar" "cosmoar" toolchain_end # tinycc toolchain toolchain "tinycc" set_toolset "as" "tcc" "gcc" "clang" set_toolset "cc" "tcc" set_toolset "cxx" "gcc" "g++" "clang" "clang++" set_toolset "mm" "gcc" "clang" set_toolset "mxx" "gcc" "g++" "clang" "clang++" set_toolset "ld" "tcc" set_toolset "sh" "tcc" set_toolset "ar" "tcc -ar" "ar" toolchain_end # check platform _check_platform() { if test "x${_target_plat}" = "x"; then _target_plat=${_target_plat_default} fi if test "x${_target_arch}" = "x"; then _target_arch=${_target_arch_default} fi if test "x${_target_mode}" = "x"; then _target_mode=${_target_mode_default} fi if test "x${_target_kind}" = "x"; then _target_kind=${_target_kind_default} fi echo "checking for platform ... ${_target_plat}" echo "checking for architecture ... ${_target_arch}" } # get toolchain compile command for gcc/clang _toolchain_compcmd_for_gcc_clang() { local program="${1}" local objectfile="${2}" local sourcefile="${3}" local flags="${4}" _ret="${program} -c ${flags} -o ${objectfile} ${sourcefile}" } # get toolchain link command for gcc/clang _toolchain_linkcmd_for_gcc_clang() { local toolkind="${1}" local program="${2}" local binaryfile="${3}" local objectfiles="${4}" local flags="${5}" if test_eq "${toolkind}" "sh"; then flags="-shared -fPIC ${flags}" fi _ret="${program} -o ${binaryfile} ${objectfiles} ${flags}" } # get toolchain link command for ar _toolchain_linkcmd_for_ar() { local toolkind="${1}" local program="${2}" local binaryfile="${3}" local objectfiles="${4}" local flags="${5}" _ret="${program} ${flags} ${binaryfile} ${objectfiles}" } # get toolchain compile command _toolchain_compcmd() { local sourcekind="${1}" local objectfile="${2}" local sourcefile="${3}" local flags="${4}" _get_toolchain_toolset "${_target_toolchain}" "${sourcekind}"; local program="${_ret}" path_toolname "${program}"; local toolname="${_ret}" local compcmd="" case "${toolname}" in gcc) _toolchain_compcmd_for_gcc_clang "${program}" "${objectfile}" "${sourcefile}" "${flags}"; compcmd="${_ret}";; gxx) _toolchain_compcmd_for_gcc_clang "${program}" "${objectfile}" "${sourcefile}" "${flags}"; compcmd="${_ret}";; clang) _toolchain_compcmd_for_gcc_clang "${program}" "${objectfile}" "${sourcefile}" "${flags}"; compcmd="${_ret}";; clangxx) _toolchain_compcmd_for_gcc_clang "${program}" "${objectfile}" "${sourcefile}" "${flags}"; compcmd="${_ret}";; emcc) _toolchain_compcmd_for_gcc_clang "${program}" "${objectfile}" "${sourcefile}" "${flags}"; compcmd="${_ret}";; emxx) _toolchain_compcmd_for_gcc_clang "${program}" "${objectfile}" "${sourcefile}" "${flags}"; compcmd="${_ret}";; cosmocc) _toolchain_compcmd_for_gcc_clang "${program}" "${objectfile}" "${sourcefile}" "${flags}"; compcmd="${_ret}";; cosmocxx) _toolchain_compcmd_for_gcc_clang "${program}" "${objectfile}" "${sourcefile}" "${flags}"; compcmd="${_ret}";; tcc) _toolchain_compcmd_for_gcc_clang "${program}" "${objectfile}" "${sourcefile}" "${flags}"; compcmd="${_ret}";; *) raise "unknown toolname(${toolname})!" ;; esac _ret="${compcmd}" } # get toolchain link command _toolchain_linkcmd() { local toolkind="${1}" local binaryfile="${2}" local objectfiles="${3}" local flags="${4}" _get_toolchain_toolset "${_target_toolchain}" "${toolkind}"; local program="${_ret}" path_toolname "${program}"; local toolname="${_ret}" case "${toolname}" in gcc) _toolchain_linkcmd_for_gcc_clang "${toolkind}" "${program}" "${binaryfile}" "${objectfiles}" "${flags}"; linkcmd="${_ret}";; gxx) _toolchain_linkcmd_for_gcc_clang "${toolkind}" "${program}" "${binaryfile}" "${objectfiles}" "${flags}"; linkcmd="${_ret}";; clang) _toolchain_linkcmd_for_gcc_clang "${toolkind}" "${program}" "${binaryfile}" "${objectfiles}" "${flags}"; linkcmd="${_ret}";; clangxx) _toolchain_linkcmd_for_gcc_clang "${toolkind}" "${program}" "${binaryfile}" "${objectfiles}" "${flags}"; linkcmd="${_ret}";; emcc) _toolchain_linkcmd_for_gcc_clang "${toolkind}" "${program}" "${binaryfile}" "${objectfiles}" "${flags}"; linkcmd="${_ret}";; emxx) _toolchain_linkcmd_for_gcc_clang "${toolkind}" "${program}" "${binaryfile}" "${objectfiles}" "${flags}"; linkcmd="${_ret}";; cosmocc) _toolchain_linkcmd_for_gcc_clang "${toolkind}" "${program}" "${binaryfile}" "${objectfiles}" "${flags}"; linkcmd="${_ret}";; cosmocxx) _toolchain_linkcmd_for_gcc_clang "${toolkind}" "${program}" "${binaryfile}" "${objectfiles}" "${flags}"; linkcmd="${_ret}";; tcc) _toolchain_linkcmd_for_gcc_clang "${toolkind}" "${program}" "${binaryfile}" "${objectfiles}" "${flags}"; linkcmd="${_ret}";; ar) _toolchain_linkcmd_for_ar "${toolkind}" "${program}" "${binaryfile}" "${objectfiles}" "${flags}"; linkcmd="${_ret}";; emar) _toolchain_linkcmd_for_ar "${toolkind}" "${program}" "${binaryfile}" "${objectfiles}" "${flags}"; linkcmd="${_ret}";; cosmoar) _toolchain_linkcmd_for_ar "${toolkind}" "${program}" "${binaryfile}" "${objectfiles}" "${flags}"; linkcmd="${_ret}";; *) raise "unknown toolname(${toolname})!" ;; esac _ret="${linkcmd}" } # try make _toolchain_try_make() { local program="${1}" if _os_runv "${program}" "--version"; then return 0 fi return 1 } # try ninja _toolchain_try_ninja() { local program="${1}" if _os_runv "${program}" "--version"; then return 0 fi return 1 } # try gcc _toolchain_try_gcc() { if test "x${_toolchain_try_gcc_result}" = "xok"; then return 0 elif test "x${_toolchain_try_gcc_result}" = "xno"; then return 1 fi local kind="${1}" local program="${2}" if _os_runv "${program}" "--version"; then _toolchain_try_gcc_result="ok" return 0 fi _toolchain_try_gcc_result="no" return 1 } # try g++ _toolchain_try_gxx() { if test "x${_toolchain_try_gxx_result}" = "xok"; then return 0 elif test "x${_toolchain_try_gxx_result}" = "xno"; then return 1 fi local kind="${1}" local program="${2}" if _os_runv "${program}" "--version"; then _toolchain_try_gxx_result="ok" return 0 fi _toolchain_try_gxx_result="no" return 1 } # try clang _toolchain_try_clang() { if test "x${_toolchain_try_clang_result}" = "xok"; then return 0 elif test "x${_toolchain_try_clang_result}" = "xno"; then return 1 fi local kind="${1}" local program="${2}" if _os_runv "${program}" "--version"; then _toolchain_try_clang_result="ok" return 0 fi _toolchain_try_clang_result="no" return 1 } # try clang++ _toolchain_try_clangxx() { if test "x${_toolchain_try_clangxx_result}" = "xok"; then return 0 elif test "x${_toolchain_try_clangxx_result}" = "xno"; then return 1 fi local kind="${1}" local program="${2}" if _os_runv "${program}" "--version"; then _toolchain_try_clangxx_result="ok" return 0 fi _toolchain_try_clangxx_result="no" return 1 } # try tcc _toolchain_try_tcc() { if test "x${_toolchain_try_tcc_result}" = "xok"; then return 0 elif test "x${_toolchain_try_tcc_result}" = "xno"; then return 1 fi local kind="${1}" local program="${2}" if _os_runv "${program}" "-v"; then _toolchain_try_tcc_result="ok" return 0 fi _toolchain_try_tcc_result="no" return 1 } # try ar _toolchain_try_ar() { local kind="${1}" local program="${2}" # generate the source file _os_tmpfile local tmpfile="${_ret}" local objectfile="${tmpfile}.o" local libraryfile="${tmpfile}.a" echo "" > "${objectfile}" # try linking it local ok=false if _os_runv "${program}" "-cr" "${libraryfile}" "${objectfile}"; then ok=true fi # remove files _os_tryrm "${objectfile}" _os_tryrm "${libraryfile}" if ${ok}; then return 0 fi return 1 } # try cosmoar _toolchain_try_cosmoar() { if test "x${_toolchain_try_cosmoar_result}" = "xok"; then return 0 elif test "x${_toolchain_try_cosmoar_result}" = "xno"; then return 1 fi local kind="${1}" local program="${2}" if _os_runv "${program}" "--version"; then _toolchain_try_cosmoar_result="ok" return 0 fi _toolchain_try_cosmoar_result="no" return 1 } # try program _toolchain_try_program() { local toolchain="${1}" local kind="${2}" local program="${3}" local ok=false path_toolname "${program}"; local toolname="${_ret}" case "${toolname}" in gcc) _toolchain_try_gcc "${kind}" "${program}" && ok=true;; gxx) _toolchain_try_gxx "${kind}" "${program}" && ok=true;; clang) _toolchain_try_clang "${kind}" "${program}" && ok=true;; clangxx) _toolchain_try_clangxx "${kind}" "${program}" && ok=true;; emcc) _toolchain_try_clang "${kind}" "${program}" && ok=true;; emxx) _toolchain_try_clangxx "${kind}" "${program}" && ok=true;; cosmocc) _toolchain_try_gcc "${kind}" "${program}" && ok=true;; cosmocxx) _toolchain_try_gxx "${kind}" "${program}" && ok=true;; tcc) _toolchain_try_tcc "${kind}" "${program}" && ok=true;; ar) _toolchain_try_ar "${kind}" "${program}" && ok=true;; emar) _toolchain_try_ar "${kind}" "${program}" && ok=true;; cosmoar) _toolchain_try_cosmoar "${kind}" "${program}" && ok=true;; *) raise "unknown toolname(${toolname})!" ;; esac if ${ok}; then vprint "checking for ${program} ... ok" return 0 fi vprint "checking for ${program} ... no" return 1 } # try toolset _toolchain_try_toolset() { local toolchain=${1} local kind=${2} local description=${3} local indices="0 1 2 3 4 5" for idx in ${indices}; do local key="${kind}" if test_nq "${idx}" "0"; then key="${key}_${idx}" fi _get_toolchain_toolset "${toolchain}" "${key}"; local program="${_ret}" if test_nz "${program}"; then if _toolchain_try_program "${toolchain}" "${kind}" "${program}"; then _set_toolchain_toolset "${toolchain}" "${kind}" "${program}" echo "checking for the ${description} (${kind}) ... ${program}" return 0 fi fi done return 1 } # try toolchain _toolchain_try() { local toolchain=${1} vprint "checking for $toolchain toolchain ..." if _toolchain_try_toolset "${toolchain}" "cc" "c compiler" && _toolchain_try_toolset "${toolchain}" "cxx" "c++ compiler" && _toolchain_try_toolset "${toolchain}" "as" "assembler" && _toolchain_try_toolset "${toolchain}" "mm" "objc compiler" && _toolchain_try_toolset "${toolchain}" "mxx" "objc++ compiler" && _toolchain_try_toolset "${toolchain}" "ld" "linker" && _toolchain_try_toolset "${toolchain}" "ar" "static library archiver" && _toolchain_try_toolset "${toolchain}" "sh" "shared library linker"; then return 0 fi return 1 } # detect make _toolchain_detect_make() { if test "x${_make_program}" = "x"; then _make_program=${_make_program_default} fi if _toolchain_try_make "${_make_program}"; then echo "checking for make ... ok" else echo "checking for make ... no" raise "make not found!" fi } # detect ninja _toolchain_detect_ninja() { if test "x${_ninja_program}" = "x"; then _ninja_program=${_ninja_program_default} fi if _toolchain_try_ninja "${_ninja_program}"; then echo "checking for ninja ... ok" else echo "checking for ninja ... no" raise "ninja not found!" fi } # detect build backend _toolchain_detect_backend() { if test "x${project_generator}" = "xgmake"; then _toolchain_detect_make elif test "x${project_generator}" = "xninja"; then _toolchain_detect_ninja fi } # detect toolchain _toolchain_detect() { # detect build backend _toolchain_detect_backend # detect toolchains local toolchains="${1}" if test "x${toolchains}" = "x"; then if is_plat "macosx"; then toolchains="envs clang gcc" elif is_plat "mingw"; then if is_arch "i386"; then toolchains="i686_w64_mingw32" else toolchains="x86_64_w64_mingw32" fi elif is_plat "wasm"; then toolchains="emcc" elif is_plat "linux" && ! is_arch "${os_arch}"; then toolchains="envs" if is_arch "arm64"; then toolchains="${toolchains} aarch64_linux_gnu" fi else toolchains="envs gcc clang" fi fi for toolchain in ${toolchains}; do if _toolchain_try "$toolchain"; then _target_toolchain=${toolchain} break fi done } # check toolchain _check_toolchain() { local toolchain=${_target_toolchain} _target_toolchain="" _toolchain_detect ${toolchain} if test "x${_target_toolchain}" != "x"; then echo "checking for toolchain ... ${_target_toolchain}" else echo "checking for toolchain ... no" raise "toolchain not found!" fi } # get function code # # sigsetjmp # sigsetjmp((void*)0, 0) # _get_funccode() { local func="${1}" local code="" if string_contains "${func}" "("; then code="${func}" else code="typedef void (*func_t)(); volatile func_t p${func} = (func_t)${func}; while (p${func}) {break;};" fi _ret="${code}" } # generate cxsnippets source code _generate_cxsnippets_sourcecode() { local funcs="${1}" local includes="${2}" local types="${3}" local snippets="${4}" local snippet_includes="" for include in $includes; do snippet_includes="${snippet_includes}#include \"${include}\"\n" done local snippet_types="" for type in $types; do string_replace "${type}" '[^a-zA-Z]' "_"; local typevar="${_ret}" snippet_types="${snippet_types}typedef ${type} __type_${typevar};\n" done local snippet_funcs="" for func in $funcs; do _get_funccode "${func}"; func="${_ret}" snippet_funcs="${snippet_funcs}${func}\n " done local snippets_code="" if test_nz "${snippet_includes}"; then snippets_code="${snippets_code}${snippet_includes}\n" fi if test_nz "${snippet_types}"; then snippets_code="${snippets_code}${snippet_types}\n" fi if test_nz "${snippets}"; then snippets_code="${snippets_code}${snippets}\n" fi _ret=' '"${snippets_code}"'int main(int argc, char** argv) { '"${snippet_funcs}"' return 0; }' } # check cxsnippets _check_cxsnippets() { local name="${1}" local kind="${2}" _get_option_item "${name}" "${kind}funcs"; local funcs="${_ret}" _get_option_item "${name}" "${kind}includes"; local includes="${_ret}" _get_option_item "${name}" "${kind}types"; local types="${_ret}" _get_option_item "${name}" "${kind}snippets"; local snippets="${_ret}" if test_z "${funcs}" && test_z "${includes}" && test_z "${types}" && test_z "${snippets}"; then return 0 fi # get c/c++ extension local extension=".c" local sourcekind="cc" if test_eq "${kind}" "cxx"; then extension=".cpp" sourcekind="cxx" fi # generate source code _generate_cxsnippets_sourcecode "${funcs}" "${includes}" "${types}" "${snippets}"; local sourcecode="${_ret}" dprint "${sourcecode}" # generate the source file _os_tmpfile local tmpfile="${_ret}" local sourcefile="${tmpfile}${extension}" local objectfile="${tmpfile}.o" local binaryfile="${tmpfile}.bin" print "${sourcecode}" > "${sourcefile}" # try compiling it local ok=false if ! ${ok}; then local compflags="" _get_toolchain_toolset "${_target_toolchain}" "${sourcekind}"; local program="${_ret}" path_toolname "${program}"; local toolname="${_ret}" local itemnames="languages warnings optimizes defines undefines includedirs" for itemname in ${itemnames}; do _get_option_abstract_flags "${name}" "${sourcekind}" "${toolname}" "${itemname}"; local flags="${_ret}" if test_nz "${flags}"; then _split_flags "${flags}"; flags="${_ret}" compflags="${compflags} ${flags}" fi done local flagnames="cxflags" if test_eq "${sourcekind}" "cxx"; then flagnames="${flagnames} cxxflags" else flagnames="${flagnames} cflags" fi for flagname in $flagnames; do _get_option_item "${name}" "${flagname}"; local flags="${_ret}" if test_nz "${flags}"; then compflags="${compflags} ${flags}" fi done if test_eq "${sourcekind}" "cxx"; then if test_nz "${CXXFLAGS}"; then compflags="${compflags} ${CXXFLAGS}" fi else if test_nz "${CFLAGS}"; then compflags="${compflags} ${CFLAGS}" fi fi if test_nz "${CPPFLAGS}"; then compflags="${compflags} ${CPPFLAGS}" fi _toolchain_compcmd "${sourcekind}" "${objectfile}" "${sourcefile}" "${compflags}"; local compcmd="${_ret}" if ${xmake_sh_diagnosis}; then print "> ${compcmd}" fi if _os_runv ${compcmd}; then ok=true fi fi # try linking it _get_option_item "${name}" "links"; local links="${_ret}" _get_option_item "${name}" "syslinks"; local syslinks="${_ret}" _get_option_item "${name}" "ldflags"; local ldflags="${_ret}" if test_nz "${syslinks}"; then links="${links} ${syslinks}" fi if ${ok} && (test_nz "${links}" || test_nz "${ldflags}"); then local toolkind="ld" _get_toolchain_toolset "${_target_toolchain}" "${toolkind}"; local program="${_ret}" path_toolname "${program}"; local toolname="${_ret}" local itemnames="linkdirs links syslinks" local linkflags="" for itemname in ${itemnames}; do _get_option_abstract_flags "${name}" "${toolkind}" "${toolname}" "${itemname}"; local flags="${_ret}" if test_nz "${flags}"; then _split_flags "${flags}"; flags="${_ret}" linkflags="${linkflags} ${flags}" fi done _get_option_item "${name}" "ldflags"; local flags="${_ret}" if test_nz "${flags}"; then linkflags="${linkflags} ${flags}" fi if test_nz "${LDFLAGS}"; then linkflags="${linkflags} ${LDFLAGS}" fi _toolchain_linkcmd "${toolkind}" "${binaryfile}" "${objectfile}" "${linkflags}"; local linkcmd="${_ret}" if ${xmake_sh_diagnosis}; then print "> ${linkcmd}" fi if _os_runv ${linkcmd}; then ok=true else ok=false fi fi # trace if ${xmake_sh_verbose} || ${xmake_sh_diagnosis}; then if test_nz "${includes}"; then print "> checking for ${kind} includes(${includes})" fi if test_nz "${types}"; then print "> checking for ${kind} types(${types})" fi if test_nz "${funcs}"; then print "> checking for ${kind} funcs(${funcs})" fi if test_nz "${links}"; then print "> checking for ${kind} links(${links})" fi fi # remove files _os_tryrm "${sourcefile}" _os_tryrm "${objectfile}" _os_tryrm "${binaryfile}" if ${ok}; then return 0 fi return 1 } # check csnippets _check_csnippets() { local name="${1}" if _check_cxsnippets "${name}" "c"; then return 0 fi return 1 } # check cxxsnippets _check_cxxsnippets() { local name="${1}" if _check_cxsnippets "${name}" "cxx"; then return 0 fi return 1 } # check option _check_option() { local name="${1}" _get_option_value "${name}"; local value="${_ret}" _get_option_item "${name}" "default"; local default="${_ret}" if test_nz "${value}"; then if _is_enabled "${value}"; then return 0 else return 1 fi elif test_nz "${default}"; then if _is_enabled "${default}"; then return 0 else return 1 fi else _get_option_item "${name}" "before_check"; local before_check="${_ret}" if test_nz "${before_check}"; then eval ${before_check} fi if _check_csnippets "${name}" && _check_cxxsnippets "${name}"; then return 0 fi fi return 1 } # check options _check_options() { _get_options_for_checking; local options="${_ret}" for name in $options; do if _check_option "$name"; then echo "checking for ${name} .. ok" _set_option_value "${name}" true else echo "checking for ${name} .. no" _set_option_value "${name}" false fi done } # check all _check_all() { _check_platform _check_toolchain _check_options } _check_all #----------------------------------------------------------------------------- # init builtin variables, e.g. add_headerfiles "${builddir}/config.h" # projectdir="${xmake_sh_projectdir}" if path_is_absolute "${xmake_sh_builddir}"; then builddir="${xmake_sh_builddir}" else builddir="${xmake_sh_projectdir}/${xmake_sh_builddir}" fi buildir=${builddir} # deprecated plat="${_target_plat}" arch="${_target_arch}" mode="${_target_mode}" kind="${_target_kind}" #----------------------------------------------------------------------------- # load project targets # # load targets _load_targets() { echo "analyzing project configuration .." _loading_options=false _loading_toolchains=false _loading_targets=true _xmake_sh_option_current="" _xmake_sh_target_current="" _xmake_sh_toolchain_current="" local file=${xmake_sh_projectdir}/xmake.sh if test -f "${file}"; then includes "${file}" else # include all xmake.sh files in next sub-directories _os_find "${xmake_sh_projectdir}" "xmake.sh" 2; local files="${_ret}" for file in ${files}; do includes "${file}" done fi } _load_targets # get toolset kinds for all targets # e.g. cc cxx as mm mxx ld sh ar _get_targets_toolkinds() { if test_z "${_targets_toolkinds_dedup}"; then _dedup "${_targets_toolkinds}"; _targets_toolkinds_dedup="${_ret}" fi _ret="${_targets_toolkinds_dedup}" } #----------------------------------------------------------------------------- # generate configfiles # # vprint config variable in `${name}` _vprint_configvar_value() { local name="${1}" local value="${2}" vprint " > replace ${name} -> ${value}" } # vprint config variable in `${define name}` _vprint_configvar_define() { local name="${1}" local value="${2}" if test_z "${value}"; then vprint " > replace ${name} -> /* #undef ${name} */" elif test_eq "${value}" "1" || test_eq "${value}" "true"; then vprint " > replace ${name} -> #define ${name} 1" elif test_eq "${value}" "0" || test_eq "${value}" "false"; then vprint " > replace ${name} -> /*#define ${name} 0*/" else vprint " > replace ${name} -> #define ${name} ${value}" fi } # replace config variable in `${define name}` _replace_configvar_define() { local name="${1}" local value="${2}" if test_z "${value}"; then _ret="s/\${define ${name}}/\/*#undef ${name}*\//g" elif test_eq "${value}" "1" || test_eq "${value}" "true"; then _ret="s/\${define ${name}}/#define ${name} 1/g" elif test_eq "${value}" "0" || test_eq "${value}" "false"; then _ret="s/\${define ${name}}/\/*#define ${name} 0*\//g" else _ret="s/\${define ${name}}/#define ${name} ${value}/g" fi } # replace config variable in `${name}` _replace_configvar_value() { local name="${1}" local value="${2}" _ret="s@\${${name}}@${value}@g" } # generate configfile for the given target _generate_configfile() { local target="${1}" local configfile_in="${2}" _get_target_item "${target}" "configdir"; local configdir="${_ret}" if test_z "${configdir}"; then path_directory configfile_in; configdir="${_ret}" fi if ! test -d "${configdir}"; then mkdir -p "${configdir}" fi path_basename "${configfile_in}"; local filename="${_ret}" local configfile="${configdir}/${filename}" echo "generating ${configfile} .." # replace builtin variables local patterns="" local target_os="" if is_plat "mingw"; then target_os="windows" else target_os="${_target_plat}" fi string_toupper ${target_os}; target_os="${_ret}" _vprint_configvar_value "OS" "${target_os}" _replace_configvar_value "OS" "${target_os}"; patterns="${_ret};${patterns}" # replace version _get_target_item "${target}" "version"; local version="${_ret}" _get_target_item "${target}" "version_build"; local version_build="${_ret}" string_split "${version}" "." local version_major="${_ret}" local version_minor="${_ret2}" local version_alter="${_ret3}" if test_nz "${version}"; then _vprint_configvar_value "VERSION" "${version}" _replace_configvar_value "VERSION" "${version}"; patterns="${_ret};${patterns}" fi if test_nz "${version_major}"; then _vprint_configvar_value "VERSION_MAJOR" "${version_major}" _replace_configvar_value "VERSION_MAJOR" "${version_major}"; patterns="${_ret};${patterns}" fi if test_nz "${version_minor}"; then _vprint_configvar_value "VERSION_MINOR" "${version_minor}" _replace_configvar_value "VERSION_MINOR" "${version_minor}"; patterns="${_ret};${patterns}" fi if test_nz "${version_alter}"; then _vprint_configvar_value "VERSION_ALTER" "${version_alter}" _replace_configvar_value "VERSION_ALTER" "${version_alter}"; patterns="${_ret};${patterns}" fi if test_nz "${version_build}"; then _os_date "${version_build}"; version_build="${_ret}" _vprint_configvar_value "VERSION_BUILD" "${version_build}" _replace_configvar_value "VERSION_BUILD" "${version_build}"; patterns="${_ret};${patterns}" fi # replace git variables local content="" content=$(cat "${configfile_in}") if string_contains "${content}" "GIT_"; then _os_iorunv "git" "describe" "--tags"; local git_tag="${_ret}" _vprint_configvar_value "GIT_TAG" "${git_tag}" _replace_configvar_value "GIT_TAG" "${git_tag}"; patterns="${_ret};${patterns}" _os_iorunv "git" "describe" "--tags" "--long"; local git_tag_long="${_ret}" _vprint_configvar_value "GIT_TAG_LONG" "${git_tag_long}" _replace_configvar_value "GIT_TAG_LONG" "${git_tag_long}"; patterns="${_ret};${patterns}" _os_iorunv "git" "rev-parse" "--abbrev-ref" "HEAD"; local git_branch="${_ret}" _vprint_configvar_value "GIT_BRANCH" "${git_branch}" _replace_configvar_value "GIT_BRANCH" "${git_branch}"; patterns="${_ret};${patterns}" _os_iorunv "git" "rev-parse" "--short" "HEAD"; local git_commit="${_ret}" _vprint_configvar_value "GIT_COMMIT" "${git_commit}" _replace_configvar_value "GIT_COMMIT" "${git_commit}"; patterns="${_ret};${patterns}" _os_iorunv "git" "rev-parse" "HEAD"; local git_commit_long="${_ret}" _vprint_configvar_value "GIT_COMMIT_LONG" "${git_commit_long}" _replace_configvar_value "GIT_COMMIT_LONG" "${git_commit_long}"; patterns="${_ret};${patterns}" _os_iorunv "log" "-1" "--date=format:%Y%m%d%H%M%S" "--format=%ad"; local git_commit_date="${_ret}" _vprint_configvar_value "GIT_COMMIT_DATE" "${git_commit_date}" _replace_configvar_value "GIT_COMMIT_DATE" "${git_commit_date}"; patterns="${_ret};${patterns}" fi # replace configvars in target local count=0 local configfile_dst="${configfile}" _os_tmpfile; local tmpfile="${_ret}" cp "${configfile_in}" "${tmpfile}" _get_target_item "${target}" "configvars"; local configvars="${_ret}" for name in ${configvars}; do _get_target_item "${target}" "configvar_${name}"; local value="${_ret}" _vprint_configvar_define "${name}" "${value}" _vprint_configvar_value "${name}" "${value}" _replace_configvar_define "${name}" "${value}"; patterns="${_ret};${patterns}" _replace_configvar_value "${name}" "${value}"; patterns="${_ret};${patterns}" count=$((count + 1)) # do replace if test_eq "$count" "10"; then _io_replace_file "${tmpfile}" "${configfile}" "${patterns}" local swapfile="${tmpfile}" tmpfile="${configfile}" configfile="${swapfile}" patterns="" count=0 fi done # do replace (left) if test_nz "${patterns}"; then _io_replace_file "${tmpfile}" "${configfile}" "${patterns}" local swapfile="${tmpfile}" tmpfile="${configfile}" configfile="${swapfile}" patterns="" count=0 fi # replace fallback patterns='s/${define \(.*\)}/\/*#undef \1*\//g;' _io_replace_file "${tmpfile}" "${configfile}" "${patterns}" if test_nq "${configfile}" "${configfile_dst}"; then cp "${configfile}" "${configfile_dst}" fi echo "${configfile_dst} is generated!" } # generate configfiles _generate_configfiles() { for target in ${_xmake_sh_targets}; do _get_target_item "${target}" "configfiles"; local configfiles="${_ret}" for configfile in ${configfiles}; do _generate_configfile "${target}" "${configfile}" done done } _generate_configfiles #----------------------------------------------------------------------------- # generate gmake file # _gmake_begin() { echo "generating makefile .." } _gmake_add_header() { echo "# this is the build file for this project # it is autogenerated by the xmake.sh build system. # do not edit by hand. " > "${xmake_sh_makefile}" } _gmake_add_switches() { echo "ifneq (\$(VERBOSE),1)" >> "${xmake_sh_makefile}" echo "VV=@" >> "${xmake_sh_makefile}" echo "endif" >> "${xmake_sh_makefile}" echo "" >> "${xmake_sh_makefile}" echo "ifeq (\$(PREFIX),)" >> "${xmake_sh_makefile}" echo "PREFIX=${_install_prefix_default}" >> "${xmake_sh_makefile}" echo "endif" >> "${xmake_sh_makefile}" echo "" >> "${xmake_sh_makefile}" echo "INSTALLDIR:=\$(DESTDIR)" >> "${xmake_sh_makefile}" echo "ifneq (\$(PREFIX),)" >> "${xmake_sh_makefile}" echo "ifneq (\$(INSTALLDIR),)" >> "${xmake_sh_makefile}" echo "PREFIX_:=\$(patsubst /%,%,\$(PREFIX))" >> "${xmake_sh_makefile}" echo "INSTALLDIR:=\$(INSTALLDIR)/\$(PREFIX_)" >> "${xmake_sh_makefile}" echo "else" >> "${xmake_sh_makefile}" echo "INSTALLDIR:=\$(PREFIX)" >> "${xmake_sh_makefile}" echo "endif" >> "${xmake_sh_makefile}" echo "endif" >> "${xmake_sh_makefile}" echo "" >> "${xmake_sh_makefile}" } _gmake_add_flags() { _get_targets_toolkinds; local kinds="${_ret}" for target in ${_xmake_sh_targets}; do for kind in ${kinds}; do _get_target_flags "${target}" "${kind}"; local flags="${_ret}" _get_flagname "${kind}"; local flagname="${_ret}" local key="${target}_${flagname}" echo "${key}=${flags}" >> "${xmake_sh_makefile}" done echo "" >> "${xmake_sh_makefile}" done } _gmake_add_toolchains() { _get_targets_toolkinds; local kinds="${_ret}" for kind in ${kinds}; do _get_toolchain_toolset "${_target_toolchain}" "${kind}"; local program="${_ret}" local key="${kind}" echo "${key}=${program}" >> "${xmake_sh_makefile}" done echo "" >> "${xmake_sh_makefile}" } _gmake_add_build_object_for_gcc_clang() { local kind="${1}" local sourcefile="${2}" local objectfile="${3}" local flagname="${4}" path_directory "${objectfile}"; local objectdir="${_ret}" print "\t@mkdir -p ${objectdir}" >> "${xmake_sh_makefile}" print "\t\$(VV)\$(${kind}) -c \$(${flagname}) -o ${objectfile} ${sourcefile}" >> "${xmake_sh_makefile}" } _gmake_add_build_object() { local target=${1} local sourcefile="${2}" local objectfile="${3}" path_sourcekind "${sourcefile}"; local sourcekind="${_ret}" _get_toolchain_toolset "${_target_toolchain}" "${sourcekind}"; local program="${_ret}" path_toolname "${program}"; local toolname="${_ret}" _get_flagname "${sourcekind}"; local flagname="${_ret}" flagname="${target}_${flagname}" echo "${objectfile}: ${sourcefile}" >> "${xmake_sh_makefile}" print "\t@echo compiling.${_target_mode} ${sourcefile}" >> "${xmake_sh_makefile}" case "${toolname}" in gcc) _gmake_add_build_object_for_gcc_clang "${sourcekind}" "${sourcefile}" "${objectfile}" "${flagname}";; gxx) _gmake_add_build_object_for_gcc_clang "${sourcekind}" "${sourcefile}" "${objectfile}" "${flagname}";; clang) _gmake_add_build_object_for_gcc_clang "${sourcekind}" "${sourcefile}" "${objectfile}" "${flagname}";; clangxx) _gmake_add_build_object_for_gcc_clang "${sourcekind}" "${sourcefile}" "${objectfile}" "${flagname}";; emcc) _gmake_add_build_object_for_gcc_clang "${sourcekind}" "${sourcefile}" "${objectfile}" "${flagname}";; emxx) _gmake_add_build_object_for_gcc_clang "${sourcekind}" "${sourcefile}" "${objectfile}" "${flagname}";; cosmocc) _gmake_add_build_object_for_gcc_clang "${sourcekind}" "${sourcefile}" "${objectfile}" "${flagname}";; cosmocxx) _gmake_add_build_object_for_gcc_clang "${sourcekind}" "${sourcefile}" "${objectfile}" "${flagname}";; tcc) _gmake_add_build_object_for_gcc_clang "${sourcekind}" "${sourcefile}" "${objectfile}" "${flagname}";; *) raise "unknown toolname(${toolname})!" ;; esac echo "" >> "${xmake_sh_makefile}" } _gmake_add_build_objects() { local target=${1} _get_target_sourcefiles "${target}"; local sourcefiles="${_ret}" for sourcefile in ${sourcefiles}; do _get_target_objectfile "${target}" "${sourcefile}"; local objectfile="${_ret}" _gmake_add_build_object "${target}" "${sourcefile}" "${objectfile}" done } _gmake_add_build_target_for_gcc_clang() { local kind="${1}" local targetfile="${2}" local objectfiles="${3}" local flagname="${4}" path_directory "${targetfile}"; local targetdir="${_ret}" print "\t@mkdir -p ${targetdir}" >> "${xmake_sh_makefile}" print "\t\$(VV)\$(${kind}) -o ${targetfile} ${objectfiles} \$(${flagname})" >> "${xmake_sh_makefile}" } _gmake_add_build_target_for_ar() { local kind="${1}" local targetfile="${2}" local objectfiles="${3}" local flagname="${4}" path_directory "${targetfile}"; local targetdir="${_ret}" print "\t@mkdir -p ${targetdir}" >> "${xmake_sh_makefile}" print "\t\$(VV)\$(${kind}) \$(${flagname}) ${flags} ${targetfile} ${objectfiles}" >> "${xmake_sh_makefile}" } _gmake_add_build_target() { local target=${1} _get_targetdir "${target}"; local targetdir="${_ret}" _get_target_file "${target}"; local targetfile="${_ret}" _get_target_item "${target}" "deps"; local deps="${_ret}" _get_target_objectfiles "${target}"; local objectfiles="${_ret}" # get linker _get_target_item "${target}" "kind"; local targetkind="${_ret}" local toolkind="" case "${targetkind}" in binary) toolkind="ld";; static) toolkind="ar";; shared) toolkind="sh";; *) raise "unknown targetkind(${targetkind})!" ;; esac _get_toolchain_toolset "${_target_toolchain}" "${toolkind}"; local program="${_ret}" path_toolname "${program}"; local toolname="${_ret}" # get linker flags _get_flagname "${toolkind}"; local flagname="${_ret}" flagname="${target}_${flagname}" # get depfiles local dep="" local depfiles="" for dep in ${deps}; do _get_target_file "${dep}"; local depfile="${_ret}" if test_nz "${depfiles}"; then depfiles="${depfiles} ${depfile}" else depfiles="${depfile}" fi done # link target echo "${target}: ${targetfile}" >> "${xmake_sh_makefile}" echo "${targetfile}: ${depfiles}${objectfiles}" >> "${xmake_sh_makefile}" if test_eq "${targetkind}" "static"; then print "\t@echo archiving.${_target_mode} ${targetfile}" >> "${xmake_sh_makefile}" else print "\t@echo linking.${_target_mode} ${targetfile}" >> "${xmake_sh_makefile}" fi case "${toolname}" in gcc) _gmake_add_build_target_for_gcc_clang "${toolkind}" "${targetfile}" "${objectfiles}" "${flagname}";; gxx) _gmake_add_build_target_for_gcc_clang "${toolkind}" "${targetfile}" "${objectfiles}" "${flagname}";; clang) _gmake_add_build_target_for_gcc_clang "${toolkind}" "${targetfile}" "${objectfiles}" "${flagname}";; clangxx) _gmake_add_build_target_for_gcc_clang "${toolkind}" "${targetfile}" "${objectfiles}" "${flagname}";; emcc) _gmake_add_build_target_for_gcc_clang "${toolkind}" "${targetfile}" "${objectfiles}" "${flagname}";; emxx) _gmake_add_build_target_for_gcc_clang "${toolkind}" "${targetfile}" "${objectfiles}" "${flagname}";; cosmocc) _gmake_add_build_target_for_gcc_clang "${toolkind}" "${targetfile}" "${objectfiles}" "${flagname}";; cosmocxx) _gmake_add_build_target_for_gcc_clang "${toolkind}" "${targetfile}" "${objectfiles}" "${flagname}";; tcc) _gmake_add_build_target_for_gcc_clang "${toolkind}" "${targetfile}" "${objectfiles}" "${flagname}";; ar) _gmake_add_build_target_for_ar "${toolkind}" "${targetfile}" "${objectfiles}" "${flagname}";; emar) _gmake_add_build_target_for_ar "${toolkind}" "${targetfile}" "${objectfiles}" "${flagname}";; cosmoar) _gmake_add_build_target_for_ar "${toolkind}" "${targetfile}" "${objectfiles}" "${flagname}";; *) raise "unknown toolname(${toolname})!" ;; esac # @see https://github.com/tboox/tbox/issues/214 if test_eq "${targetkind}" "shared"; then _get_target_item "${target}" "version"; local version="${_ret}" _get_target_soname "${target}"; local soname="${_ret}" if test_nz "${soname}" && test_nz "${version}"; then _get_target_filename "${target}"; local filename="${_ret}" _get_target_extension "${target}"; local extension="${_ret}" local targetfile_with_version="${targetdir}/${filename}.${version}" if test_eq "${extension}" ".dylib"; then path_basename "${filename}"; local basename="${_ret}" targetfile_with_version="${targetdir}/${basename}.${version}${extension}" fi local targetfile_with_soname="${targetdir}/${soname}" path_filename "${targetfile_with_version}"; local targetfilename_with_version="${_ret}" if test_nq "${soname}" "${filename}" && test_nq "${soname}" "${targetfilename_with_version}"; then print "\t@cp -p ${targetfile} ${targetfile_with_version}" >> "${xmake_sh_makefile}" print "\t@cd ${targetdir} && ln -sf ${targetfilename_with_version} ${soname} && ln -sf ${soname} ${filename}" >> "${xmake_sh_makefile}" fi fi fi # end echo "" >> "${xmake_sh_makefile}" # build objects _gmake_add_build_objects "${target}" } _gmake_add_build_targets() { local target="" local defaults="" for target in ${_xmake_sh_targets}; do if _is_target_default "${target}"; then defaults="${defaults} ${target}" fi done echo "default:${defaults}" >> "${xmake_sh_makefile}" echo "all:${_xmake_sh_targets}" >> "${xmake_sh_makefile}" echo ".PHONY: default all" >> "${xmake_sh_makefile}" echo "" >> "${xmake_sh_makefile}" for target in ${_xmake_sh_targets}; do _gmake_add_build_target "${target}" done } _gmake_add_build() { _gmake_add_build_targets } _gmake_add_run_target() { local target=${1} _get_targetdir "${target}"; local targetdir="${_ret}" _get_target_file "${target}"; local targetfile="${_ret}" if is_plat "macosx"; then print "\t@DYLD_LIBRARY_PATH=${targetdir} ${targetfile}" >> "${xmake_sh_makefile}" elif is_plat "linux" "bsd"; then print "\t@LD_LIBRARY_PATH=${targetdir} ${targetfile}" >> "${xmake_sh_makefile}" else print "\t@${targetfile}" >> "${xmake_sh_makefile}" fi } _gmake_add_run_targets() { local target="" local targets="" for target in ${_xmake_sh_targets}; do _get_target_item "${target}" "kind"; local kind="${_ret}" if test "x${kind}" = "xbinary"; then if _is_target_default "${target}"; then targets="${targets} ${target}" fi fi done echo "run:${targets}" >> "${xmake_sh_makefile}" for target in ${targets}; do _gmake_add_run_target "${target}" done echo "" >> "${xmake_sh_makefile}" } _gmake_add_run() { _gmake_add_run_targets } _gmake_add_clean_target() { local target=${1} local objectfile="" _get_target_file "${target}"; local targetfile="${_ret}" _get_target_objectfiles "${target}"; local objectfiles="${_ret}" print "\t@rm ${targetfile}" >> "${xmake_sh_makefile}" for objectfile in ${objectfiles}; do print "\t@rm ${objectfile}" >> "${xmake_sh_makefile}" done # @see https://github.com/tboox/tbox/issues/214 _get_targetdir "${target}"; local targetdir="${_ret}" _get_target_item "${target}" "kind"; local targetkind="${_ret}" if test_eq "${targetkind}" "shared"; then _get_target_item "${target}" "version"; local version="${_ret}" _get_target_soname "${target}"; local soname="${_ret}" if test_nz "${soname}" && test_nz "${version}"; then _get_target_filename "${target}"; local filename="${_ret}" _get_target_extension "${target}"; local extension="${_ret}" local targetfile_with_version="${targetdir}/${filename}.${version}" if test_eq "${extension}" ".dylib"; then path_basename "${filename}"; local basename="${_ret}" targetfile_with_version="${targetdir}/${basename}.${version}${extension}" fi local targetfile_with_soname="${targetdir}/${soname}" print "\t@if test -f ${targetfile_with_soname}; then rm ${targetfile_with_soname}; fi" >> "${xmake_sh_makefile}" print "\t@if test -f ${targetfile_with_version}; then rm ${targetfile_with_version}; fi" >> "${xmake_sh_makefile}" fi fi } _gmake_add_clean_targets() { local target="" local targets="" for target in ${_xmake_sh_targets}; do if _is_target_default "${target}"; then targets="${targets} ${target}" fi done echo "clean:${targets}" >> "${xmake_sh_makefile}" for target in ${targets}; do _gmake_add_clean_target "${target}" done echo "" >> "${xmake_sh_makefile}" } _gmake_add_clean() { _gmake_add_clean_targets } _gmake_add_install_target() { local target=${1} _get_target_file "${target}"; local targetfile="${_ret}" path_filename "${targetfile}"; local filename="${_ret}" _get_target_item "${target}" "installdir"; local installdir="${_ret}" _get_target_item "${target}" "kind"; local targetkind="${_ret}" if test_z "${installdir}"; then installdir="\$(INSTALLDIR)" fi # before install _get_target_item "${target}" "before_install"; local before_install="${_ret}" if test_nz "${before_install}"; then eval ${before_install} "\${target}" "\${installdir}" fi # @see https://github.com/tboox/tbox/issues/214 install_for_soname=false if test_eq "${targetkind}" "shared"; then _get_target_item "${target}" "version"; local version="${_ret}" _get_target_soname "${target}"; local soname="${_ret}" if test_nz "${soname}" && test_nz "${version}"; then _get_target_extension "${target}"; local extension="${_ret}" string_replace "${_install_libdir_default}" "\${prefix}" "${installdir}"; _install_libdir_default="${_ret}" local targetfile_with_version="${_install_libdir_default}/${filename}.${version}" if test_eq "${extension}" ".dylib"; then path_basename "${filename}"; local basename="${_ret}" targetfile_with_version="${_install_libdir_default}/${basename}.${version}${extension}" fi local targetfile_with_soname="${_install_libdir_default}/${soname}" path_filename "${targetfile_with_version}"; local targetfilename_with_version="${_ret}" if test_nq "${soname}" "${filename}" && test_nq "${soname}" "${targetfilename_with_version}"; then install_for_soname=true fi fi fi # install target file if test_eq "${targetkind}" "binary"; then string_replace "${_install_bindir_default}" "\${prefix}" "${installdir}"; _install_bindir_default="${_ret}" print "\t@echo installing ${targetfile} to ${_install_bindir_default}" >> "${xmake_sh_makefile}" print "\t@mkdir -p ${_install_bindir_default}" >> "${xmake_sh_makefile}" print "\t@cp -p ${targetfile} ${_install_bindir_default}/${filename}" >> "${xmake_sh_makefile}" elif ${install_for_soname}; then string_replace "${_install_libdir_default}" "\${prefix}" "${installdir}"; _install_libdir_default="${_ret}" print "\t@echo installing ${targetfile} to ${_install_libdir_default}" >> "${xmake_sh_makefile}" print "\t@mkdir -p ${_install_libdir_default}" >> "${xmake_sh_makefile}" print "\t@cp -p ${targetfile} ${targetfile_with_version}" >> "${xmake_sh_makefile}" print "\t@cd ${_install_libdir_default} && ln -sf ${targetfilename_with_version} ${soname} && ln -sf ${soname} ${filename}" >> "${xmake_sh_makefile}" elif test_eq "${targetkind}" "static" || test_eq "${targetkind}" "shared"; then string_replace "${_install_libdir_default}" "\${prefix}" "${installdir}"; _install_libdir_default="${_ret}" print "\t@echo installing ${targetfile} to ${_install_libdir_default}" >> "${xmake_sh_makefile}" print "\t@mkdir -p ${_install_libdir_default}" >> "${xmake_sh_makefile}" print "\t@cp -p ${targetfile} ${_install_libdir_default}/${filename}" >> "${xmake_sh_makefile}" fi # install header files _get_target_item "${target}" "headerfiles"; local headerfiles="${_ret}" if test_nz "${headerfiles}"; then string_replace "${_install_includedir_default}" "\${prefix}" "${installdir}"; _install_includedir_default="${_ret}" local srcheaderfile="" local includedir="${_install_includedir_default}" for srcheaderfile in ${headerfiles}; do string_split "${srcheaderfile}" ":" local srcheaderfile="${_ret}" local rootdir="${_ret2}" local prefixdir="${_ret3}" local filename="${_ret4}" if test_z "${filename}"; then path_filename "${srcheaderfile}"; filename="${_ret}" fi local dstheaderdir="${includedir}" if test_nz "${prefixdir}"; then dstheaderdir="${dstheaderdir}/${prefixdir}" fi local dstheaderfile="${dstheaderdir}/${filename}" if test_nz "${rootdir}"; then path_relative "${rootdir}" "${srcheaderfile}"; local subfile="${_ret}" dstheaderfile="${dstheaderdir}/${subfile}" fi path_directory "${dstheaderfile}"; dstheaderdir="${_ret}" print "\t@mkdir -p ${dstheaderdir}" >> "${xmake_sh_makefile}" print "\t@cp -p ${srcheaderfile} ${dstheaderfile}" >> "${xmake_sh_makefile}" done fi # install user files _get_target_item "${target}" "installfiles"; local installfiles="${_ret}" if test_nz "${installfiles}"; then local srcinstallfile="" for srcinstallfile in ${installfiles}; do string_split "${srcinstallfile}" ":" local srcinstallfile="${_ret}" local rootdir="${_ret2}" local prefixdir="${_ret3}" local filename="${_ret4}" if test_z "${filename}"; then path_filename "${srcinstallfile}"; filename="${_ret}" fi local dstinstalldir="${installdir}" if test_nz "${prefixdir}"; then dstinstalldir="${dstinstalldir}/${prefixdir}" fi local dstinstallfile="${dstinstalldir}/${filename}" if test_nz "${rootdir}"; then path_relative "${rootdir}" "${srcinstallfile}"; local subfile="${_ret}" dstinstallfile="${dstinstalldir}/${subfile}" fi path_directory "${dstinstallfile}"; dstinstalldir="${_ret}" print "\t@mkdir -p ${dstinstalldir}" >> "${xmake_sh_makefile}" print "\t@cp -p ${srcinstallfile} ${dstinstallfile}" >> "${xmake_sh_makefile}" done fi # after install _get_target_item "${target}" "after_install"; local after_install="${_ret}" if test_nz "${after_install}"; then eval ${after_install} "\${target}" "\${installdir}" fi } _gmake_add_install_targets() { local target="" local targets="" for target in ${_xmake_sh_targets}; do if _is_target_default "${target}"; then targets="${targets} ${target}" fi done echo "install:${targets}" >> "${xmake_sh_makefile}" for target in ${targets}; do _gmake_add_install_target "${target}" done echo "" >> "${xmake_sh_makefile}" } _gmake_add_install() { _gmake_add_install_targets } _gmake_done() { echo "makefile is generated!" if "${xmake_sh_diagnosis}"; then cat "${xmake_sh_makefile}" fi } # generate build file for gmake _generate_for_gmake() { _gmake_begin _gmake_add_header _gmake_add_switches _gmake_add_toolchains _gmake_add_flags _gmake_add_build _gmake_add_clean _gmake_add_install _gmake_add_run _gmake_done } #----------------------------------------------------------------------------- # generate ninja file # _ninja_begin() { echo "generating ninja build file .." } _ninja_add_header() { echo "# this is the build file for this project # it is autogenerated by the xmake.sh build system. # do not edit by hand. " > "${xmake_sh_ninjafile}" echo "ninja_required_version = 1.3" >> "${xmake_sh_ninjafile}" echo "builddir = ${xmake_sh_builddir}" >> "${xmake_sh_ninjafile}" echo "" >> "${xmake_sh_ninjafile}" } _ninja_add_switches() { local value="" value="${_install_prefix_default}"; _ninja_escape "${value}"; value="${_ret}" echo "prefix_default = ${value}" >> "${xmake_sh_ninjafile}" value="${_install_bindir_default}"; _ninja_escape "${value}"; value="${_ret}" echo "bindir_default = ${value}" >> "${xmake_sh_ninjafile}" value="${_install_libdir_default}"; _ninja_escape "${value}"; value="${_ret}" echo "libdir_default = ${value}" >> "${xmake_sh_ninjafile}" value="${_install_includedir_default}"; _ninja_escape "${value}"; value="${_ret}" echo "includedir_default = ${value}" >> "${xmake_sh_ninjafile}" echo "" >> "${xmake_sh_ninjafile}" } _ninja_add_rules() { echo "rule command" >> "${xmake_sh_ninjafile}" echo " command = \$command" >> "${xmake_sh_ninjafile}" echo " description = \$description" >> "${xmake_sh_ninjafile}" echo " restat = \$restat" >> "${xmake_sh_ninjafile}" echo "" >> "${xmake_sh_ninjafile}" } _ninja_add_toolchains() { _get_targets_toolkinds; local kinds="${_ret}" if test_nz "${kinds}"; then echo "# toolchain programs" >> "${xmake_sh_ninjafile}" for kind in ${kinds}; do _get_toolchain_toolset "${_target_toolchain}" "${kind}"; local program="${_ret}" echo "${kind} = ${program}" >> "${xmake_sh_ninjafile}" done echo "" >> "${xmake_sh_ninjafile}" fi } _ninja_add_flags() { _get_targets_toolkinds; local kinds="${_ret}" for target in ${_xmake_sh_targets}; do for kind in ${kinds}; do _get_target_flags "${target}" "${kind}"; local flags="${_ret}" if test_nz "${flags}"; then _get_flagname "${kind}"; local flagname="${_ret}" local key="${target}_${flagname}" _ninja_escape "${flags}"; flags="${_ret}" echo "${key} = ${flags}" >> "${xmake_sh_ninjafile}" fi done echo "" >> "${xmake_sh_ninjafile}" done } _ninja_add_build_object() { local target=${1} local sourcefile="${2}" local objectfile="${3}" path_sourcekind "${sourcefile}"; local sourcekind="${_ret}" _get_target_flags "${target}" "${sourcekind}"; local flags="${_ret}" _toolchain_compcmd "${sourcekind}" "${objectfile}" "${sourcefile}" "${flags}"; local compcmd="${_ret}" path_directory "${objectfile}"; local objectdir="${_ret}" local use_shell_wrapper=false local command="mkdir -p \"${objectdir}\" && ${compcmd}" if is_host "msys" "cygwin" "mingw"; then use_shell_wrapper=true fi if ${use_shell_wrapper}; then _shell_escape_single_quotes "${command}"; local command_script="${_ret}" command="sh -lc ${command_script}" fi local description="compiling.${_target_mode} ${sourcefile}" _ninja_escape "${command}"; command="${_ret}" _ninja_escape "${description}"; description="${_ret}" echo "build ${objectfile}: command ${sourcefile}" >> "${xmake_sh_ninjafile}" echo " command = ${command}" >> "${xmake_sh_ninjafile}" echo " description = ${description}" >> "${xmake_sh_ninjafile}" echo " restat = 0" >> "${xmake_sh_ninjafile}" echo "" >> "${xmake_sh_ninjafile}" } _ninja_add_build_objects() { local target=${1} _get_target_sourcefiles "${target}"; local sourcefiles="${_ret}" for sourcefile in ${sourcefiles}; do _get_target_objectfile "${target}" "${sourcefile}"; local objectfile="${_ret}" _ninja_add_build_object "${target}" "${sourcefile}" "${objectfile}" done } _ninja_add_build_target() { local target=${1} _get_targetdir "${target}"; local targetdir="${_ret}" _get_target_file "${target}"; local targetfile="${_ret}" _get_target_item "${target}" "deps"; local deps="${_ret}" _get_target_objectfiles "${target}"; local objectfiles="${_ret}" _get_target_item "${target}" "kind"; local targetkind="${_ret}" local toolkind="" case "${targetkind}" in binary) toolkind="ld";; static) toolkind="ar";; shared) toolkind="sh";; *) raise "unknown targetkind(${targetkind})!" ;; esac _get_target_flags "${target}" "${toolkind}"; local linkflags="${_ret}" _toolchain_linkcmd "${toolkind}" "${targetfile}" "${objectfiles}" "${linkflags}"; local linkcmd="${_ret}" local use_shell_wrapper=false local command="mkdir -p \"${targetdir}\" && ${linkcmd}" if is_host "msys" "cygwin" "mingw"; then use_shell_wrapper=true fi local description="linking.${_target_mode} ${targetfile}" if test_eq "${targetkind}" "shared"; then _get_target_item "${target}" "version"; local version="${_ret}" _get_target_soname "${target}"; local soname="${_ret}" if test_nz "${soname}" && test_nz "${version}"; then _get_target_filename "${target}"; local targetfilename="${_ret}" _get_target_extension "${target}"; local extension="${_ret}" local targetfile_with_version="${targetdir}/${targetfilename}.${version}" if test_eq "${extension}" ".dylib"; then path_basename "${targetfilename}"; local basename="${_ret}" targetfile_with_version="${targetdir}/${basename}.${version}${extension}" fi local targetfile_with_soname="${targetdir}/${soname}" path_filename "${targetfile_with_version}"; local targetfilename_with_version="${_ret}" if test_nq "${soname}" "${targetfilename}" && test_nq "${soname}" "${targetfilename_with_version}"; then command="${command} && cp -p ${targetfile} ${targetfile_with_version} && cd \"${targetdir}\" && ln -sf ${targetfilename_with_version} ${soname} && ln -sf ${soname} ${targetfilename}" fi fi fi if ${use_shell_wrapper}; then _shell_escape_single_quotes "${command}"; local command_script="${_ret}" command="sh -lc ${command_script}" fi _ninja_escape "${command}"; command="${_ret}" _ninja_escape "${description}"; description="${_ret}" local orderdeps="" local dep="" for dep in ${deps}; do _get_target_file "${dep}"; local depfile="${_ret}" if test_nz "${orderdeps}"; then orderdeps="${orderdeps} ${depfile}" else orderdeps="${depfile}" fi done if test_nz "${orderdeps}"; then echo "build ${targetfile}: command ${objectfiles} | ${orderdeps}" >> "${xmake_sh_ninjafile}" else echo "build ${targetfile}: command ${objectfiles}" >> "${xmake_sh_ninjafile}" fi echo " command = ${command}" >> "${xmake_sh_ninjafile}" echo " description = ${description}" >> "${xmake_sh_ninjafile}" echo " restat = 0" >> "${xmake_sh_ninjafile}" echo "" >> "${xmake_sh_ninjafile}" echo "build ${target}: phony ${targetfile}" >> "${xmake_sh_ninjafile}" echo "" >> "${xmake_sh_ninjafile}" _ninja_add_build_objects "${target}" } _ninja_add_build_targets() { local target="" local defaults="" for target in ${_xmake_sh_targets}; do if _is_target_default "${target}"; then if test_nz "${defaults}"; then defaults="${defaults} ${target}" else defaults="${target}" fi fi done if test_nz "${defaults}"; then echo "build default: phony ${defaults}" >> "${xmake_sh_ninjafile}" echo "" >> "${xmake_sh_ninjafile}" echo "default default" >> "${xmake_sh_ninjafile}" else echo "default all" >> "${xmake_sh_ninjafile}" fi echo "build all: phony ${_xmake_sh_targets}" >> "${xmake_sh_ninjafile}" echo "" >> "${xmake_sh_ninjafile}" for target in ${_xmake_sh_targets}; do _ninja_add_build_target "${target}" done } _ninja_add_build() { _ninja_add_build_targets } _ninja_add_run_target() { local target=${1} _get_targetdir "${target}"; local targetdir="${_ret}" _get_target_file "${target}"; local targetfile="${_ret}" local command="" if is_host "msys" "cygwin" "mingw"; then local projectdir="${xmake_sh_projectdir}" local targetfile_rel="./${targetfile}" local targetfile_alt="./build/${targetfile#*/}" local targetbuilddir="${targetfile%/*}" local targetbuilddir_rel="./${targetbuilddir}" local targetbuilddir_alt="./build/${targetbuilddir#*/}" local run_script="cd \"${projectdir}\" && target=\"${targetfile_rel}\"; alt=\"${targetfile_alt}\"; if [ ! -f \"\$target\" ] && [ -f \"\$alt\" ]; then target=\"\$alt\"; fi; if [ ! -f \"\$target\" ]; then echo \"[ninja run] missing ${targetfile_rel} (and fallback ${targetfile_alt})\"; ls -l \"${targetbuilddir_rel}\" || true; ls -l \"${targetbuilddir_alt}\" || true; exit 1; fi; \"\$target\"" _shell_escape_single_quotes "${run_script}"; local run_script_escaped="${_ret}" command="sh -lc ${run_script_escaped}" elif is_plat "macosx"; then command="DYLD_LIBRARY_PATH=${targetdir} ${targetfile}" elif is_plat "linux" "bsd"; then command="LD_LIBRARY_PATH=${targetdir} ${targetfile}" else command="${targetfile}" fi local description="running.${_target_mode} ${targetfile}" _ninja_escape "${command}"; command="${_ret}" _ninja_escape "${description}"; description="${_ret}" echo "build run.${target}: command | ${targetfile}" >> "${xmake_sh_ninjafile}" echo " command = ${command}" >> "${xmake_sh_ninjafile}" echo " description = ${description}" >> "${xmake_sh_ninjafile}" echo " restat = 0" >> "${xmake_sh_ninjafile}" echo "" >> "${xmake_sh_ninjafile}" } _ninja_add_run_targets() { local target="" local runtargets="" for target in ${_xmake_sh_targets}; do _get_target_item "${target}" "kind"; local kind="${_ret}" if test_eq "${kind}" "binary"; then if _is_target_default "${target}"; then if test_nz "${runtargets}"; then runtargets="${runtargets} run.${target}" else runtargets="run.${target}" fi _ninja_add_run_target "${target}" fi fi done if test_nz "${runtargets}"; then echo "build run: phony ${runtargets}" >> "${xmake_sh_ninjafile}" echo "" >> "${xmake_sh_ninjafile}" fi } _ninja_add_run() { _ninja_add_run_targets } _ninja_add_clean_target() { local target=${1} _get_target_file "${target}"; local targetfile="${_ret}" _get_target_objectfiles "${target}"; local objectfiles="${_ret}" local removefiles="${targetfile}" local objectfile="" for objectfile in ${objectfiles}; do removefiles="${removefiles} ${objectfile}" done _get_targetdir "${target}"; local targetdir="${_ret}" _get_target_item "${target}" "kind"; local targetkind="${_ret}" if test_eq "${targetkind}" "shared"; then _get_target_item "${target}" "version"; local version="${_ret}" _get_target_soname "${target}"; local soname="${_ret}" if test_nz "${soname}" && test_nz "${version}"; then _get_target_filename "${target}"; local filename="${_ret}" _get_target_extension "${target}"; local extension="${_ret}" local targetfile_with_version="${targetdir}/${filename}.${version}" if test_eq "${extension}" ".dylib"; then path_basename "${filename}"; local basename="${_ret}" targetfile_with_version="${targetdir}/${basename}.${version}${extension}" fi local targetfile_with_soname="${targetdir}/${soname}" removefiles="${removefiles} ${targetfile_with_soname} ${targetfile_with_version}" fi fi local command="rm -f ${removefiles}" local description="cleaning.${_target_mode} ${target}" _ninja_escape "${command}"; command="${_ret}" _ninja_escape "${description}"; description="${_ret}" echo "build clean.${target}: command" >> "${xmake_sh_ninjafile}" echo " command = ${command}" >> "${xmake_sh_ninjafile}" echo " description = ${description}" >> "${xmake_sh_ninjafile}" echo " restat = 0" >> "${xmake_sh_ninjafile}" echo "" >> "${xmake_sh_ninjafile}" } _ninja_add_clean_targets() { local target="" local cleantargets="" for target in ${_xmake_sh_targets}; do if _is_target_default "${target}"; then if test_nz "${cleantargets}"; then cleantargets="${cleantargets} clean.${target}" else cleantargets="clean.${target}" fi _ninja_add_clean_target "${target}" fi done if test_nz "${cleantargets}"; then echo "build clean: phony ${cleantargets}" >> "${xmake_sh_ninjafile}" echo "" >> "${xmake_sh_ninjafile}" fi } _ninja_add_clean() { _ninja_add_clean_targets } _ninja_install_prepare_script() { local target="${1}" local ninjadir="${xmake_sh_builddir}/.ninja" local scriptdir="${xmake_sh_projectdir}/${ninjadir}" mkdir -p "${scriptdir}" _ret="${scriptdir}/install_${target}.sh" _ret2="${ninjadir}/install_${target}.sh" } _ninja_install_write_header() { local scriptfile="${1}" local escaped_prefix_default="${2}" local escaped_installdir_template="${3}" local escaped_projectdir="${4}" local escaped_bindir_template="${5}" local escaped_libdir_template="${6}" local escaped_includedir_template="${7}" cat > "${scriptfile}" <> "${scriptfile}" elif ${install_for_soname}; then local version_template="${_install_libdir_default}/${filename}.${version}" if test_eq "${extension}" ".dylib"; then version_template="${_install_libdir_default}/${filename_basename}.${version}${extension}" fi _shell_escape_single_quotes "${targetfile}"; local escaped_targetfile="${_ret}" _shell_escape_single_quotes "${version_template}"; local escaped_version_template="${_ret}" _shell_escape_single_quotes "${soname}"; local escaped_soname="${_ret}" _shell_escape_single_quotes "${filename}"; local escaped_filename="${_ret}" cat >> "${scriptfile}" <> "${scriptfile}" fi } _ninja_install_append_headerfiles() { local scriptfile="${1}" local headerfiles="${2}" if test_nz "${headerfiles}"; then local srcheaderfile="" for srcheaderfile in ${headerfiles}; do string_split "${srcheaderfile}" ":" local srcheaderpath="${_ret}" local rootdir="${_ret2}" local prefixdir="${_ret3}" local headername="${_ret4}" if test_z "${headername}"; then path_filename "${srcheaderpath}"; headername="${_ret}" fi local dstheaderdir_template="${_install_includedir_default}" if test_nz "${prefixdir}"; then dstheaderdir_template="${dstheaderdir_template}/${prefixdir}" fi local dstheaderfile_template="" if test_nz "${rootdir}"; then path_relative "${rootdir}" "${srcheaderpath}"; local subfile="${_ret}" dstheaderfile_template="${dstheaderdir_template}/${subfile}" else dstheaderfile_template="${dstheaderdir_template}/${headername}" fi _shell_escape_single_quotes "${srcheaderpath}"; local escaped_src="${_ret}" _shell_escape_single_quotes "${dstheaderfile_template}"; local escaped_dst="${_ret}" echo "copy_file ${escaped_src} ${escaped_dst}" >> "${scriptfile}" done fi } _ninja_install_append_installfiles() { local scriptfile="${1}" local installdir="${2}" local installfiles="${3}" if test_nz "${installfiles}"; then local srcinstallfile="" for srcinstallfile in ${installfiles}; do string_split "${srcinstallfile}" ":" local srcfilepath="${_ret}" local rootdir="${_ret2}" local prefixdir="${_ret3}" local installname="${_ret4}" if test_z "${installname}"; then path_filename "${srcfilepath}"; installname="${_ret}" fi local dst_template="${installdir}" if test_z "${dst_template}"; then dst_template="\${prefix}" fi if test_nz "${prefixdir}"; then dst_template="${dst_template}/${prefixdir}" fi if test_nz "${rootdir}"; then path_relative "${rootdir}" "${srcfilepath}"; local subfile="${_ret}" dst_template="${dst_template}/${subfile}" else dst_template="${dst_template}/${installname}" fi _shell_escape_single_quotes "${srcfilepath}"; local escaped_src="${_ret}" _shell_escape_single_quotes "${dst_template}"; local escaped_dst="${_ret}" echo "copy_file ${escaped_src} ${escaped_dst}" >> "${scriptfile}" done fi } _ninja_add_install_target() { local target=${1} _get_target_file "${target}"; local targetfile="${_ret}" path_filename "${targetfile}"; local filename="${_ret}" _get_target_item "${target}" "installdir"; local installdir="${_ret}" _get_target_item "${target}" "kind"; local targetkind="${_ret}" local install_for_soname=false local version="" local soname="" local extension="" local filename_basename="" if test_eq "${targetkind}" "shared"; then _get_target_item "${target}" "version"; version="${_ret}" _get_target_soname "${target}"; soname="${_ret}" _get_target_extension "${target}"; extension="${_ret}" path_basename "${filename}"; filename_basename="${_ret}" if test_nz "${soname}" && test_nz "${version}"; then local targetfilename_with_version_guess="${filename}.${version}" if test_eq "${extension}" ".dylib"; then targetfilename_with_version_guess="${filename_basename}.${version}${extension}" fi if test_nq "${soname}" "${filename}" && test_nq "${soname}" "${targetfilename_with_version_guess}"; then install_for_soname=true fi fi fi _ninja_install_prepare_script "${target}" local scriptfile="${_ret}" local scriptfile_rel="${_ret2}" _shell_escape_single_quotes "${_install_prefix_default}"; local escaped_prefix_default="${_ret}" _shell_escape_single_quotes "${installdir}"; local escaped_installdir_template="${_ret}" _shell_escape_single_quotes "${xmake_sh_projectdir}"; local escaped_projectdir="${_ret}" _shell_escape_single_quotes "${_install_bindir_default}"; local escaped_bindir_template="${_ret}" _shell_escape_single_quotes "${_install_libdir_default}"; local escaped_libdir_template="${_ret}" _shell_escape_single_quotes "${_install_includedir_default}"; local escaped_includedir_template="${_ret}" _ninja_install_write_header "${scriptfile}" \ "${escaped_prefix_default}" \ "${escaped_installdir_template}" \ "${escaped_projectdir}" \ "${escaped_bindir_template}" \ "${escaped_libdir_template}" \ "${escaped_includedir_template}" _ninja_install_append_target_artifacts "${scriptfile}" "${targetkind}" "${install_for_soname}" \ "${targetfile}" "${filename}" "${version}" "${soname}" "${extension}" "${filename_basename}" _get_target_item "${target}" "headerfiles"; local headerfiles="${_ret}" _ninja_install_append_headerfiles "${scriptfile}" "${headerfiles}" _get_target_item "${target}" "installfiles"; local installfiles="${_ret}" _ninja_install_append_installfiles "${scriptfile}" "${installdir}" "${installfiles}" echo "" >> "${scriptfile}" chmod +x "${scriptfile}" local command="sh ${scriptfile_rel}" local description="installing.${_target_mode} ${target}" _ninja_escape "${command}"; command="${_ret}" _ninja_escape "${description}"; description="${_ret}" echo "build install.${target}: command | ${targetfile}" >> "${xmake_sh_ninjafile}" echo " command = ${command}" >> "${xmake_sh_ninjafile}" echo " description = ${description}" >> "${xmake_sh_ninjafile}" echo " restat = 0" >> "${xmake_sh_ninjafile}" echo "" >> "${xmake_sh_ninjafile}" } _ninja_add_install_targets() { local target="" local installtargets="" for target in ${_xmake_sh_targets}; do if _is_target_default "${target}"; then if test_nz "${installtargets}"; then installtargets="${installtargets} install.${target}" else installtargets="install.${target}" fi _ninja_add_install_target "${target}" fi done if test_nz "${installtargets}"; then echo "build install: phony ${installtargets}" >> "${xmake_sh_ninjafile}" echo "" >> "${xmake_sh_ninjafile}" fi } _ninja_add_install() { _ninja_add_install_targets } _ninja_done() { echo "ninja build file is generated!" if "${xmake_sh_diagnosis}"; then cat "${xmake_sh_ninjafile}" fi } # generate build file for ninja _generate_for_ninja() { _ninja_begin _ninja_add_header _ninja_add_switches _ninja_add_toolchains _ninja_add_flags _ninja_add_rules _ninja_add_build _ninja_add_clean _ninja_add_install _ninja_add_run _ninja_done } #----------------------------------------------------------------------------- # generate build file # _generate_build_file() { if test_eq "${project_generator}" "gmake"; then _generate_for_gmake elif test_eq "${project_generator}" "ninja"; then _generate_for_ninja else raise "unknown generator: ${project_generator}" fi } _generate_build_file