How to (plus reasons to) produce cross-platform source code from Unix consoles (such as iSH or Termux on smartphones)
[Version of this post is 033d35b]
Reasons to
Because not everyone can afford to use a traditional computer with Microsoft Windows (Win32), but everyone should produce cross-platform code.
MinGW allows you to ensure that your code is cross-platform (if your code compiles on Linux + Win32, it is more cross-platform than most code).
You can also use Termux's Hangover package to ensure that unit tests pass on Win32.
Have done so since 2012 (back when Terminal IDE (which is now discontinued) was Android's sole console app and WINE was Android's sole virtual environment for Win32), with minimal additional effort (versus effort to produce source code which just executes on Arm64), plus with the benefit that the code has use on more computers.
Lots of businesses which thought "Such and such single, popular, platfom will always be more than enough for us" have since found that post-production "ports' were worth the effort, although it is much more difficult to "port" non-standard software than to produce software which is cross-platform from the start.
For example, when Android became more popular than Windows (and Bluetooth keyboard/mouse became affordable to regular users), Blizzard Entertainment spent millions of dollars to port World of Warcraft to Arm64 (the "port" was dubbed Project Neptune), cost which would have been avoided if all of the source code was cross-platform from the start. Since none of the original developers were familiar with cross-platform standards, Blizzard had to outsource the "port" to programmers from India, and arguments with them resulted in Project Neptune being discontinued (after millions of dollars were spent to produce the "port").
Example 2: When Microsoft first produced Microsoft Windows, Microsoft thought that Unix would never become popular enough with consumers that it would be worth the effort to produce a POSIX-compliant (cross-platform) subsystem. Once contracts for rich government consumers required FIPS 151-2, Microsoft found that it was worth the effort, and produced the Microsoft POSIX subsystem for Windows 2000, but the total effort was much more than if the original Microsoft Windows had used POSIX-compliant subsystems.
Recent; as more consumers switched to Unix-like systems, Microsoft has learned that POSIX has enough value that it was worth the effort to produce ports. What followed was Windows SFU (Windows Services for UNIX) and Windows Subsystem for Linux with has actual Unix consoles.
How to
Most Linux distros (such as Ubuntu) ship with consoles (such as Konsole). For distros which don’t include consoles, install consoles such as iSH (for iOS) or Termux (for Android OS).
https://github.com/SwuduSusuwu/SusuLib?tab=readme-ov-file#contributor-conventionsrules is common standards (“best practices” / “style rules”) for
git
,/bin/sh
, and x86_64-w64-mingw32-g++ (which uses theC++
rules).
https://github.com/SwuduSusuwu/SusuLib/blob/preview/build.sh is project-specific variables to replace with your own. Sources
make.sh
andMacros.sh
for utils.https://github.com/SwuduSusuwu/SusuLib/blob/preview/sh/make.sh is numerous
make
-related/bin/sh
utils, such as the actualmingw
code. SourcesMacros.sh
for utils.https://github.com/SwuduSusuwu/SusuLib/blob/preview/sh/Macros.sh is
/bin/sh
utils (such as fast substr alternatives tosed
, loggers, and a minimalist alternative toncurses
).
Source code
git clone https://github.com/SwuduSusuwu/SusuLib.git && cd SusuLib
For the most new source code; git switch preview
To go back to the stable version; git switch trunk
[Notice: what follows is in reverse order (standard is to show contents of includes first, but since the focus is not on internals, this post starts with the end-use).]
Source code as of commit 033d35b follows (use git clone
is for the absolute most new):
Build script (/bin/sh
code)
less build.sh
(https://github.com/SwuduSusuwu/SusuLib/blob/preview/build.sh)
#!/bin/sh
#
#/* (C) 2024 Swudu Susuwu, dual licenses: choose [_GPLv2_](./LICENSE_GPLv2) or [_Apache 2_](./LICENSE) (allows all uses).
# * Builds `./c/` and `./cxx/` into `./obj/` and `./bin/`. Usage: "Console flags" from `./README.md#optionssetup`. */
GIT_ROOT="$(dirname "$(git rev-parse --git-dir)")/" #`git` does not set `${GIT_DIR}`, nor `${GIT_WORK_TREE}`
SUSUWU_INCLUDE_ERROR() { #/* Usage; `SUSUWU_INCLUDE_ERROR "<relative path>" "error message"` */
echo "[$0: Error: \`GIT_WORK_TREE=${GIT_ROOT}\` \`\${GIT_WORK_TREE}${1}\` ${2}.]"; exit 1
}
#shellcheck source=./sh/make.sh
SUSUWU_INCLUDE() { #/* Usage; `SUSUWU_INCLUDE "<relative path>"` */
SUSUWU_INCLUDE_PATH=${1}; shift #/* So path is not included in source'd scripts `$@` and `$*`. */
if [ ! -e "${GIT_ROOT}${SUSUWU_INCLUDE_PATH}" ]; then
SUSUWU_INCLUDE_ERROR "${SUSUWU_INCLUDE_PATH}" "not found"
elif [ ! -x "${GIT_ROOT}${SUSUWU_INCLUDE_PATH}" ] || ! . "${GIT_ROOT}${SUSUWU_INCLUDE_PATH}"; then
SUSUWU_INCLUDE_ERROR "${SUSUWU_INCLUDE_PATH}" "not executable"
fi
}
SUSUWU_INCLUDE "./sh/Macros.sh" #/* SUSUWU_DEFAULT_BRANCH() SUSUWU_PRINT() SUSUWU_PROCESS_ABORT_ON_FIRST_ERROR() SUSUWU_PROCESS_S() SUSUWU_PROCESS_VERBOSE() SUSUWU_PRODUCTION_USE() SUSUWU_SH_* */
SUSUWU_INCLUDE "./sh/make.sh" #/* SUSUWU_BUILD_CTAGS SUSUWU_BUILD_OBJECTS() SUSUWU_BUILD_EXECUTABLE() SUSUWU_DEPENDENCY_INCLUDE() SUSUWU_INSTALL() SUSUWU_PROCESS_CLEAN_REBUILD() SUSUWU_PROCESS_MINGW() SUSUWU_PROCESS_RELEASE_DEBUG() SUSUWU_SETUP_BUILD_FLAGS() SUSUWU_SETUP_CXX() SUSUWU_SETUP_BINDIR() SUSUWU_SETUP_OBJDIR() SUSUWU_SETUP_OUTPUT() SUSUWU_TEST_BASH() SUSUWU_TEST_OUTPUT() SUSUWU_UNINSTALL() */
SUSUWU_PRINT "$(SUSUWU_SH_NOTICE)" "(C) 2024 Swudu Susuwu, dual licenses: choose [GPLv2](./LICENSE_GPLv2) or [Apache 2](./LICENSE), allows all uses."
THIS_DEFAULT_BRANCH="$(SUSUWU_DEFAULT_BRANCH "trunk")"
SUSUWU_PRINT "$(SUSUWU_SH_WARNING)" "$(SUSUWU_SH_QUOTE "CODE" "git branch") is \"$(SUSUWU_SH_QUOTE "CURRENT" "experimental")\" (which is unstable & sets $(SUSUWU_SH_QUOTE "CODE" "-DSUSUWU_EXPERIMENTAL")); for production use, execute $(SUSUWU_SH_QUOTE "CODE" "git switch $(SUSUWU_SH_QUOTE "PROPOSED" "${THIS_DEFAULT_BRANCH}")")."
export FLAGS_USER="-DSUSUWU_EXPERIMENTAL -DSUSUWU_DEFAULT_BRANCH=\"${THIS_DEFAULT_BRANCH}\"" #/* Usage: "Macro flags" from `./README.md#optionssetup`. */
export FLAGS_ANALYSIS="-Wall -Wno-unused-function -Wno-unused-function -Wextra -Wno-unused-parameter -Wno-ignored-qualifiers -Wpedantic" #/*TODO: -`-Wno-*`, +`-Werror` */
export FLAGS_RELEASE="-fomit-frame-pointer -DNDEBUG -O2" #/* without frame pointer (pointer used for stacktraces), without `assert(...)`/`SUSUWU_DEBUG(...)`/`SUSUWU_NOTICE(...)`, with optimization level 2 */
export CXXFLAGS_DEBUG="-std=c++11" #/* ensure unit tests pass with C++11 support as max */
export FLAGS_DEBUG="-g -Og" #/* in MSVC is `/Zi /Od`: symbols for `gdb`/`lldb` use, optimizations compatible with `-g`/`-fsan*` */
export FLAGS_DEBUG="${FLAGS_DEBUG} -fno-omit-frame-pointer" #/* thus optimization won't remove stacktraces: https://stackoverflow.com/questions/48234575/g-will-fno-omit-frame-pointer-be-effective-if-specified-before-o2-or-o3 https://clang.llvm.org/docs/MemorySanitizer.html */
#export FLAGS_DEBUG="${FLAGS_DEBUG} -fno-optimize-sibling-calls" #/* Don't inline functions. Does extra stacktraces. */
export FLAGS_FSAN="-fsanitize=address -fno-sanitize-recover=all -fsanitize=float-divide-by-zero -fsanitize=float-cast-overflow -fno-sanitize=null -fno-sanitize=alignment"
#export FLAGS_FSAN="${FLAGS_FSAN} -fsanitize=undefined" #/* causes 'cannot locate symbol "__ubsan_handle_function_type_mismatch_abort"' */
export FLAGS_TENSORFLOW="-std=c++17 -DSUSUWU_USE_TENSORFLOW" #/* `./cxx/*` uses `#ifdef SUSUWU_USE_TENSORFLOW`, TensorFlow requires C++17 (for `std::optional`) */
C_SOURCE_PATH="./c/" #/* Usage: replace with directory root for _C_ source code */
CXX_SOURCE_PATH="./cxx/" #/* Usage: replace with directory root for _C++_ source code */
if [ -n "${GITHUB_ACTIONS}" ]; then
SUSUWU_IS_VIRTUAL=true #/* `build.sh` is executed through [GitHub Workflows](https://docs.github.com/en/actions/writing-workflows/about-workflows). TODO: `|| <test for other amnesiac environments, such as Docker>` */
else
SUSUWU_IS_VIRTUAL=false
fi
SUSUWU_INSTALL_TENSORFLOW="${SUSUWU_INSTALL_TENSORFLOW:-"${SUSUWU_IS_VIRTUAL}"}" #/* If virtual, install prerequisites for `cxx/ClassTensorFlowCns.hxx`; use `export SUSUWU_INSTALL_TENSORFLOW=false` to reduce resource use, or `export SUSUWU_INSTALL_TENSORFLOW=true` to install prerequisites on all computers (default is to avoid changes to system unless virtual) */
if [ -f "${0}.bash" ] && [ -n "${BASH_VERSION}" ] || [ "${0##*.}" = "bash" ]; then #/* Notice: assumes left-associative */
SUSUWU_INSTALL_TENSORFLOW=false #/* `SUSUWU_TEST_BASH` should not reinstall. TODO: ensure compatible (does not prevent install) with ports to `/bin/bash` */
fi
FLAGS_USER_BACKUP="${FLAGS_USER}" #/* Allows to undo insertion of TensorFlow includes */
if [ true = "${SUSUWU_INSTALL_TENSORFLOW}" ]; then
SUSUWU_PRINT "$0" "$(SUSUWU_SH_NOTICE)" "Was executed through one of GitHub's Workflows (or user set $(SUSUWU_SH_QUOTE "VAR" "SUSUWU_INSTALL_TENSORFLOW")), will auto-install $(SUSUWU_SH_QUOTE "CODE" "libeigen3-dev") and $(SUSUWU_SH_QUOTE "CODE" "libtensorflow")."
if ! (sudo apt -y install libtensorflow || sudo apt -y install libtensorflow-dev #|| git clone https://github.com/tensorflow/tensorflow.git --depth 1
); then #/* If normal `apt` is not sufficient to install `libtensorflow` */
if [ true = "${USE_GOOGLEAPIS_TENSORFLOW}" ]; then
LIBTENSORFLOW_TAR="libtensorflow-cpu-linux-x86_64-2.11.0.tar.gz"
wget --no-verbose "https://storage.googleapis.com/tensorflow/libtensorflow/${LIBTENSORFLOW_TAR}" && \
tar xzf "${LIBTENSORFLOW_TAR}" && \
ls -a include && ls -a include/tensorflow && ls -a include/tensorflow/core && ls -a include/tensorflow/third-party && \
sudo mv lib/* /usr/lib/ #&& \
# sudo mv include/* /usr/include/ && \
# ls /usr/include/tensorflow/
git clone https://github.com/openxla/xla.git --depth 1 #/* `libtensorflow` does not include `xla` */
elif [ true = "${SUSUWU_BUILD_TENSORFLOW}" ]; then #/* prepackaged `libtensorflow` does not have C++ headers; use shallow clone of TensorFlow source */
git clone https://github.com/tensorflow/tensorflow.git --depth 1
command -v bazel || sudo apt install bazel || {
curl -LO "https://github.com/bazelbuild/bazelisk/releases/latest/download/bazelisk-linux-amd64"
mkdir -p "${GITHUB_WORKSPACE}/bin/"
mv bazelisk-linux-amd64 "${GITHUB_WORKSPACE}/bin/bazel"
chmod +x "${GITHUB_WORKSPACE}/bin/bazel"
PATH="${PATH} ${GITHUB_WORKSPACE}/bin"
}
SUSUWU_PWD_BACKUP="$(pwd)"
command -v bazel && cd ./tensorflow/third_party/xla/third_party/eigen3/ && {
bazel build
cd "${SUSUWU_PWD_BACKUP}" || exit 1
}
else #/* `libtensorflow` C++ package */
wget --no-verbose "https://github.com/ika-rwth-aachen/libtensorflow_cc/releases/download/v2.13.0/libtensorflow-cc_2.13.0_$(dpkg --print-architecture).deb"
sudo dpkg -i "libtensorflow-cc_2.13.0_$(dpkg --print-architecture).deb"
sudo ldconfig
fi
fi
fi
if SUSUWU_DEPENDENCY_INCLUDE "-I" "libtensorflow" "./" "tensorflow/core/" "sudo apt install libtensorflow"; then
TENSORFLOW_PATH_PREFIX=""
elif SUSUWU_DEPENDENCY_INCLUDE "-I" "tensorflow" "tensorflow/" "tensorflow/core/" "git clone https://github.com/tensorflow/tensorflow.git --depth 1"; then
TENSORFLOW_PATH_PREFIX="tensorflow/third_party/"
TENSORFLOW_FULL_PATH_PREFIX="${SUSUWU_DEPENDENCY_INCLUDE_PATH}third_party/"
fi
TENSORFLOW_INCLUDE_PATH="${SUSUWU_DEPENDENCY_INCLUDE_PATH}" #/* `TENSORFLOW_INCLUDE_PATH=$(SUSUWU_DEPENDENCY_INCLUDE ...)` discards `SUSUWU_DEPENDENCY_INCLUDE`'s changes to env vars */
TENSORFLOW_FULL_PATH_PREFIX="${TENSORFLOW_INCLUDE_PATH}third_party/"
if [ -n "${TENSORFLOW_INCLUDE_PATH}" ]; then
SUSUWU_DEPENDENCY_INCLUDE "-I" "tensorflow:xla" "${TENSORFLOW_PATH_PREFIX}xla/" "xla/" "https://github.com/openxla/xla.git --depth 1" && {
XLA_SOURCE_PATH="${SUSUWU_DEPENDENCY_INCLUDE_PATH}"
XLA_PATH_PREFIX="${TENSORFLOW_PATH_PREFIX}xla/third_party/"
XLA_FULL_PATH_PREFIX="${TENSORFLOW_FULL_PATH_PREFIX}xla/third_party/"
SUSUWU_DEPENDENCY_INCLUDE "-I" "tensorflow:xla:eigen" "${XLA_PATH_PREFIX}eigen3/" "Eigen/" "cd ${XLA_FULL_PATH_PREFIX}eigen3/ && bazel build"
EIGEN_INCLUDE_PATH="${SUSUWU_DEPENDENCY_INCLUDE_PATH}"
}
SUSUWU_DEPENDENCY_INCLUDE "-I" "tensorflow:tsl" "${TENSORFLOW_PATH_PREFIX}xla/third_party/tsl/" "tsl/" ""
if [ -z "${EIGEN_INCLUDE_PATH}" ]; then
SUSUWU_DEPENDENCY_INCLUDE "-I" "eigen" "eigen3/" "unsupported/Eigen/" "sudo apt -y install libeigen3-dev eigen" ||
SUSUWU_DEPENDENCY_INCLUDE "-I" "eigen" "eigen3/" "Eigen/" "git clone https://github.com/PX4/eigen.git --depth 1"
if [ -z "${SUSUWU_DEPENDENCY_INCLUDE_PATH}" ] &&
[ true = "${SUSUWU_INSTALL_TENSORFLOW}" ]; then
if sudo apt -y install libeigen3-dev || sudo apt -y install eigen || git clone https://github.com/PX4/eigen.git --depth 1; then
SUSUWU_DEPENDENCY_INCLUDE "-I" "eigen" "eigen3/" "unsupported/Eigen/" "" ||
SUSUWU_DEPENDENCY_INCLUDE "-I" "eigen" "eigen3/" "Eigen/" ""
fi
fi
fi
if [ -z "${ABSEIL_INCLUDE_PATH}" ]; then #/* Allows custom path with `ABSEIL_INCLUDE_PATH=path; ./build.sh` */
SUSUWU_DEPENDENCY_INCLUDE "-I" "abseil" "absl/" "status/status.h" "git clone https://github.com/abseil/abseil-cpp.git --depth 1 && sudo apt install libabsl-dev"
if [ -z "${SUSUWU_DEPENDENCY_INCLUDE_PATH}" ] &&
[ true = "${SUSUWU_INSTALL_TENSORFLOW}" ]; then
git clone https://github.com/abseil/abseil-cpp.git --depth 1 && sudo apt install libabsl-dev
SUSUWU_DEPENDENCY_INCLUDE "-I" "abseil" "absl/" "status/status.h" ""
fi
fi
ML_DTYPES_ROOT="xla/third_party/py/ml_dtypes/"
ML_DTYPES_PATH="${TENSORFLOW_PATH_PREFIX}${ML_DTYPES_ROOT}"
ML_DTYPES_FULL_PATH="${TENSORFLOW_FULL_PATH_PREFIX}${ML_DTYPES_ROOT}"
ML_DTYPES_PREFIX="ml_dtypes/include/"
# if [ -e "../${ML_DTYPES_PATH}${ML_DTYPES_PREFIX}float8.h" ]; then
# echo "Found: ../${ML_DTYPES_PATH}${ML_DTYPES_PREFIX}float8.h"
# fi
if ! SUSUWU_DEPENDENCY_INCLUDE "-I" "tensorflow:ml_dtypes" "${ML_DTYPES_PATH}" "${ML_DTYPES_PREFIX}float8.h" "cd ${ML_DTYPES_FULL_PATH} && bazel build"; then #/* If can't use `ml_dtypes` from `tensorflow` */
ML_DTYPES_GIT="https://github.com/jax-ml/ml_dtypes.git"
if [ ! -d "ml_dtypes" ] && [ true = "${SUSUWU_INSTALL_TENSORFLOW}" ]; then
ML_DTYPES_COMMIT_HASH="00d98cd92ade342fef589c0470379abb27baebe9" #/* TODO: extract compatible commit hash from `workspace.bzl` */
if [ -z "${ML_DTYPES_COMMIT_HASH}" ]; then
git clone "${ML_DTYPES_GIT}" --depth 1 #/* Possible mismatch if commit is too new */
else
if git init ml_dtypes && cd ml_dtypes; then
git remote add origin ${ML_DTYPES_GIT}
git fetch --depth 1 origin ${ML_DTYPES_COMMIT_HASH}
git checkout FETCH_HEAD
cd ../
fi
fi
fi
if ! SUSUWU_DEPENDENCY_INCLUDE "-I" "jax-ml:ml_dtypes" "ml_dtypes/" "${ML_DTYPES_PREFIX}float8.h" "git clone ${ML_DTYPES_GIT} --depth 1"; then #/* If can't use `ml_dtypes` from `jax-ml` */
ML_DTYPES_FALLBACK_PREFIX="tensorflow/core/platform/"
ML_DTYPES_FALLBACK="${TENSORFLOW_INCLUDE_PATH}${ML_DTYPES_FALLBACK_PREFIX}"
if [ -e "${ML_DTYPES_FALLBACK}float8.h" ]; then # If fallback path has `float8.h`
SUSUWU_PRINT "$0" "$(SUSUWU_SH_WARNING)" "As last resort, will use $(SUSUWU_SH_QUOTE "PATH" "${ML_DTYPES_FALLBACK}") (which has $(SUSUWU_SH_QUOTE "PATH" "float8.h")) as path for $(SUSUWU_SH_QUOTE "CODE" "ml_dtypes"). If this causes more $(SUSUWU_SH_QUOTE "CODE" "#include") errors, execute $(SUSUWU_SH_QUOTE "CODE" "cd ${XLA_SOURCE_PATH} && git reset --hard HEAD") or $(SUSUWU_SH_QUOTE "CODE" "find \"${XLA_SOURCE_PATH}\" -type f -exec sed \"s|\\\"${ML_DTYPES_PREFIX}|\\\"${ML_DTYPES_FALLBACK_PREFIX}|\" -i'' {} +") to undo."
find "${XLA_SOURCE_PATH}" -type f -exec sed "s|\"${ML_DTYPES_PREFIX}|\"${ML_DTYPES_FALLBACK_PREFIX}|" -i'' {} + # [error: 'ml_dtypes/include/float8.h' file not found](https://github.com/tensorflow/tensorflow/issues/93130) fix. #/* TODO: filter with `grep "\.\(h\|cc\)$"` */
export SUSUWU_USED_ML_DTYPES_SED=true
fi
fi
fi
TENSORFLOW_HAS_PROTOS=$(test -f "${TENSORFLOW_INCLUDE_PATH}tensorflow/core/framework/types.pb.h")
if [ true = "${SUSUWU_INSTALL_TENSORFLOW}" ] && ! ${TENSORFLOW_HAS_PROTOS}; then
if ! command -v protoc >/dev/null; then
sudo apt install protobuf || sudo snap install protobuf
fi
TENSORFLOW_FW="tensorflow/core/framework/"
# TENSORFLOW_INCLUDE_PATH_FW="${TENSORFLOW_INCLUDE_PATH}${TENSORFLOW_FW}"
# for PROTO_SOURCE_CODE in "${TENSORFLOW_INCLUDE_PATH}${TENSORFLOW_FW}"*.proto; do
# if [ ! -e "${PROTO_SOURCE_CODE%proto}pb.h" ]; then
# protoc --cpp_out="${TENSORFLOW_INCLUDE_PATH_FW}" --proto_path="${TENSORFLOW_INCLUDE_PATH}" --experimental_allow_proto3_optional "${PROTO_SOURCE_CODE##*"${TENSORFLOW_INCLUDE_PATH}"}" #/* "../tensorflow/tensorflow/core/framework/tensor_shape.h:*:10: fatal error: 'tensorflow/core/framework/*.pb.h' file not found" workaround */
SUSUWU_PWD_BACKUP="$(pwd)"
cd "${TENSORFLOW_INCLUDE_PATH}" || exit 1
# ls "${TENSORFLOW_FW}" | grep ".proto"
# protoc --cpp_out="${TENSORFLOW_INCLUDE_PATH_FW}" --proto_path="${TENSORFLOW_INCLUDE_PATH}" "${TENSORFLOW_FW}*.proto" #/* "../tensorflow/tensorflow/core/framework/tensor_shape.h:*:10: fatal error: 'tensorflow/core/framework/*.pb.h' file not found" workaround */
# if [ ! -e "${TENSORFLOW_INCLUDE_PATH_FW}types.pb.h" ]; then
# protoc --cpp_out="${TENSORFLOW_INCLUDE_PATH_FW}" --proto_path="${TENSORFLOW_INCLUDE_PATH}" "${TENSORFLOW_FW}types.proto" #/* "../tensorflow/tensorflow/core/framework/tensor_shape.h:22:10: fatal error: 'tensorflow/core/framework/types.pb.h' file not found" workaround */
# fi
# if [ ! -e "${TENSORFLOW_INCLUDE_PATH_FW}function.pb.h" ]; then
# protoc --cpp_out="${TENSORFLOW_INCLUDE_PATH_FW}" --proto_path="${TENSORFLOW_INCLUDE_PATH}" "${TENSORFLOW_FW}function.proto" #/* " ../tensorflow/tensorflow/c/eager/abstract_function.h:19:10: fatal error: 'tensorflow/core/framework/function.pb.h' file not found" */
# fi
for PROTO_SOURCE_CODE in "${TENSORFLOW_FW}"*.proto; do
if [ ! -e "${PROTO_SOURCE_CODE%proto}pb.h" ]; then
protoc --cpp_out="${TENSORFLOW_FW}" --proto_path=./ --experimental_allow_proto3_optional "${PROTO_SOURCE_CODE}" #/* "../tensorflow/tensorflow/core/framework/tensor_shape.h:*:10: fatal error: 'tensorflow/core/framework/*.pb.h' file not found" workaround */
fi
done
if [ -d "${XLA_SOURCE_PATH}" ]; then
TENSORFLOW_PROTOBUF="xla/tsl/protobuf/"
cd "${SUSUWU_PWD_BACKUP}" && cd "${XLA_SOURCE_PATH}" || exit 1
# ls "${TENSORFLOW_PROTOBUF}" | grep ".proto"
for PROTO_SOURCE_CODE in "${TENSORFLOW_PROTOBUF}"*.proto; do
if [ ! -e "${PROTO_SOURCE_CODE%proto}pb.h" ]; then
protoc --cpp_out="${TENSORFLOW_PROTOBUF}" --proto_path=./ "${PROTO_SOURCE_CODE}" #/* "../tensorflow/third_party/xla/xla/tsl/platform/status.h:38:10: fatal error: 'xla/tsl/protobuf/error_codes.pb.h' file not found" workaround */
fi
done
fi
cd "${SUSUWU_PWD_BACKUP}" || exit 1
fi
fi
SUSUWU_PROCESS_S $@ #/* Usage: `./build.sh -q`. Silences `SUSUWU_SH_NOTICE` ("Notice:") messages, prevents `set -x`. */
SUSUWU_PROCESS_VERBOSE $@ #/* Usage: `./build.sh --verbose`. Enables `SUSUWU_SH_DEBUG` ("Debug:") messages, forces `set -x`. */
SUSUWU_PRODUCTION_USE "${THIS_DEFAULT_BRANCH}"
SUSUWU_PROCESS_MINGW "$@" #/* Usage: `apt install mingw wine && ./build.sh --mingw`. [MinGW cross-builds to Windows.] */
SUSUWU_SETUP_CXX #/* Analogous to `make config` */
SUSUWU_PROCESS_RELEASE_DEBUG "$@" #/* Usage: `./build.sh --debug` or `./build.sh --release` */
SUSUWU_PROCESS_ABORT_ON_FIRST_ERROR "$@" #/* Usage: `./build.sh --abort-on-first-error`. If an object fails to build, `exit 1`. */
SUSUWU_SETUP_OBJDIR "./obj/" #/* Usage: replace with directory root for new objects */
SUSUWU_SETUP_BINDIR "./bin/" #/* Usage: replace with directory root for new executables */
SUSUWU_SETUP_OUTPUT "Susuwu" #/* Usage: replace with name of your program */
SUSUWU_PROCESS_CLEAN_REBUILD "$@" #/* Usage: `./build.sh --clean` or `./build.sh --rebuild` */
CFLAGS_BACKUP="${CFLAGS}" #/* Allows to undo insertion of TensorFlow includes */
CXXFLAGS_BACKUP="${CXXFLAGS}" #/* Allows to undo insertion of TensorFlow includes */
SUSUWU_SETUP_BUILD_FLAGS #/* Analogous to `make config` */
if [ -n "${TENSORFLOW_INCLUDE_PATH}" ]; then #/* If `libtensorflow` was found */
SUSUWU_TENSORFLOW_TEST_PATH="${CXX_SOURCE_PATH}ClassTensorFlowCns.cxx"
#shellcheck disable=SC2086 #`"${CXXFLAGS}"` gives "clang++: error: language not recognized"
if ${CXX} ${CXXFLAGS} ${FLAGS_TENSORFLOW} -c "${SUSUWU_TENSORFLOW_TEST_PATH}"; then #/* TODO: ` 2>/dev/null; then` as soon as difficult-to-parse errors such as `fatal error: "unsupported/Eigen/CXX11/Tensor' file not found" have solutions */
CXXFLAGS="${CXXFLAGS} ${FLAGS_TENSORFLOW}"
LDFLAGS="${LDFLAGS} -labsl_status -labsl_strings -labsl_base"
# LDFLAGS="${LDFLAGS} -lprotobuf" #/* `-lprotobuf` gives "SUMMARY: AddressSanitizer: SEGV generated_message_reflection.cc in google::protobuf::(anonymous namespace)::AddDescriptorsImpl(google::protobuf::internal::DescriptorTable const*)" */
LDFLAGS="${LDFLAGS} -ltensorflow_cc -ltensorflow_framework"
# #LDFLAGS="${LDFLAGS} -l:libabsl_status.so"
#if [ -d "/usr/local/lib/" ]; then
# #LDFLAGS="${LDFLAGS} -L/usr/local/lib"
# if [ -f /usr/local/lib/libtensorflow_framework.so ] || [ -f /usr/local/lib/libtensorflow_framework.so.2 ]; then
# ls /usr/local/lib/libtensorflow*
SUSUWU_PRINT "$0" "$(SUSUWU_SH_NOTICE)" "$(SUSUWU_SH_QUOTE "CODE" "${CXX} ${SUSUWU_TENSORFLOW_TEST_PATH}") passed, will enable $(SUSUWU_SH_QUOTE "CODE" "CXXFLAGS=\"\${CXXFLAGS} ${FLAGS_TENSORFLOW}\"")."
else
FLAGS_USER="${FLAGS_USER_BACKUP}" #/* Undo insertion of TensorFlow includes */
CFLAGS="${CFLAGS_BACKUP}" #/* Undo insertion of TensorFlow includes */
CXXFLAGS="${CXXFLAGS_BACKUP}" #/* Undo insertion of TensorFlow includes */
SUSUWU_SETUP_BUILD_FLAGS #/* Analogous to `make config` */
SUSUWU_PRINT "$0" "$(SUSUWU_SH_NOTICE)" "$(SUSUWU_SH_QUOTE "CODE" "${CXX} ${CXXFLAGS} ${SUSUWU_TENSORFLOW_TEST_PATH}") failed, will not enable $(SUSUWU_SH_QUOTE "CODE" "CXXFLAGS=\"\${CXXFLAGS} ${FLAGS_TENSORFLOW}\"") (skipped). If $(SUSUWU_SH_QUOTE "CODE" "libtensorflow") is installed, insert $(SUSUWU_SH_QUOTE "CODE" "${FLAGS_TENSORFLOW}") into $(SUSUWU_SH_QUOTE "CODE" "$0:FLAGS_USER"). To troubleshoot, use $(SUSUWU_SH_QUOTE "CODE" "cd ${TENSORFLOW_INCLUDE_PATH} && ./configure")"
# ${SUSUWU_USED_ML_DTYPES_SED} && find "${XLA_SOURCE_PATH}" -type f -exec sed "s|\"${ML_DTYPES_FALLBACK_PREFIX}|\"${ML_DTYPES_PREFIX}|" -i'' {} + # [error: 'ml_dtypes/include/float8.h' file not found](https://github.com/tensorflow/tensorflow/issues/93130) fix. #TODO: exclude 'third_party/xla/xla/tsl/platform/resource_loader.h'
fi
fi
SUSUWU_PROCESS_INCLUDES "${CXX_SOURCE_PATH}Class*.hxx" "${CXX_SOURCE_PATH}Macros.hxx"
#shellcheck disable=SC2119 #Specifics were removed from `SUSUWU_BUILD_CTAGS` call to match `./hooks/pre-commit`.
SUSUWU_BUILD_CTAGS #/* Usage: `apt install ctags vim && vim -t tagToSearchFor` */
SUSUWU_BUILD_OBJECTS "${CC}" "${CFLAGS}" ".c" "${C_SOURCE_PATH}rfc6234/sha1.c" "${C_SOURCE_PATH}rfc6234/sha224-256.c" "${C_SOURCE_PATH}rfc6234/sha384-512.c"
SUSUWU_BUILD_OBJECTS "${CXX}" "${CXXFLAGS}" ".cxx" "${CXX_SOURCE_PATH}*.cxx"
SUSUWU_BUILD_EXECUTABLE
SUSUWU_STATUS=$?
SUSUWU_BUILD_STATUS=${SUSUWU_STATUS}
SUSUWU_TEST_OUTPUT #/* Analogous to `make test` or `make execute` */
SUSUWU_STATUS=$?
[ 0 -eq ${SUSUWU_BUILD_STATUS} ] && SUSUWU_INSTALL "${USRBIN}" && SUSUWU_UNINSTALL "${USRBIN}" #/* Analogous to `make install && make uninstall`. Won't clobber files which exist. */
[ 0 -eq ${SUSUWU_STATUS} ] && {
SUSUWU_TEST_BASH || SUSUWU_STATUS=$?
}
exit ${SUSUWU_STATUS}
Utils (/bin/sh
code)
less sh/Macros.sh
(https://github.com/SwuduSusuwu/SusuLib/blob/preview/sh/Macros.sh)
#!/bin/sh
#
#/* (C) 2024 Swudu Susuwu, dual licenses: choose [_GPLv2_](./LICENSE_GPLv2) or [_Apache 2_](./LICENSE) (allows all uses).
# * Based on `../cxx/Macros.hxx`.
# * TODO: [map options/flags (which `SUSUWU_PROCESS_*` functions use) to descriptions (for `--help` output.)](https://github.com/SwuduSusuwu/SusuLib/issues/24) */
export SUSUWU_SH_CONSOLE_PARAMS="$*" #/* For functions which are not passed `$@` */
SUSUWU_SH_HAS_PARAM() ( #/* Usage: `if SUSUWU_SH_HAS_PARAM "--param [...]" "$@";`. [This processes params passed to `${0}`.] */
if [ "$#" -eq 1 ]; then #/* If function was not passed `$@`,
SUSUWU_SH_HAS_PARAM "${1}" "${SUSUWU_SH_CONSOLE_PARAMS}" # * use stored values */
return $?
fi
PARAM_Q="${1}"; shift;
for PARAM_W in "$@"; do
for PARAM in ${PARAM_Q}; do #/* The param can have aliases, such as short forms. */
if [ "${PARAM}" = "${PARAM_W}" ]; then
return 0
fi
done
done
return 1
)
SUSUWU_SH_REMOVE_PARAM() ( #/* Usage: `echo "$(SUSUWU_SH_REMOVE_PARAM "--unwanted-param" "$@")"`. [This processes params passed to `${0}`.] */
PARAM=${1}; shift;
NEW_PARAMS=""
SUSUWU_SH_REMOVE_PARAM_FOUND=1
for PARAM_W in "$@"; do
if [ "${PARAM}" != "${PARAM_W}" ]; then
SUSUWU_SH_REMOVE_PARAM_FOUND=0
if [ -z "${NEW_PARAMS}" ]; then
NEW_PARAMS="${PARAM_W}"
elif [ -n "${NEW_PARAMS}" ]; then
NEW_PARAMS="${NEW_PARAMS} ${PARAM_W}" #/* TODO: spaces? */
fi
fi
done
echo "${NEW_PARAMS}"
return ${SUSUWU_SH_REMOVE_PARAM_FOUND}
)
SUSUWU_STATIC_IS_PREVIEW() ( #/* Usage; `if SUSUWU_IS_PREVIEW_CONSTANT; then EXPERIMENTAL_CODE(); fi`. Is fast (versus `SUSUWU_IS_PREVIEW()`, but must hardcode for production (or release) versus "experimental" (or "preview") branches. */
return 0 #/* TODO: for production (or release) branches, `return 1` */
)
SUSUWU_IS_PREVIEW() ( #/* Usage; `if SUSUWU_IS_PREVIEW ["<default branch>"]; then EXPERIMENTAL_CODE(); fi` */
if command -v git >/dev/null && git rev-parse --is-inside-work-tree >/dev/null 2>&1; then #test -d ".git/"; then
THIS_BRANCH="$(git rev-parse --abbrev-ref HEAD)" #/* Compute current branch */
if [ ! "HEAD" = "${THIS_BRANCH}" ]; then #/* If `git rebase` (or "detached HEAD"); unknown branch, skip */
if [ "$(SUSUWU_DEFAULT_BRANCH "${1}")" = "${THIS_BRANCH}" ]; then #/* If production (or release) branch */
return 1
else #/* else is "experimental" (or "preview") branch. */
return 0
fi
fi
fi
SUSUWU_STATIC_IS_PREVIEW #/* Use hardcoded value. */
)
SUSUWU_PATH_SUFFIX_SLASH() ( #/* Usage: `OBJDIR="$(SUSUWU_ENSURE_DIR_SLASH "${OBJDIR}")"` */
DIR=${1}
if [ "${DIR}" = "${DIR%/}" ]; then #/* "%/" removes slash; if equal after this,
DIR="${DIR}/" # * ... original doesn't have '/', append '/'. */
fi
echo "${DIR}" #/* return with slash */
)
SUSUWU_PATH_AFFIX_DOTSLASH() ( #/* Usage: `BINDIR="$(SUSUWU_PATH_AFFIX_DOTSLASH "${BINDIR}")"` */
DIR=${1}
case "${DIR}" in
./*) #/* If original has "./", continue. */
;;
*) #/* If default (if original doesn't match "./"),
DIR="./${DIR}" # * ... affix "./" */
;;
esac
echo "${DIR}" #/* return with "./" */
)
SUSUWU_PATH_UNAMBIGUOUS() ( #/* Usage: `echo "USRBIN=\"$(SUSUWU_UNAMBIGUOUS_PATH "${USRBIN}")\"` */
if [ "$(realpath -q "${1}")" != "${1}" ]; then #/* If relative path,
SUSUWU_PATH_AFFIX_DOTSLASH "${1}" # * ensure path starts with "./" */
else
echo "${1}"
fi
)
SUSUWU_PATH_SHOULD_NOT_EXIST() { #/* Usage: `SUSUWU_PATH_SHOULD_NOT_EXIST "<function>" "<path>" && cp "${0}" "<path>"` */
if [ -e "${2}" ]; then
SUSUWU_PRINT "${1}: SUSUWU_PATH_SHOULD_NOT_EXIST()" "$(SUSUWU_SH_ERROR)" "$(SUSUWU_SH_QUOTE "PATH" "${2}") exists. Use $(SUSUWU_SH_QUOTE "CODE" "mv \"${2}\" \"${2}.bak\"") (or $(SUSUWU_SH_QUOTE "CODE" "rm \"${2}\"")) and re-execute $(SUSUWU_SH_QUOTE "CODE FUNCTION" "${1}") (perhaps use $(SUSUWU_SH_QUOTE "CODE" "${0}")) to continue."
exit 1
fi
return 0
}
SUSUWU_ESCAPE_SPACES() ( #/* Usage: `SUSUWU_OBJECTLIST="${SUSUWU_OBJECTLIST} $(SUSUWU_ESCAPE_SPACES "${OBJECT}"). */
# echo $(echo "$@" | sed 's/ /\\\ /') #/* Error: `sed not found`, although is installed. */
# echo "\"${@}\""; #/* Error: if `OBJECT="obj/main.o"`, `SUSUWU_BUILD_EXECUTABLE()` gives `clang++: error: no such file or directory: '"obj/main.o"'` (uses path with literal quotes). */
NEW_PATH=""
for OLD_PATH_TOKEN in "$@"; do #/* Split old path into tokens */
if [ -z "${NEW_PATH}" ]; then
NEW_PATH="${OLD_PATH_TOKEN}"
elif [ -n "${NEW_PATH}" ]; then
NEW_PATH="${NEW_PATH}\\ ${OLD_PATH}" #/* Error: if `OBJECT="obj/long path.o"`, `SUSUWU_BUILD_EXECUTABLE()` gives `clang++: error: no such file or directory: 'obj/long\'\nclang++: error: no such file or directory: 'path.o'`. TODO: fix this. Is not a regression (`Macros.sh` never supported spaces; `build.sh`'s paths don't have spaces.) */
fi
done
echo "${NEW_PATH}"
)
SUSUWU_ESCAPE_QUOTED() ( #/* Usage: `echo "\"param\": \"$(SUSUWU_ESCAPE_QUOTED "${VALUE}")\"" >> out.json`. */
echo "$@" | sed 's/"/\\"/g' | sed 's/\\/\\\\/g'
)
SUSUWU_STR_TOKEN_FIRST() ( # Usage: `SUSUWU_STR_TOKEN_FIRST "<input>" "<delimiter>". Purpose: splits <input> on <delimiter>, returns all before last <delimiter>.
echo "${1%${2}*}" #/* TODO: allow <delimiter>="\033". */
) #/* Analogous to echo "${1}" | sed "s/\(${2}[^${2}]*\)\$//" #shellcheck disable=SC2001 #https://www.shellcheck.net/wiki/SC2001 */
[ "uwu" = "$(SUSUWU_STR_TOKEN_FIRST "uwu" ":")" ] || echo "[$0: SUSUWU_STR_TOKEN_FIRST(): Error: logic_error; test failed.]"
[ "uwu:q" = "$(SUSUWU_STR_TOKEN_FIRST "uwu:q:mukyu" ":")" ] || echo "[$0: SUSUWU_STR_TOKEN_FIRST(): Error: logic_error; test failed.]"
[ "uwu^q" = "$(SUSUWU_STR_TOKEN_FIRST "uwu^q^mukyu" "^")" ] || echo "[$0: SUSUWU_STR_TOKEN_FIRST(): Error: logic_error; test failed.]"
#[ "uwu\033q" = "$(SUSUWU_STR_TOKEN_FIRST "uwu\033q\033mukyu" "\\033")" ] || echo "[$0: SUSUWU_STR_TOKEN_FIRST(): Error: logic_error; test failed.]"
#[ "uwu$'\033'q" = "$(SUSUWU_STR_TOKEN_FIRST "uwu$'\033'q$'\033'mukyu" $'\033')" ] || echo "[$0: SUSUWU_STR_TOKEN_FIRST(): Error: logic_error; test failed.]"
SUSUWU_STR_TOKEN_LAST() ( #/* Usage: `SUSUWU_STR_TOKEN_LAST "<input>" "<delimiter>". Purpose: splits <input> on <delimiter>, returns all after last <delimiter>. */
RESULT="${1#*${2}}" #/* `RESULT` is all after first <delimiter>` */
if [ "${RESULT}" != "${1}" ]; then #/* if `RESULT` is not the input, 1 or more <delimiter> was found,
SUSUWU_STR_TOKEN_LAST "${RESULT}" "${2}" # * so search for next <delimiter>. */
else
echo "${RESULT}" #/* `RESULT` is all after last <delimiter> */
fi
) #/* Analogous to: echo "${1}" | sed "s/^.*${2}//" #shellcheck disable=SC2001 #https://www.shellcheck.net/wiki/SC2001 */
[ "mukyu" = "$(SUSUWU_STR_TOKEN_LAST "mukyu" ":")" ] || echo "[$0: SUSUWU_STR_TOKEN_LAST(): Error: logic_error; test failed.]"
[ "mukyu" = "$(SUSUWU_STR_TOKEN_LAST "uwu:q:mukyu" ":")" ] || echo "[$0: SUSUWU_STR_TOKEN_LAST(): Error: logic_error; test failed.]"
[ "mukyu" = "$(SUSUWU_STR_TOKEN_LAST "uwu^q^mukyu" "^")" ] || echo "[$0: SUSUWU_STR_TOKEN_LAST(): Error: logic_error; test failed.]"
[ "mukyu" = "$(SUSUWU_STR_TOKEN_LAST "uwu\033q\033mukyu" "\033")" ] || echo "[$0: SUSUWU_STR_TOKEN_LAST(): Error: logic_error; test failed.]"
#/* `SUSUWU_SH_<color>`. Notice: update [cxx/Macros.hxx](cxx/Macros.hxx) if you update those. */
#/* Usage: `SUSUWU_PRINT "${SUSUWU_SH_<warn-level>}" "${SUSUWU_SH_<color>}<message>${SUSUWU_SH_DEFAULT}"`. */
export SUSUWU_SH_DEFAULT="\033[0m"
export SUSUWU_SH_BLACK="\033[0;30m" #xterm256 "\033[38;5;0m"
export SUSUWU_SH_DARK_GRAY="\033[1;30m" #xterm256 "\033[38;5;8m"
export SUSUWU_SH_RED="\033[0;31m" #xterm256 "\033[38;5;1m"
export SUSUWU_SH_LIGHT_RED="\033[1;31m" #xterm256 "\033[38;5;9m"
export SUSUWU_SH_GREEN="\033[0;32m" #xterm256 "\033[38;5;2m"
export SUSUWU_SH_LIGHT_GREEN="\033[1;32m" #xterm256 "\033[38;5;10m"
export SUSUWU_SH_BROWN="\033[0;33m" #xterm256 "\033[38;5;3m"
export SUSUWU_SH_YELLOW="\033[1;33m" #xterm256 "\033[38;5;11m"
export SUSUWU_SH_BLUE="\033[0;34m" #xterm256 "\033[38;5;4m"
export SUSUWU_SH_LIGHT_BLUE="\033[1;34m" #xterm256 "\033[38;5;12m"
export SUSUWU_SH_PURPLE="\033[0;35m" #xterm256 "\033[38;5;5m"
export SUSUWU_SH_LIGHT_PURPLE="\033[1;35m" #xterm256 "\033[38;5;13m"
export SUSUWU_SH_CYAN="\033[0;36m" #xterm256 "\033[38;5;6m"
export SUSUWU_SH_LIGHT_CYAN="\033[1;36m" #xterm256 "\033[38;5;14m"
export SUSUWU_SH_LIGHT_GRAY="\033[0;37m" #xterm256 "\033[38;5;7m"
export SUSUWU_SH_WHITE="\033[1;37m" #xterm256 "\033[38;5;15m"
export SUSUWU_SH_RESET_WHITE="\033[0;1;37m" #xterm256 "\033[38;5;15m"
export SUSUWU_SH_BG_DEFAULT="\033[0;40m" #xterm256 "\033[48;5;0m"
export SUSUWU_SH_BG_RESET_BLACK="\033[0;40m" #xterm256 "\033[48;5;0m"
export SUSUWU_SH_BG_DARK_GRAY="\033[1;40m" #xterm256 "\033[48;5;8m"
export SUSUWU_SH_BG_RED="\033[0;41m" #xterm256 "\033[48;5;1m"
export SUSUWU_SH_BG_LIGHT_RED="\033[1;41m" #xterm256 "\033[48;5;9m"
export SUSUWU_SH_BG_GREEN="\033[0;42m" #xterm256 "\033[48;5;2m"
export SUSUWU_SH_BG_LIGHT_GREEN="\033[1;42m" #xterm256 "\033[48;5;10m"
export SUSUWU_SH_BG_BROWN="\033[0;43m" #xterm256 "\033[48;5;3m"
export SUSUWU_SH_BG_YELLOW="\033[1;43m" #xterm256 "\033[48;5;11m"
export SUSUWU_SH_BG_BLUE="\033[0;44m" #xterm256 "\033[48;5;4m"
export SUSUWU_SH_BG_LIGHT_BLUE="\033[1;44m" #xterm256 "\033[48;5;12m"
export SUSUWU_SH_BG_PURPLE="\033[0;45m" #xterm256 "\033[48;5;5m"
export SUSUWU_SH_BG_LIGHT_PURPLE="\033[1;45m" #xterm256 "\033[48;5;13m"
export SUSUWU_SH_BG_CYAN="\033[0;46m" #xterm256 "\033[48;5;6m"
export SUSUWU_SH_BG_LIGHT_CYAN="\033[1;46m" #xterm256 "\033[48;5;14m"
export SUSUWU_SH_BG_LIGHT_GRAY="\033[0;47m" #xterm256 "\033[48;5;7m"
export SUSUWU_SH_BG_WHITE="\033[1;47m" #xterm256 "\033[48;5;15m"
export SUSUWU_SH_CLOSE_="${SUSUWU_SH_DEFAULT}"
export SUSUWU_SH_TPUT_COMMAND="${SUSUWU_SH_TPUT_COMMAND:-tput}" #/* Usage: override with `export SUSUWU_SH_TPUT_COMMAND=<executable>` */
SUSUWU_SH_COLOR_COUNT() ( #/* Usage: `LOCAL_COLOR_COUNT=SUSUWU_SH_COLOR_COUNT` */
SUSUWU_SH_COLOR_COUNT_MINIMUM_COLORS=8
if [ -n "${SUSUWU_SH_COLOR_COUNT_CACHE}" ]; then
echo "${SUSUWU_SH_COLOR_COUNT_CACHE}"
test "${SUSUWU_SH_COLOR_COUNT_CACHE}" -ge "${SUSUWU_SH_COLOR_COUNT_MINIMUM_COLORS}" #/* test that color count is Greater or Equal to minimum count */
return $? #/* return `test`'s return value */
fi
if [ -n "${GITHUB_ACTIONS}" ]; then
echo "[$0: Warning: SUSUWU_SH_COLOR_COUNT(): detected environment is _GitHub_, will force color use. If this console (\`[ \"\${TERM}\" = \"${TERM}\" ]\`, which _GitHub_ uses) lacks colors such as ${SUSUWU_SH_BLUE}blue${SUSUWU_SH_DEFAULT} (shows glitches, or literal codes such as \"\\\033[0;34m\"), [post an issue](https://github.com/SwuduSusuwu/SusuLib/issues/new) about this.]" >&2
echo 8 #/* [_GitHub_ Autobuild](https://github.com/SwuduSusuwu/SusuLib/actions/runs/13209802112/job/36880995224) workaround. */
return 0 #/* TODO: include other tests (`return 1` if the console does not allow color codes) */
fi
if command -v "${SUSUWU_SH_TPUT_COMMAND}" >/dev/null; then #/* if installed `ncurses-utils`,
COLOR_COUNT="$(${SUSUWU_SH_TPUT_COMMAND} colors 2>/dev/null)" || COLOR_COUNT="-1"
echo "${COLOR_COUNT}"
test "${COLOR_COUNT}" -ge "${SUSUWU_SH_COLOR_COUNT_MINIMUM_COLORS}" # * test that color count is Greater or Equal to minimum count */
return $? #/* return `test`'s return value */
else
echo "[$0: Notice: SUSUWU_SH_COLOR_COUNT(): The program \`${SUSUWU_SH_TPUT_COMMAND}\` was not found; use \`apt install ncurses-utils\` to have \`SUSUWU_SH_*()\` compatible with all consoles.]" >&2
fi
for CONSOLE in "xterm-256color" "screen-256color" "tmux-256color" "rxvt-256color"; do
if [ "${TERM}" = "${CONSOLE}" ]; then
echo 256 #/* 256-color console */
return 0
fi
done
for CONSOLE in "xterm" "xterm-color" "screen" "screen-color" "tmux" "tmux-color" "linux" "rxvt" "rxvt-unicode"; do
if [ "${TERM}" = "${CONSOLE}" ]; then
echo 8 #/* standard 8-color console */
return 0 #/* supported console */
fi
done
echo -1 #/* no known attributes */
return 1 #/* unsupported console */
)
SUSUWU_SH_COLOR_COUNT_CACHE="$(SUSUWU_SH_COLOR_COUNT)"
SUSUWU_SH_HAS_UNIX_CONSOLE() ( #/* Usage: `if SUSUWU_SH_HAS_UNIX_CONSOLE; then echo "\033[0;34mThis is blue."` */
test "$(SUSUWU_SH_COLOR_COUNT)" -ge "8" #/* test that color count is Greater or Equal to 8 */
return $? #/* return `test`'s return value */
)
SUSUWU_SH_HAS_256COLOR_CONSOLE() ( #/* Usage: `if SUSUWU_SH_HAS_256COLOR_CONSOLE; then echo "\033[38;5;4mThis is blue."` */
test "$(SUSUWU_SH_COLOR_COUNT)" -ge "256" #/* test that color count is Greater or Equal to 256 */
return $? #/* return `test`'s return value */
)
export SUSUWU_SH_USE_PUSH_DEFAULT="${SUSUWU_SH_DEFAULT}"
export SUSUWU_SH_USE_PUSH_RESET_WHITE="${SUSUWU_SH_RESET_WHITE}"
SUSUWU_SH_USE_PUSH_DEBUG() ( #/* Usage: `SUSUWU_SH_USE_PUSH_DEBUG "${FUNCNAME}" "<arg>" "<step>"` */
${SUSUWU_VERBOSE} && printf "[Debug: ${SUSUWU_SH_LIGHT_CYAN}${1}${SUSUWU_SH_WHITE}(\"${SUSUWU_SH_GREEN}%s${SUSUWU_SH_WHITE}\"): ${3} ${SUSUWU_SH_LIGHT_CYAN}SUSUWU_SH_DEFAULT${SUSUWU_SH_WHITE}=\"${SUSUWU_SH_GREEN}%s${SUSUWU_SH_WHITE}\", ${SUSUWU_SH_LIGHT_CYAN}SUSUWU_SH_RESET_WHITE${SUSUWU_SH_WHITE}=\"${SUSUWU_SH_GREEN}%s${SUSUWU_SH_WHITE}\", ${SUSUWU_SH_LIGHT_CYAN}SUSUWU_SH_USE_PUSH_DEFAULT${SUSUWU_SH_WHITE}=\"${SUSUWU_SH_GREEN}%s${SUSUWU_SH_WHITE}\", ${SUSUWU_SH_LIGHT_CYAN}SUSUWU_SH_USE_PUSH_RESET_WHITE${SUSUWU_SH_WHITE}=\"${SUSUWU_SH_GREEN}%s${SUSUWU_SH_WHITE}\"]\n" "${2}" "${SUSUWU_SH_DEFAULT}" "${SUSUWU_SH_RESET_WHITE}" "${SUSUWU_SH_USE_PUSH_DEFAULT}" "${SUSUWU_SH_USE_PUSH_RESET_WHITE}" >&2
)
SUSUWU_SH_USE_PUSH() { #/* Usage: `SUSUWU_SH_USE_PUSH "${SUSUWU_SH_<attribute>}" && SUSUWU_SH_USE "${SUSUWU_SH_<attribute>}" "<message>" ["${SUSUWU_SH_RESET_WHITE}"] && SUSUWU_SH_USE_POP`. Purpose: nested `SUSUWU_SH_USE`. */
if SUSUWU_STATIC_IS_PREVIEW; then
SUSUWU_SH_USE_PUSH_DEBUG "SUSUWU_SH_USE_PUSH" "${1}" "Pushing:"
SUSUWU_SH_DEFAULT="${1}"
SUSUWU_SH_RESET_WHITE="${1}"
SUSUWU_SH_USE_PUSH_DEFAULT="${SUSUWU_SH_USE_PUSH_DEFAULT}^${1}"
SUSUWU_SH_USE_PUSH_RESET_WHITE="${SUSUWU_SH_USE_PUSH_RESET_WHITE}^${1}"
SUSUWU_SH_USE_PUSH_DEBUG "SUSUWU_SH_USE_PUSH" "${1}" "Pushed:"
fi
}
SUSUWU_SH_USE_POP() { #/* Usage: `SUSUWU_SH_USE_PUSH "${SUSUWU_SH_<attribute>}" && SUSUWU_SH_USE "${SUSUWU_SH_<attribute>}" "<message>" ["${SUSUWU_SH_RESET_WHITE}"] && SUSUWU_SH_USE_POP`. Purpose: nested `SUSUWU_SH_USE`. */
if SUSUWU_STATIC_IS_PREVIEW; then
SUSUWU_SH_USE_PUSH_DEBUG "SUSUWU_SH_USE_POP" "" "Popping:"
# SUSUWU_SH_USE_PUSH_DEFAULT="$(SUSUWU_STR_TOKEN_FIRST "${SUSUWU_SH_USE_PUSH_DEFAULT}" '^')"
# SUSUWU_SH_USE_PUSH_RESET_WHITE="$(SUSUWU_STR_TOKEN_FIRST "${SUSUWU_SH_USE_PUSH_RESET_WHITE}" '^')"
# SUSUWU_SH_DEFAULT="$(SUSUWU_STR_TOKEN_LAST "${SUSUWU_SH_USE_PUSH_DEFAULT}" '^')" #'\033')"
# SUSUWU_SH_RESET_WHITE="$(SUSUWU_STR_TOKEN_LAST "${SUSUWU_SH_USE_PUSH_RESET_WHITE}" '^')" #TODO: figure out why this doesn't restore last color code
SUSUWU_SH_USE_PUSH_DEFAULT="${SUSUWU_SH_USE_PUSH_DEFAULT%^*}"
SUSUWU_SH_USE_PUSH_RESET_WHITE="${SUSUWU_SH_USE_PUSH_RESET_WHITE%^*}"
SUSUWU_SH_DEFAULT="${SUSUWU_SH_USE_PUSH_DEFAULT##*^}"
SUSUWU_SH_RESET_WHITE="${SUSUWU_SH_USE_PUSH_RESET_WHITE##*^}"
SUSUWU_SH_USE_PUSH_DEBUG "SUSUWU_SH_USE_POP" "" "Popped:"
fi
} #/* TODO: figure out if why [it appears that] this is always executed sequential (does not nest blocks, which is required to push & pop) */
SUSUWU_SH_USE() { #/* Usage: `SUSUWU_SH_USE "${SUSUWU_SH_<attribute>}" "<message>" ["${SUSUWU_SH_DEFAULT}"] ["&1"]`. Uses <attribute> on <message>. */
# if [ ! $(SUSUWU_SH_HAS_UNIX_CONSOLE) ]; then
# case "${1}" in
# "${SUSUWU_SH_}") #/* TODO; non-standard code routes (such as `ConsoleApi2.h:SetConsoleTextAttribute`). */
# ;;
# esac
# fi
if SUSUWU_SH_HAS_UNIX_CONSOLE; then
printf '%b' "${1}"
SUSUWU_SH_USE_PUSH "${1}"
printf '%b' "${2}"
SUSUWU_SH_USE_POP
echo "${3:-${SUSUWU_SH_RESET_WHITE}}" #/* echo "${3:-${SUSUWU_SH_DEFAULT}}" */
else
echo "${2}" #TODO: `>${4:-&1}` #/* `&1` is `std::cout`/`stdout` */
fi
}
SUSUWU_SH_USE2() { #/* Usage: `SUSUWU_SH_USE2 "${SUSUWU_SH_<attribute>}" "<message>" ["${SUSUWU_SH_RESET_WHITE}"] ["&2"]`. Is `SUSUWU_SH_USE` for `&2`. */
SUSUWU_SH_USE "${1}" "${2}" "${3:-${SUSUWU_SH_RESET_WHITE}}" "${4:-&2}" #/* `&2` is `std::cerr`/`stderr` */
}
#/* `SUSUWU_SH_<warn-level>`. Notice: update [cxx/Macros.hxx](cxx/Macros.hxx) if you update those.
# * Usage: `SUSUWU_PRINT "${SUSUWU_SH_<warn-level>}" "<message>"`. */
SUSUWU_SH_ERROR() ( SUSUWU_SH_USE2 "${SUSUWU_SH_RED}" "Error: "; )
SUSUWU_SH_WARNING() ( SUSUWU_SH_USE2 "${SUSUWU_SH_PURPLE}" "Warning: "; )
SUSUWU_SH_INFO() ( SUSUWU_SH_USE2 "${SUSUWU_SH_CYAN}" "Info: "; )
SUSUWU_SH_SUCCESS() ( SUSUWU_SH_USE2 "${SUSUWU_SH_GREEN}" "Success: "; )
SUSUWU_SH_NOTICE() ( SUSUWU_SH_USE2 "${SUSUWU_SH_BLUE}" "Notice: "; )
SUSUWU_SH_DEBUG() ( SUSUWU_SH_USE2 "${SUSUWU_SH_BLUE}" "Debug: "; )
SUSUWU_SH_QUOTE() { #/* Usage: `SUSUWU_SH_QUOTE "<type-of-quote [...]>" "<code | quote>" ["<optional original color>"])"`. */
# if [ "${1% }" != "${1}" ]; then #if [ ${1} != "${1}" ]; then #/* analogous to `if [ "${1#}" -gt 1 ]; then` */
# echo "SUSUWU_SH_QUOTE: multiple modes"
# fi #/* TODO: figure out why noneof those conditions work */
SUSUWU_SH_QUOTE_Q="${2}"
for SUSUWU_SH_QUOTE_W in ${1}; do
if [ "${SUSUWU_SH_QUOTE_Q}" != "${2}" ]; then
SUSUWU_PRINT "SUSUWU_SH_QUOTE()" "$(SUSUWU_SH_DEBUG)" "Multiple modes: \${1}='$(SUSUWU_SH_QUOTE "CURRENT" "${1}")'"
fi
SUSUWU_SH_QUOTE_Q="$(SUSUWU_SH_QUOTE_SUB "${SUSUWU_SH_QUOTE_W}" "${SUSUWU_SH_QUOTE_Q}" "${3}")"
done
echo "${SUSUWU_SH_QUOTE_Q}"
}
SUSUWU_SH_QUOTE_SUB() { #/* Usage: `SUSUWU_SH_QUOTE_SUB "<type-of-quote>" "<code | quote>" ["<optional original color>"])"` */
case "${1}" in #/* Notice: update [`README.md#cc-source`](README.md#cc-source) if you update those. */
"CODE") #/* command / code quote */
echo "\`$(SUSUWU_SH_USE2 "${SUSUWU_SH_BROWN}" "${2}" "${3}")\`" ;;
"PATH") #/* path to file or directory */
echo "\"$(SUSUWU_SH_USE2 "${SUSUWU_SH_PURPLE}" "${2}" "${3}")\"" ;;
"FUNCTION") #/* name of function */
SUSUWU_SH_USE2 "${SUSUWU_SH_LIGHT_CYAN}" "${2}" "${3}" ;;
"STDERR") #/* error message quote */
SUSUWU_SH_USE2 "${SUSUWU_SH_RED}" "${2}" "${3}" ;;
"STATUS") #/* status code / return value */
SUSUWU_SH_USE2 "${SUSUWU_SH_PURPLE}" "${2}" "${3}" ;;
"VAR") #/* name of variable / name of constant */
SUSUWU_SH_USE2 "${SUSUWU_SH_LIGHT_CYAN}" "${2}" "${3}" ;;
"CURRENT") #/* current value */
SUSUWU_SH_USE2 "${SUSUWU_SH_GREEN}" "${2}" "${3}" ;;
"PROPOSED") #/* speculative / proposed value */
SUSUWU_SH_USE2 "${SUSUWU_SH_LIGHT_PURPLE}" "${2}" "${3}" ;;
*) #/* default */
SUSUWU_SH_USE2 "" "${2}" "${3}"
SUSUWU_PRINT "SUSUWU_SH_QUOTE_SUB()" "$(SUSUWU_SH_NOTICE)" "Unknown $(SUSUWU_SH_QUOTE "VAR" "<type-of-quote>") $(SUSUWU_SH_QUOTE "CODE" "${1}")." ;;
esac
return $?
}
SUSUWU_S=false
SUSUWU_VERBOSE=false
export SUSUWU_ECHO_COMMANDS_TO="/dev/null" #/* `sh/make.sh:SUSUWU_SH_COMPILER_COMMAND`'s path for `echo`. */
SUSUWU_ECHO_COMMANDS() { #/* Usage: `SUSUWU_ECHO_COMMANDS [true | false]`. ] */
( ${1}) && (${SUSUWU_VERBOSE}) && set -x
(! ${1}) && (${SUSUWU_VERBOSE}) && set +x
( ${1}) && (! ${SUSUWU_S}) && SUSUWU_ECHO_COMMANDS_TO="&2" #/* TODO: fix variable descriptors */
(! ${1}) && (! ${SUSUWU_VERBOSE}) && SUSUWU_ECHO_COMMANDS_TO="/dev/null"
}
SUSUWU_PROCESS_S() { #/* Usage: `SUSUWU_PROCESS_S $@`. [This processes params passed to `${0}`.] */
if SUSUWU_SH_HAS_PARAM "-s --silent --quiet --debug=n" "$@"; then
SUSUWU_S=true
fi
# SUSUWU_PRINT "SUSUWU_PROCESS_VERBOSE()" "$(SUSUWU_SH_INFO)" "Was passed \`$(SUSUWU_SH_QUOTE "CURRENT" "--silent")\` (or an alias), so $(SUSUWU_SH_QUOTE "CODE" "\$(SUSUWU_SH_NOTICE)") is disabled." #/* TODO? Is it against the principles of `--silent` to print about it? */
}
SUSUWU_PROCESS_VERBOSE() { #/* Usage: `SUSUWU_PROCESS_VERBOSE $@`. [This processes params passed to `${0}`.] */
if SUSUWU_SH_HAS_PARAM "-v --verbose -d --debug=a" "$@"; then
SUSUWU_VERBOSE=true
SUSUWU_ECHO_COMMANDS true
fi
SUSUWU_PRINT "SUSUWU_PROCESS_VERBOSE()" "$(SUSUWU_SH_DEBUG)" "Was passed \`$(SUSUWU_SH_QUOTE "CURRENT" "--verbose")\` (or an alias), so $(SUSUWU_SH_QUOTE "CODE" "\$(SUSUWU_SH_DEBUG)") is enabled."
}
SUSUWU_SH_HAS_FUNCNAME() ( #/* Usage: `if SUSUWU_SH_HAS_FUNCNAME 2>/dev/null; then echo "${FUNCNAME[0]}(): used FUNCNAME."` */
# [ "$(uname)" = "Darwin" ] && return 0 #/* redundant (due to `${FUNCNAME[0]}` test). */
# test "$(type -t FUNCNAME)" = "array" #/* always returns "1". */
#shellcheck disable=SC2039 #/* this is a feature test, so disable "In POSIX sh, array references are undefined." */
test "${FUNCNAME[0]}" = "SUSUWU_SH_HAS_FUNCNAME" 2>/dev/null #if no arrays, prints "Bad substitution".
) #/* ends with implicit `return $?` */
if [ -z "${SUSUWU_SH_HAS_FUNCNAME_RESULT}" ]; then
SUSUWU_SH_HAS_FUNCNAME 2>/dev/null #/* `/bin/sh` ignores the `2>/dev/null` in `SUSUWU_SH_HAS_FUNCNAME()`. */
export SUSUWU_SH_HAS_FUNCNAME_RESULT=$? #/* Usage: `if [ ${SUSUWU_SH_HAS_FUNCNAME_RESULT} -eq 0 ]; then echo "${FUNCNAME[0]}: used FUNCNAME."` */
fi
#for var in SUSUWU_SH_FILE SUSUWU_SH_LINE SUSUWU_SH_FUNC; do
# [ -z "${!var}" ] && export "$var=true" #/* prints "Bad substitution". */
#done
export SUSUWU_SH_FILE="${SUSUWU_SH_FILE:-""}"
export SUSUWU_SH_LINE="${SUSUWU_SH_LINE:-""}"
export SUSUWU_SH_FUNC="${SUSUWU_SH_FUNC:-"true"}"
export SUSUWU_SH_FILE_OR_LINE="${SUSUWU_SH_FILE:-${SUSUWU_SH_LINE}}"
SUSUWU_PRINT() ( #/* Usage: `SUSUWU_PRINT ["<optional caller-name>"] "$(SUSUWU_SH_<warn-level>)" "<message>" */
if [ "$#" -eq 3 ]; then #/* If passed `<caller-name>` */
CALLER_FUNC="${1}"; shift
fi
LEVEL="${1}"
MESSAGE="${2}"
case "${LEVEL}" in
"$(SUSUWU_SH_NOTICE)")
${SUSUWU_S} && return 1
;;
"$(SUSUWU_SH_DEBUG)")
(! ${SUSUWU_VERBOSE}) && return 1
;;
esac
NEW_MESSAGE="[${SUSUWU_SH_FILE:+"$0:"}${SUSUWU_SH_LINE:+"${LINENO}:"}${SUSUWU_SH_FILE_OR_LINE:+" "}${LEVEL}"
if [ "true" = "${SUSUWU_SH_FUNC}" ]; then
if [ -n "${CALLER_FUNC}" ]; then
NEW_MESSAGE="${NEW_MESSAGE}$(SUSUWU_SH_QUOTE "FUNCTION" "${CALLER_FUNC}: ")"
elif [ "${SUSUWU_SH_HAS_FUNCNAME_RESULT}" -eq 0 ] && [ "${#FUNCNAME}" -ge 2 ]; then
#shellcheck disable=SC2039 #if `SUSUWU_SH_HAS_FUNCNAME`, console supports this
NEW_MESSAGE="${NEW_MESSAGE}$(SUSUWU_SH_QUOTE "FUNCTION" "${FUNCNAME[1]}(): ")"
elif [ -n "${KSH_VERSION}" ]; then
NEW_MESSAGE="${NEW_MESSAGE}$(SUSUWU_SH_QUOTE "FUNCTION" "${.sh.fun}: ")"
fi
fi
NEW_MESSAGE="${NEW_MESSAGE}${MESSAGE}${SUSUWU_SH_CLOSE_}]"
printf '%b\n' "${NEW_MESSAGE}" >&2 #/* fd=2 is `std::cerr`/`stderr` */
) #/* ends with implicit `return $?` */
SUSUWU_PRINT "SUSUWU_PRINT()" "$(SUSUWU_SH_DEBUG)" "Test: $(SUSUWU_SH_QUOTE "CODE" "$(SUSUWU_SH_QUOTE "FUNCTION" "SUSUWU_SH_USE_PUSH") ... $(SUSUWU_SH_QUOTE "FUNCTION" "SUSUWU_SH_USE_POP")"). TODO: test should have ellipses ($(SUSUWU_SH_QUOTE "CODE" "...")) brown."
if ! SUSUWU_SH_HAS_UNIX_CONSOLE && [ ! "${SUSUWU_SH_CONSOLE_ERROR_SHOWN}" ]; then
export SUSUWU_SH_CONSOLE_ERROR_SHOWN=true
SUSUWU_PRINT "SUSUWU_SH_HAS_UNIX_CONSOLE()" "$(SUSUWU_SH_WARNING)" "failed. TODO: support systems without UNIX console codes. If your console ($(SUSUWU_SH_QUOTE "CODE" "[ \"\${TERM}\" = \"${TERM}\" ]")) shows colors such as ${SUSUWU_SH_BLUE}blue${SUSUWU_SH_DEFAULT} (not glitches or literal codes such as \"\\\033[0;34m\"), you can [post an issue](https://github.com/SwuduSusuwu/SusuLib/issues/new) about this, or use $(SUSUWU_SH_QUOTE "CODE" "export TERM=\"linux\"") to enable console code use."
fi
export SUSUWU_ABORT_ON_FIRST_ERROR=false
SUSUWU_PROCESS_ABORT_ON_FIRST_ERROR() { #/* Usage: `SUSUWU_PROCESS_ABORT_ON_FIRST_ERROR $@`. */
if SUSUWU_SH_HAS_PARAM "--abort-on-first-error -S --no-keep-going --stop" "$@"; then
SUSUWU_ABORT_ON_FIRST_ERROR=true
fi
}
SUSUWU_LOCAL_WORKSPACE_PATH() ( #/* Usage: `"$(SUSUWU_LOCAL_WORKSPACE_PATH)/compile_commands.json"` [Substitute for `${GIT_WORK_TREE}` or `${GIT_DIR}`]` */
git rev-parse --absolute-git-dir >/dev/null 2>&1 || return 1
dirname "$(git rev-parse --absolute-git-dir 2>/dev/null)"
)
SUSUWU_DEFAULT_BRANCH() ( #/* Usage: `echo "$(SUSUWU_DEFAULT_BRANCH ["<fallback>"])"` */
DEFAULT_BRANCH="$(git symbolic-ref -q --short "refs/remotes/$(git remote)/HEAD" | sed -n "s/$(git remote)\/\(.*\)/\1/p")" #/* remote branch */
if [ -z "${DEFAULT_BRANCH}" ]; then #/* if `git remote` not found */
DEFAULT_BRANCH="$(git branch --sort=-refname | grep -o -m1 '\b\(main\|master\|trunk\)\b')" #local branch; if you update this, update `README.md#git`.
fi
echo "${DEFAULT_BRANCH:-${1}}" #/* https://github.com/SwuduSusuwu/SusuLib/actions/runs/<number>/job/<number> has bare repos, which use <fallback>. */
)
SUSUWU_PRODUCTION_USE() ( #/* Usage: `SUSUWU_PRODUCTION_USE ["<default branch>"]` */
if command -v git >/dev/null && git rev-parse --is-inside-work-tree >/dev/null 2>&1; then #/* analogous to `test -d ".git/"; then` */
THIS_BRANCH="$(git rev-parse --abbrev-ref HEAD)" #/* detect current branch */
if [ -n "${1}" ]; then
DEFAULT_BRANCH="${1}" #/* use default branch from build script */
else
DEFAULT_BRANCH="$(SUSUWU_DEFAULT_BRANCH "${1}")" #/* detect default branch */
fi
if [ "${DEFAULT_BRANCH}" = "${THIS_BRANCH}" ]; then
SUSUWU_PRINT "SUSUWU_PRODUCTION_USE()" "$(SUSUWU_SH_NOTICE)" "$(SUSUWU_SH_QUOTE "CODE" "git branch") is \"$(SUSUWU_SH_QUOTE "CURRENT" "${THIS_BRANCH}")\"."
else
SUSUWU_PRINT "SUSUWU_PRODUCTION_USE()" "$(SUSUWU_SH_WARNING)" "$(SUSUWU_SH_QUOTE "CODE" "git branch") is \"$(SUSUWU_SH_QUOTE "CURRENT" "${THIS_BRANCH}")\"; for production use, use $(SUSUWU_SH_QUOTE "CODE" "git switch $(SUSUWU_SH_QUOTE "PROPOSED" "${DEFAULT_BRANCH}")")."
fi
fi
)
SUSUWU_TEST_BASH() ( #/* Usage: `s/exit ${STATUS}/${STATUS} && SUSUWU_TEST_BASH && STATUS=$?; exit ${STATUS}/` */
if command -v "bash" >/dev/null && [ -z "${BASH_VERSION}" ]; then #/* If system has `bash`, && this is not an infinite loop (this is not already `bash`);
BASH_PATH="${0}.bash" # * , path for `bash` version of this. */
SUSUWU_PATH_SHOULD_NOT_EXIST "SUSUWU_TEST_BASH()" "${BASH_PATH}" #/* In case user wants to disable this (or this crashed on last execution). */
SUSUWU_PRINT "SUSUWU_TEST_BASH()" "$(SUSUWU_SH_NOTICE)" "Will produce $(SUSUWU_SH_QUOTE "CODE" "${BASH_PATH}") to test $(SUSUWU_SH_QUOTE "CODE" "/bin/bash"). Use $(SUSUWU_SH_QUOTE "CODE" "touch '${BASH_PATH}'") to disable this."
cp "${0}" "${BASH_PATH}" || exit 1
if sed 's|/bin/sh|/bin/bash|' -i'' "${BASH_PATH}"; then #/* If produced `/bin/bash` version; */
#shellcheck disable=SC2016 #/* That's not supposed to expand */
sed 's|^\s*SUSUWU_BUILD_OBJECTS[^#]*|$(SUSUWU_S=true; \0) |' -i'' "${BASH_PATH}" #/* Silence subsequent `SUSUWU_BUILD_OBJECTS()`. */
#shellcheck disable=SC2016 #/* That's not supposed to expand */
sed 's|^\s*SUSUWU_TEST_OUTPUT[^#]*|$(SUSUWU_S=true; \0) |' -i'' "${BASH_PATH}" #/* Silence subsequent `SUSUWU_TEST_OUTPUT()`. */
("${BASH_PATH}") #/* Execute `/bin/bash` version. */
fi
BASH_STATUS=$?
rm "${BASH_PATH}"
if [ 0 -eq ${BASH_STATUS} ]; then
SUSUWU_PRINT "SUSUWU_TEST_BASH()" "$(SUSUWU_SH_SUCCESS)" "$(SUSUWU_SH_QUOTE "CODE" "${BASH_PATH}") returned status code $(SUSUWU_SH_QUOTE "STATUS" "${BASH_STATUS}")."
else
SUSUWU_PRINT "SUSUWU_TEST_BASH()" "$(SUSUWU_SH_ERROR)" "$(SUSUWU_SH_QUOTE "CODE" "${BASH_PATH}") returned status code $(SUSUWU_SH_QUOTE "STATUS" "${BASH_STATUS}")."
fi
return ${BASH_STATUS}
fi
)
less sh/make.sh
(https://github.com/SwuduSusuwu/SusuLib/blob/preview/sh/make.sh)
#!/bin/sh
#
#/* (C) 2024 Swudu Susuwu, dual licenses: choose [_GPLv2_](./LICENSE_GPLv2) or [_Apache 2_](./LICENSE) (allows all uses).
# * TODO: [map options/flags (which `SUSUWU_PROCESS_*` functions use) to descriptions (for `--help` output.)](https://github.com/SwuduSusuwu/SusuLib/issues/24) */
GIT_ROOT="$(dirname "$(git rev-parse --git-dir)")/" #/* `git` does not set `${GIT_DIR}`, nor `${GIT_WORK_TREE}` */
if [ ! "$(realpath -q ./)" = "$(realpath -q "${GIT_ROOT}")" ]; then
GIT_ROOT_USE="${GIT_ROOT}" #/* If current path is not root, add root path */
fi
SUSUWU_INCLUDE_ERROR() { #/* Usage; `SUSUWU_INCLUDE_ERROR "<relative path>" "error message"` */
echo "[$0: Error: \`GIT_WORK_TREE=${GIT_ROOT}\` \`\${GIT_WORK_TREE}${1}\` ${2}.]"; exit 1
}
#shellcheck source=./sh/Macros.sh
SUSUWU_INCLUDE() { #/* Usage; `SUSUWU_INCLUDE "<relative path>"` */
SUSUWU_INCLUDE_PATH=${1}; shift #/* So path is not included in source'd scripts `$@` and `$*`. */
if [ ! -e "${GIT_ROOT}${SUSUWU_INCLUDE_PATH}" ]; then
SUSUWU_INCLUDE_ERROR "${SUSUWU_INCLUDE_PATH}" "not found"
elif [ ! -x "${GIT_ROOT}${SUSUWU_INCLUDE_PATH}" ] || ! . "${GIT_ROOT}${SUSUWU_INCLUDE_PATH}"; then
SUSUWU_INCLUDE_ERROR "${SUSUWU_INCLUDE_PATH}" "not executable"
fi
}
SUSUWU_INCLUDE "./sh/Macros.sh" #/* SUSUWU_ABORT_ON_FIRST_ERROR SUSUWU_ECHO_COMMANDS() SUSUWU_ECHO_COMMANDS_TO SUSUWU_ESCAPE_SPACES() SUSUWU_LOCAL_WORKSPACE_PATH() SUSUWU_PATH_SHOULD_NOT_EXIST() SUSUWU_PATH_SUFFIX_SLASH() SUSUWU_PATH_UNAMBIGUOUS() SUSUWU_PRINT() SUSUWU_S SUSUWU_SH_CONSOLE_PARAMS SUSUWU_SH_HAS_PARAM() SUSUWU_SH_REMOVE_PARAM() SUSUWU_SH_<color> SUSUWU_SH_<type-of-code>() SUSUWU_SH_<warn-level>() SUSUWU_ESCAPE_QUOTED() SUSUWU_VERBOSE */
SUSUWU_COMPILE_JSON_PATH_="$(SUSUWU_LOCAL_WORKSPACE_PATH)/compile_commands.json" # [`clang-tidy` compilation database](https://clang.llvm.org/docs/JSONCompilationDatabase.html#build-system-integration)
SUSUWU_SET_NEW_BUILD() { #/* Usage: `SUSUWU_SET_NEW_BUILD [true | false]`. ] */
export SUSUWU_NEW_BUILD SUSUWU_RELINK SUSUWU_COMPILE_JSON_PATH
if [ true = "${1}" ]; then #/* If is first build, or passed `--rebuild`, or global headers `touch`d;
SUSUWU_NEW_BUILD=true # * build "${OBJDIR}${OUTPUT}",
SUSUWU_RELINK=true # * build "${BINDIR}${OUTPUT}",
SUSUWU_COMPILE_JSON_PATH="${SUSUWU_COMPILE_JSON_PATH_}" # *and produce compilation database. */
else #/* If is built;
SUSUWU_NEW_BUILD=false # * don't clobber,
SUSUWU_RELINK=false # * don't clobber,
SUSUWU_COMPILE_JSON_PATH="/dev/null" # * don't clobber. */
fi
}
SUSUWU_SET_NEW_BUILD false
SUSUWU_HAS_PARTIAL_JSON() ( #/* Usage: `if $(SUSUWU_HAS_PARTIAL_JSON "compile_commands.json"); then RESUME_JSON_BUILD... ` */
[ "[" = "$(head -c1 "${1}")" ] && [ "}," = "$(tail -c3 "${1}")" ] # [ "]" != "$(tail -c2 "${1}")" ]
return $?
)
SUSUWU_COMPILE_COMMAND() { #/* Usage: `SUSUWU_COMPILE_COMMAND "<directory>" "<file>" ["<command>" | <arguments>...]"`. Allows to echo commands ( + produce `./${SUSUWU_COMPILE_JSON_PATH}`). */
if [ "/dev/null" != "${SUSUWU_ECHO_COMMANDS_TO}" ]; then
SUSUWU_PRINT "SUSUWU_COMPILE_COMMAND()" "$(SUSUWU_SH_NOTICE)" "${3}" #"${SUSUWU_ECHO_COMMANDS_TO}" #/* TODO: redirection */
fi
if SUSUWU_HAS_PARTIAL_JSON "${SUSUWU_COMPILE_JSON_PATH_}" && [ "/dev/null" = "${SUSUWU_COMPILE_JSON_PATH}" ]; then
SUSUWU_PRINT "SUSUWU_HAS_PARTIAL_JSON()" "$(SUSUWU_SH_INFO)" "found partial $(SUSUWU_SH_QUOTE "PATH" "${SUSUWU_COMPILE_JSON_PATH_}"), will resume."
export SUSUWU_COMPILE_JSON_PATH="${SUSUWU_COMPILE_JSON_PATH_}" #/* Notice: assumes that `SUSUWU_PROCESS_INCLUDES()` is not called once `SUSUWU_COMPILE_COMMAND()` is. */
fi
{
echo " {"
echo " \"directory\": \"${1}\","
echo " \"command\": \"$(SUSUWU_ESCAPE_QUOTED "${3}")\","
echo " \"file\": \"${2}\""
echo " },"
} >> "${SUSUWU_COMPILE_JSON_PATH}"
${3}
return $?
}
SUSUWU_COMPILE_JSON_PATH_FOUND() ( #/* Usage: `if SUSUWU_COMPILE_JSON_PATH_ERROR "${FUNCNAME}"; then return 1; fi` */
if [ ! -e "${SUSUWU_COMPILE_JSON_PATH_}" ]; then
SUSUWU_PRINT "${1}" "$(SUSUWU_SH_WARNING)" "$(SUSUWU_SH_QUOTE "PATH" "${SUSUWU_COMPILE_JSON_PATH_}") not found; consider $(SUSUWU_SH_QUOTE "CODE" "git restore \"${SUSUWU_COMPILE_JSON_PATH_}\"")"
return 1
fi
)
SUSUWU_LOCAL_WORKSPACE_JSON() ( #/* Usage: `git pull && SUSUWU_LOCAL_WORKSPACE_PATH_JSON && clang-tidy ${CXX_SOURCE_PATH}`. Is 'LLVM ERROR: Cannot chdir into "/home/runner/work/SusuLib/SusuLib"!' fix (replaces `${{ github.workspace }}` with `$(pwd)`) */
if ! SUSUWU_COMPILE_JSON_PATH_FOUND "SUSUWU_LOCAL_WORKSPACE_JSON()"; then
return $?
fi
[ -n "$(SUSUWU_LOCAL_WORKSPACE_PATH)" ] && sed "s|\"directory\": \"[^\"]\+\"|\"directory\": \"$(SUSUWU_LOCAL_WORKSPACE_PATH)\"|g" -i"" "${SUSUWU_COMPILE_JSON_PATH_}"
)
SUSUWU_LOCAL_WORKSPACE_JSON #/* TODO: suitable spot to put this, perhaps into `SUSUWU_SETUP_CXX()`. */
SUSUWU_GITHUB_WORKSPACE_JSON() ( #/* Usage: `echo "SUSUWU_GITHUB_WORKSPACE_JSON" >> .git/hooks/pre-commit` [Replaces `$(pwd)` with `${{ github.workspace }}` */
if ! SUSUWU_COMPILE_JSON_PATH_FOUND "SUSUWU_GITHUB_WORKSPACE_JSON()"; then
return $?
fi
JSON_BACKUP_SUFFIX=".bak" #/* TODO; remove, since this is restored with `SUSUWU_LOCAL_WORKSPACE_PATH`? */
JSON_BACKUP_PATH="${SUSUWU_COMPILE_JSON_PATH_}${JSON_BACKUP_SUFFIX}"
if SUSUWU_PATH_SHOULD_NOT_EXIST "SUSUWU_GITHUB_WORKSPACE_JSON()" "${JSON_BACKUP_PATH}"; then
sed 's|"directory": "\([^"]\+\)"|"directory": "/home/runner/work/SusuLib/SusuLib"|g' -i"${JSON_BACKUP_SUFFIX}" "${SUSUWU_COMPILE_JSON_PATH_}" && {
git add -f "${SUSUWU_COMPILE_JSON_PATH_}"
rm "${JSON_BACKUP_PATH}" #/* `mv "${JSON_BACKUP_PATH}" "${SUSUWU_COMPILE_JSON_PATH_}"` causes "error: cannot rebase: You have unstaged changes." */
}
fi
)
SUSUWU_PROCESS_MINGW() { #/* Usage: `SUSUWU_PROCESS_MINGW $@` [This processes params passed to `${0}`.] */
export CROSS_COMP=""
if SUSUWU_SH_HAS_PARAM "--mingw" "$@"; then
CROSS_COMP=" --mingw"
fi
}
SUSUWU_SETUP_CXX() { #/* Usage: ... [SUSUWU_PROCESS_MINGW $@] SUSUWU_SETUP_CXX [SUSUWU_PROCESS_RELEASE_DEBUG $@] SUSUWU_SETUP_BUILD_FLAGS SUSUWU_SETUP_BINDIR "" SUSUWU_SETUP_OBJDIR "" SUSUWU_SETUP_OUTPUT "" [SUSUWU_PROCESS_CLEAN_REBUILD $@] [SUSUWU_PROCESS_INCLUDES ""] SUSUWU_BUILD_OBJECTS ... */
export CXX CXXFLAGS CC CFLAGS LD LDFLAGS USE_FSAN
if [ " --mingw" = "${CROSS_COMP}" ]; then
if command -v x86_64-w64-mingw32-clang++ >/dev/null; then
CXX="x86_64-w64-mingw32-clang++"
# CXXFLAGS="${CXXFLAGS} -rtlib=compiler-rt"
LDFLAGS="${LDFLAGS} -rtlib=compiler-rt -lunwind -static" #-no-lgcc -nolibgcc -lmingw32 -static-libstdc++" #workaround for [`lld: error: unable to find library -lgcc`](https://github.com/termux/termux-packages/issues/24194#issuecomment-2799574245)
USE_FSAN=true #/* `-fsan*` [supports `x86_64-w64-mingw32-clang++`](https://github.com/SwuduSusuwu/SusuLib/issues/16) */
elif command -v x86_64-w64-mingw32-g++ >/dev/null; then
FLAGS_USER="${FLAGS_USER} -fopenmp"
CXX="x86_64-w64-mingw32-g++"
LDFLAGS="${LDFLAGS} -static-libgcc -static-libstdc++"
USE_FSAN=false #/* `TODO: `-fsan*` for `x86_64-w64-mingw32-g++`](https://www.mingw-w64.org/contribute/#thorough-status-report-for-sanitizers-asan-tsan-usan) */
else
SUSUWU_PRINT "SUSUWU_SETUP_CXX()" "$(SUSUWU_SH_ERROR)" "$(SUSUWU_SH_QUOTE "CODE" "x86_64-w64-mingw32-clang++ not found"), $(SUSUWU_SH_QUOTE "CODE" "x86_64-w64-mingw32-g++ not found"). Do $(SUSUWU_SH_QUOTE "CODE" "apt install llvm-mingw-w64") or $(SUSUWU_SH_QUOTE "CODE" "apt install mingw-w64")."
exit 1
fi
elif command -v clang++ >/dev/null; then
CXX="clang++" #/* TODO: +` -Xclang -analyze -Xclang -analyzer-output=text` (got no extra outputs from this) */
USE_FSAN=true #/* [`-fsan*` supports `g++`/`clang++`](https://developers.redhat.com/blog/2021/05/05/memory-error-checking-in-c-and-c-comparing-sanitizers-and-valgrind#tldr) */
elif command -v g++ >/dev/null; then
FLAGS_USER="${FLAGS_USER} -fopenmp"
CXX="g++"
USE_FSAN=true
elif command -v "${CXX}" >/dev/null; then #/* TODO: if our flags are compatible with all `${CXX}`, move this to top */
SUSUWU_PRINT "SUSUWU_SETUP_CXX()" "$(SUSUWU_SH_INFO)" "$(SUSUWU_SH_QUOTE "CODE" "clang++ not found"), $(SUSUWU_SH_QUOTE "CODE" "g++ not found"). $(SUSUWU_SH_QUOTE "CODE" "CXX=${CXX}") found, will use."
if command -v "${CC}" >/dev/null; then
SUSUWU_PRINT "SUSUWU_SETUP_CXX()" "$(SUSUWU_SH_INFO)" "$(SUSUWU_SH_QUOTE "CODE" "CC=${CC}") found, will use."
else
SUSUWU_PRINT "SUSUWU_SETUP_CXX()" "$(SUSUWU_SH_INFO)" "$(SUSUWU_SH_QUOTE "CODE" "CC=${CC}") not found, will use $(SUSUWU_SH_QUOTE "CODE" "CC=\"\${CXX} -x -c\"")"
CC="${CXX}"
CFLAGS="${CFLAGS} -x c"
fi
if command -v "${LD}" >/dev/null; then
SUSUWU_PRINT "SUSUWU_SETUP_CXX()" "$(SUSUWU_SH_INFO)" "$(SUSUWU_SH_QUOTE "CODE" "LD=${LD}") found, will use."
else
SUSUWU_PRINT "SUSUWU_SETUP_CXX()" "$(SUSUWU_SH_INFO)" "$(SUSUWU_SH_QUOTE "CODE" "LD=${LD}") not found, will use $(SUSUWU_SH_QUOTE "CODE" "LD=\"\${CXX}\"")"
LD="${CXX}"
fi
USE_FSAN=false #/* `TODO: test unknown compilers for `-fsan*` support */
return 0
else
SUSUWU_PRINT "SUSUWU_SETUP_CXX()" "$(SUSUWU_SH_ERROR)" "$(SUSUWU_SH_QUOTE "CODE" "clang++ not found"), $(SUSUWU_SH_QUOTE "CODE" "g++ not found"). $(SUSUWU_SH_QUOTE "CODE" "CXX=${CXX}") not found. Do $(SUSUWU_SH_QUOTE "CODE" "apt install clang") or $(SUSUWU_SH_QUOTE "CODE" "apt install gcc")."
exit 1
fi
LD="${CXX}"
CC="${CXX}"
CFLAGS="${CFLAGS} -x c"
return 0
}
SUSUWU_PROCESS_RELEASE_DEBUG() { #/* Usage: `SUSUWU_PROCESS_RELEASE_DEBUG $@` [This processes params passed to `${0}`.] */
export CFLAGS CXXFLAGS LDFLAGS ASAN_OPTIONS UBSAN_OPTIONS
if SUSUWU_SH_HAS_PARAM "--release" "$@"; then
SUSUWU_PRINT "SUSUWU_PROCESS_RELEASE_DEBUG()" "$(SUSUWU_SH_NOTICE)" "$(SUSUWU_SH_QUOTE "CODE" "${0} $(SUSUWU_SH_REMOVE_PARAM "--release" "$@") $(SUSUWU_SH_QUOTE "CURRENT" "--release")") does not support \`gdb\` / \`lldb\` (use $(SUSUWU_SH_QUOTE "CODE" "${0} $(SUSUWU_SH_REMOVE_PARAM "--release" "$@") $(SUSUWU_SH_QUOTE "PROPOSED" "--debug")") for such tools)."
CFLAGS="${CFLAGS} ${FLAGS_RELEASE} ${CFLAGS_RELEASE}"
CXXFLAGS="${CXXFLAGS} ${FLAGS_RELEASE} ${CXXFLAGS_RELEASE}"
else
if ! SUSUWU_SH_HAS_PARAM "--debug" "$@"; then
SUSUWU_PRINT "SUSUWU_PROCESS_RELEASE_DEBUG()" "$(SUSUWU_SH_NOTICE)" "$(SUSUWU_SH_QUOTE "CODE" "${0} $*") defaults to $(SUSUWU_SH_QUOTE "CODE" "${0} $* $(SUSUWU_SH_QUOTE "CURRENT" "--debug")")."
fi
SUSUWU_PRINT "SUSUWU_PROCESS_RELEASE_DEBUG()" "$(SUSUWU_SH_NOTICE)" "$(SUSUWU_SH_QUOTE "CODE" "${0} $(SUSUWU_SH_REMOVE_PARAM "--debug" "$@") $(SUSUWU_SH_QUOTE "CURRENT" "--debug")") is slow (use $(SUSUWU_SH_QUOTE "CODE" "${0} $(SUSUWU_SH_REMOVE_PARAM "--debug" "$@") $(SUSUWU_SH_QUOTE "PROPOSED" "--release")") to improve how fast $(SUSUWU_SH_QUOTE "CODE" "$(SUSUWU_PATH_UNAMBIGUOUS "\${BINDIR}/\${OUTPUT}")") executes)."
CFLAGS="${CFLAGS} ${FLAGS_DEBUG} ${CFLAGS_DEBUG}"
CXXFLAGS="${CXXFLAGS} ${FLAGS_DEBUG} ${CXXFLAGS_DEBUG}"
if [ true = ${USE_FSAN} ]; then
CFLAGS="${CFLAGS} ${FLAGS_FSAN}"
CXXFLAGS="${CXXFLAGS} ${FLAGS_FSAN}"
LDFLAGS="${LDFLAGS} ${FLAGS_FSAN}"
ASAN_OPTIONS=abort_on_error=1:fast_unwind_on_malloc=0:detect_leaks=0 UBSAN_OPTIONS=print_stacktrace=1 #/* "For LLDB/GDB and to prevent very short stack traces and usually false leaks detection" */
fi
fi
}
SUSUWU_SETUP_BUILD_FLAGS() { #/* Usage: ... [SUSUWU_PROCESS_MINGW $@] SUSUWU_SETUP_CXX [SUSUWU_PROCESS_RELEASE_DEBUG $@] SUSUWU_SETUP_BUILD_FLAGS SUSUWU_SETUP_BINDIR "" SUSUWU_SETUP_OBJDIR "" SUSUWU_SETUP_OUTPUT "" [SUSUWU_PROCESS_CLEAN_REBUILD $@] [SUSUWU_PROCESS_INCLUDES ""] SUSUWU_BUILD_OBJECTS ... */
export CFLAGS LDFLAGS C_SOURCE_PATH CXX_SOURCE_PATH SUSUWU_OBJECTLIST
LDFLAGS="${LDFLAGS}"
CFLAGS="${CFLAGS} ${FLAGS_USER} ${FLAGS_ANALYSIS}"
CXXFLAGS="${CXXFLAGS} ${FLAGS_USER} ${FLAGS_ANALYSIS}"
C_SOURCE_PATH=$(SUSUWU_PATH_SUFFIX_SLASH "${GIT_ROOT_USE}${C_SOURCE_PATH}") #/* if inherit C_SOURCE_PATH, perhaps it lacks '/' */
CXX_SOURCE_PATH=$(SUSUWU_PATH_SUFFIX_SLASH "${GIT_ROOT_USE}${CXX_SOURCE_PATH}") #/* if inherit CXX_SOURCE_PATH, perhaps it lacks '/' */
SUSUWU_OBJECTLIST=""
}
SUSUWU_SETUP_OBJDIR() { #/* Usage: `SUSUWU_SETUP_OBJDIR "./obj/"` */
export OBJDIR
if [ -z "${OBJDIR}" ]; then
OBJDIR="${1}"
SUSUWU_SETUP_OBJDIR_OLD() ( SUSUWU_SH_QUOTE "CURRENT PATH" "${OBJDIR}" )
SUSUWU_SETUP_OBJDIR_NEW() ( SUSUWU_SH_QUOTE "PROPOSED PATH" "<new-path>" )
SUSUWU_PRINT "SUSUWU_SETUP_OBJDIR()" "$(SUSUWU_SH_NOTICE)" "To redirect $(SUSUWU_SH_QUOTE "CODE" "${CXX} -c ... -o \"\${$(SUSUWU_SH_QUOTE "VAR" "OBJDIR" "${SUSUWU_SH_BROWN}")}\${OBJ}.o\"") (which has $(SUSUWU_SH_QUOTE "CODE" "$(SUSUWU_SH_QUOTE "VAR" "OBJDIR")=$(SUSUWU_SETUP_OBJDIR_OLD)")), use $(SUSUWU_SH_QUOTE "CODE" "$(SUSUWU_SH_QUOTE "VAR" "OBJDIR")=$(SUSUWU_SETUP_OBJDIR_NEW)") (where $(SUSUWU_SETUP_OBJDIR_NEW) is a directory which you choose)." #/* TODO: remove `${SUSUWU_SH_BROWN}` once `SUSUWU_SH_USE_POP()` is fixed. */
else
SUSUWU_PRINT "SUSUWU_SETUP_OBJDIR()" "$(SUSUWU_SH_NOTICE)" "$(SUSUWU_SH_QUOTE "CODE" "${CXX} -c ... -o \"\${$(SUSUWU_SH_QUOTE "VAR" "OBJDIR")}\"") inherits local $(SUSUWU_SH_QUOTE "CODE" "$(SUSUWU_SH_QUOTE "VAR" "OBJDIR")=\"${OBJDIR}\"") until you use $(SUSUWU_SH_QUOTE "CODE" "unset OBJDIR")."
fi
OBJDIR="$(SUSUWU_PATH_SUFFIX_SLASH "${OBJDIR}")" #/* if inherit OBJDIR, perhaps it is without last '/' */
OBJDIR="${GIT_ROOT_USE}$(SUSUWU_PATH_UNAMBIGUOUS "${OBJDIR}")"
mkdir -p "${OBJDIR}"
}
SUSUWU_SETUP_BINDIR() { #/* Usage: `SUSUWU_SETUP_BINDIR "./bin/"` */
export BINDIR
if [ -z "${BINDIR}" ]; then
BINDIR="${1}"
SUSUWU_SETUP_BINDIR_OLD() ( SUSUWU_SH_QUOTE "CURRENT PATH" "${BINDIR}" )
SUSUWU_SETUP_BINDIR_NEW() ( SUSUWU_SH_QUOTE "PROPOSED PATH" "<new-path>" )
SUSUWU_PRINT "SUSUWU_SETUP_BINDIR()" "$(SUSUWU_SH_NOTICE)" "To redirect $(SUSUWU_SH_QUOTE "CODE" "${LD} ... -o \"\${$(SUSUWU_SH_QUOTE "VAR" "BINDIR" "${SUSUWU_SH_BROWN}")}\${OUTPUT}\"") (which has $(SUSUWU_SH_QUOTE "CODE" "$(SUSUWU_SH_QUOTE "VAR" "BINDIR")=$(SUSUWU_SETUP_BINDIR_OLD)")), use $(SUSUWU_SH_QUOTE "CODE" "$(SUSUWU_SH_QUOTE "VAR" "BINDIR")=$(SUSUWU_SETUP_BINDIR_NEW)") (where $(SUSUWU_SETUP_BINDIR_NEW) is a directory which you choose)." #/* TODO: remove `${SUSUWU_SH_BROWN}` once `SUSUWU_SH_USE_POP()` is fixed. */
else
SUSUWU_PRINT "SUSUWU_SETUP_BINDIR()" "$(SUSUWU_SH_NOTICE)" "$(SUSUWU_SH_QUOTE "CODE" "${LD} ... -o \"\${$(SUSUWU_SH_QUOTE "VAR" "BINDIR")}\${OUTPUT}\"") inherits local $(SUSUWU_SH_QUOTE "CODE" "$(SUSUWU_SH_QUOTE "VAR" "BINDIR")=\"$(SUSUWU_SH_QUOTE "CURRENT" "${BINDIR}")\"") until you use $(SUSUWU_SH_QUOTE "CODE" "unset BINDIR")."
fi
BINDIR="$(SUSUWU_PATH_SUFFIX_SLASH "${BINDIR}")" #/* if inherit BINDIR, perhaps it is without last '/' */
BINDIR="${GIT_ROOT_USE}$(SUSUWU_PATH_UNAMBIGUOUS "${BINDIR}")"
mkdir -p "${BINDIR}"
}
SUSUWU_SETUP_OUTPUT() { #/* Usage: `SUSUWU_SETUP_OUTPUT "YourProgram"` */
export OUTPUT SUSUWU_RELINK
if [ -z "${BINDIR%/}" ] || [ -z "${OBJDIR%/}" ]; then
SUSUWU_PRINT "SUSUWU_SETUP_OUTPUT()" "$(SUSUWU_SH_ERROR)" "$(SUSUWU_SH_QUOTE "CODE" "[ -z \"\${BINDIR%/}\" ] || [ \"-z \${OBJDIR%/}\" ]"): $(SUSUWU_SH_QUOTE "CODE" "${0}") must call $(SUSUWU_SH_QUOTE "CODE" "SUSUWU_SETUP_OBJDIR()") + $(SUSUWU_SH_QUOTE "CODE" "SUSUWU_SETUP_BINDIR()") before $(SUSUWU_SH_QUOTE "CODE" "SUSUWU_SETUP_OUTPUT()")."
exit 1 #/* `if("/" == BINDIR || "" == BINDIR)`, can't use `${BINDIR}${OUTPUT}` */
fi
if [ -z "${CROSS_COMP}" ]; then
OUTPUT="${1}.out"
else
OUTPUT="${1}.exe"
fi
if [ -e "${BINDIR}${OUTPUT}" ]; then
SUSUWU_RELINK=false
else
SUSUWU_RELINK=true
fi
}
SUSUWU_CLEAN_OUTPUT_IMPL() ( #/* Usage: `SUSUWU_CLEAN_OUTPUT_IMPL "Reason to clean" "postscript" */
SUSUWU_PRINT "SUSUWU_CLEAN_OUTPUT_IMPL()" "$(SUSUWU_SH_INFO)" "${1}; will remove intermediate objects ($(SUSUWU_SH_QUOTE "CODE" "rm \"${OBJDIR}\"*.o")), plus remove generated executables ($(SUSUWU_SH_QUOTE "CODE" "rm \"${BINDIR}\"*.{exe,out}"))${2}"
rm "${OBJDIR}"*.o 2>/dev/null #/* The `-f` flag is omitted in case `OBJDIR` or `BINDIR` is set to "../*/". */
rm "${BINDIR}"*.exe 2>/dev/null
rm "${BINDIR}"*.out 2>/dev/null
)
SUSUWU_CLEAN_OUTPUT() { #/* Usage: `SUSUWU_REBUILD_OUTPUT "Reason to clean" */
SUSUWU_CLEAN_OUTPUT_IMPL "${1}" ", plus exit. [Use $(SUSUWU_SH_QUOTE "CODE" "${0} $(SUSUWU_SH_REMOVE_PARAM "--clean") $(SUSUWU_SH_QUOTE "PROPOSED" "--rebuild")") to remove plus continue.]"
exit 0
}
SUSUWU_REBUILD_OUTPUT() { #/* Usage: `SUSUWU_REBUILD_OUTPUT "Reason to rebuild" */
SUSUWU_CLEAN_OUTPUT_IMPL "${1}" ", plus continue. [Use $(SUSUWU_SH_QUOTE "CODE" "${0} $(SUSUWU_SH_REMOVE_PARAM "--rebuild") $(SUSUWU_SH_QUOTE "PROPOSED" "--clean")") to remove plus exit.]"
SUSUWU_SET_NEW_BUILD true
}
SUSUWU_PROCESS_CLEAN_REBUILD() { #/* Usage: `SUSUWU_PROCESS_CLEAN_REBUILD $@` [This processes params passed to `${0}`.] */
if SUSUWU_SH_HAS_PARAM "--clean" "$@"; then
SUSUWU_CLEAN_OUTPUT "Was called as $(SUSUWU_SH_QUOTE "CODE" "${0} $(SUSUWU_SH_REMOVE_PARAM "--clean" "$@") $(SUSUWU_SH_QUOTE "CURRENT" "--clean")")"
fi
if SUSUWU_SH_HAS_PARAM "--rebuild" "$@"; then
SUSUWU_REBUILD_OUTPUT "Was called as $(SUSUWU_SH_QUOTE "CODE" "${0} $(SUSUWU_SH_REMOVE_PARAM "--rebuild" "$@") $(SUSUWU_SH_QUOTE "CURRENT" "--rebuild")")"
fi
}
SUSUWU_PROCESS_INCLUDES() { #/* Usage: `SUSUWU_PROCESS_INCLUDES ${C_SOURCE_PATH}*.h ${CXX_SOURCE_PATH}*.hxx` */
#shellcheck disable=SC2068
for SUSUWU_PROCESS_INCLUDES_SOURCE_ in $@; do
SUSUWU_PROCESS_INCLUDES_OBJECT_="${OBJDIR}$(basename "${SUSUWU_PROCESS_INCLUDES_SOURCE_}" .hxx).o" #/* `basename`'s second param removes suffix */
SUSUWU_PROCESS_INCLUDES_SRCCXX_="$(dirname "${SUSUWU_PROCESS_INCLUDES_SOURCE_}")/$(basename "${SUSUWU_PROCESS_INCLUDES_SOURCE_}" .hxx).cxx" #/* `basename`'s second param removes suffix */
if [ -n "$(find "${SUSUWU_PROCESS_INCLUDES_SOURCE_}" -newer "${SUSUWU_PROCESS_INCLUDES_OBJECT_}" 2>/dev/null)" ] && [ -e "${SUSUWU_PROCESS_INCLUDES_SOURCE_}" ]; then #/* `-n`: not nil, `-e`: exists. */
SUSUWU_REBUILD_OUTPUT "$(SUSUWU_SH_QUOTE "PATH" "${SUSUWU_PROCESS_INCLUDES_SOURCE_}") (which is a common $(SUSUWU_SH_QUOTE "CODE" "#include")) is newer than $(SUSUWU_SH_QUOTE "PATH" "${SUSUWU_PROCESS_INCLUDES_OBJECT_}")"
break
elif [ ! -e "${SUSUWU_PROCESS_INCLUDES_SRCCXX_}" ]; then
# if [ ! -e "${SUSUWU_PROCESS_INCLUDES_OBJECT_}" ]; then
# SUSUWU_SET_NEW_BUILD true #/* Don't need `--rebuild`, but must regenerate `${SUSUWU_COMPILE_JSON_PATH}` */
# fi #/* TODO: remove this code block once `SUSUWU_HAS_PARTIAL_JSON()` is perfect. */
touch "${SUSUWU_PROCESS_INCLUDES_OBJECT_}"; #/* If `*.hxx` doesn't have `*.cxx` match, produce `*.o` (for future tests.) */
fi
done
echo "[" > "${SUSUWU_COMPILE_JSON_PATH}"
}
SUSUWU_BUILD_CTAGS() ( #/* Usage: `SUSUWU_BUILD_CTAGS [-flags... --flags...] [SOURCE_DIR]...`. Return value: if `ctags` is called; `0`, if not; `1`. */
SUSUWU_STATUS=1
if command -v ctags >/dev/null; then
if [ -z "${1}" ] || [ -z "${2}" ]; then
CTAGS_DEFAULTS="-R --exclude=.git/* --exclude=*.html --exclude=compile_commands.json --exclude=ml_dtypes/* --exclude=tensorflow/* ."
SUSUWU_PRINT "SUSUWU_BUILD_CTAGS()" "$(SUSUWU_SH_NOTICE)" "Was called with less than 2 params; will default to $(SUSUWU_SH_QUOTE "CODE" "SUSUWU_BUILD_CTAGS ${CTAGS_DEFAULTS}")."
#shellcheck disable=SC2086
ctags ${CTAGS_DEFAULTS} && SUSUWU_STATUS=0
else
FLAGS=${1}
#shellcheck disable=SC2086
ctags ${FLAGS} "${2}" && SUSUWU_STATUS=0
shift 2 #/* `${@:3}` requires `/bin/bash`. `shift X` sets `$@` to `${X+1} ... ${N-1}`. */
for SOURCE_PATH in "$@"; do
#shellcheck disable=SC2086
ctags ${FLAGS} -a "${SOURCE_PATH}" || SUSUWU_STATUS=1
done
fi
else
SUSUWU_PRINT "SUSUWU_BUILD_CTAGS()" "$(SUSUWU_SH_ERROR)" "\"ctags not found\"; use $(SUSUWU_SH_QUOTE "CODE" "apt install ctags")."
fi
return ${SUSUWU_STATUS};
)
SUSUWU_BUILD_OBJECTS() { #/* Usage: `SUSUWU_BUILD_OBJECTS "[${CC} || ${CXX}]" "[${CFLAGS} || ${CXXFLAGS}]" ".cxx" ${CXX_SOURCE_PATH}*.cxx [ optionalExtraPath/*.cxx ] [ ... ]*/
LOCAL_BUILD=${1}
LOCAL_BUILDFLAGS=${2} #/* `=$(echo ${2} | xargs)` strips quotes, which breaks `-DSUSUWU_DEFAULT_BRANCH="trunk"`. */
LOCAL_SOURCE_SUFFIX=${3}
shift 3 #/* `${@:4}` requires `/bin/bash`. `shift X` sets `$@` to `${X+1} ... ${N-1}`. */
SUSUWU_ECHO_COMMANDS true
#shellcheck disable=SC2068 #/* `"$@"` gives "clang++: error: no such file or directory: './cxx/*.cxx'" */
for LOCAL_SOURCE in $@; do
LOCAL_OBJECT="${OBJDIR}$(basename "${LOCAL_SOURCE}" "${LOCAL_SOURCE_SUFFIX}").o" #/* `basename`'s second param removes suffix */
#shellcheck disable=SC2166 #/* With `set -x`, the `[] || []` form prints 2 commands */
if [ -n "$(find "${LOCAL_SOURCE}" -newer "${LOCAL_OBJECT}" 2>/dev/null)" -o ! -s "${LOCAL_OBJECT}" ]; then
SUSUWU_COMPILE_COMMAND "$(pwd)" "${LOCAL_SOURCE}" "${LOCAL_BUILD} ${LOCAL_BUILDFLAGS} -c ${LOCAL_SOURCE} -o ${LOCAL_OBJECT}" || { #/* TODO: figure out how to quote `${LOCAL_BUILD}`, `${LOCAL_SOURCE}` `${LOCAL_OBJECT}` */
#shellcheck disable=SC2086 #/* `"${LOCAL_BUILDFLAGS}"` gives "clang++: error: language not recognized" */
# "${LOCAL_BUILD}" ${LOCAL_BUILDFLAGS} -c "${LOCAL_SOURCE}" -o "${LOCAL_OBJECT}" || {
SUSUWU_STATUS=$?
SUSUWU_ECHO_COMMANDS false
SUSUWU_BUILD_OBJECT_ERROR_CODE="$(SUSUWU_SH_QUOTE "CODE" "${LOCAL_BUILD}") returned status code $(SUSUWU_SH_QUOTE "STATUS" "${SUSUWU_STATUS}"). "
if [ true = ${SUSUWU_ABORT_ON_FIRST_ERROR} ]; then
SUSUWU_PRINT "SUSUWU_BUILD_OBJECTS()" "$(SUSUWU_SH_ERROR)" "${SUSUWU_BUILD_OBJECT_ERROR_CODE}[Due to $(SUSUWU_SH_QUOTE "CODE" "${0} $(SUSUWU_SH_QUOTE "CURRENT" "--abort-on-first-error")"), this is fatal.]"
exit 1
else
SUSUWU_PRINT "SUSUWU_BUILD_OBJECTS()" "$(SUSUWU_SH_WARNING)" "${SUSUWU_BUILD_OBJECT_ERROR_CODE}[Pass $(SUSUWU_SH_QUOTE "CODE" "${0} $(SUSUWU_SH_QUOTE "PROPOSED" "--abort-on-first-error")") to abort on such status codes.]"
fi
SUSUWU_ECHO_COMMANDS true
}
SUSUWU_RELINK=true
fi
SUSUWU_OBJECTLIST="${SUSUWU_OBJECTLIST} $(SUSUWU_ESCAPE_SPACES "${LOCAL_OBJECT}")"
done
}
SUSUWU_BUILD_EXECUTABLE() { #/* Usage: ... [SUSUWU_PROCESS_MINGW $@] SUSUWU_SETUP_CXX [SUSUWU_PROCESS_RELEASE_DEBUG $@] SUSUWU_SETUP_BUILD_FLAGS SUSUWU_SETUP_BINDIR "" SUSUWU_SETUP_OBJDIR "" SUSUWU_SETUP_OUTPUT "" [SUSUWU_PROCESS_CLEAN_REBUILD $@] [SUSUWU_PROCESS_INCLUDES ""] SUSUWU_BUILD_OBJECTS() SUSUWU_BUILD_EXECUTABLE() ... */
echo "]" >> "${SUSUWU_COMPILE_JSON_PATH}" && \
sed ':a;N;$!ba;s/},\n]/}\n]/g' -i'' "${SUSUWU_COMPILE_JSON_PATH}" 2>/dev/null
#shellcheck disable=SC2046 #/* Is not possible to use more quotes. */
${SUSUWU_RELINK} && "${LD}" $(echo "${LDFLAGS}${SUSUWU_OBJECTLIST}" | xargs) -o "${BINDIR}${OUTPUT}"
SUSUWU_STATUS=$?
SUSUWU_ECHO_COMMANDS false
if [ false = ${SUSUWU_RELINK} ]; then
SUSUWU_STATUS=0
SUSUWU_PRINT "SUSUWU_BUILD_EXECUTABLE()" "$(SUSUWU_SH_SUCCESS)" "Reused $(SUSUWU_SH_QUOTE "PATH" "${BINDIR}${OUTPUT}") ($(stat -c%s "${BINDIR}${OUTPUT}") bytes)."
elif [ 0 -eq ${SUSUWU_STATUS} ]; then
SUSUWU_PRINT "SUSUWU_BUILD_EXECUTABLE()" "$(SUSUWU_SH_SUCCESS)" "Produced $(SUSUWU_SH_QUOTE "PATH" "${BINDIR}${OUTPUT}") ($(stat -c%s "${BINDIR}${OUTPUT}") bytes)."
else
SUSUWU_PRINT "SUSUWU_BUILD_EXECUTABLE()" "$(SUSUWU_SH_ERROR)" "$(SUSUWU_SH_QUOTE "CODE" "${LD}") returned status code $(SUSUWU_SH_QUOTE "STATUS" "${SUSUWU_STATUS}"). [If errors include \"ld... $(SUSUWU_SH_QUOTE "STDERR" "unknown file type")\" or \"ld... $(SUSUWU_SH_QUOTE "STDERR" "undefined symbol __asan_")*\"; remove intermediate objects ($(SUSUWU_SH_QUOTE "CODE" "rm \"${OBJDIR}\"*.o")). Use $(SUSUWU_SH_QUOTE "CODE" "${0} ${SUSUWU_SH_CONSOLE_PARAMS} $(SUSUWU_SH_QUOTE "PROPOSED" "--rebuild")") to remove plus continue."
fi
return ${SUSUWU_STATUS}
}
SUSUWU_TEST_OUTPUT() { #/* Usage: ... `SUSUWU_BUILD_EXECUTABLE && SUSUWU_TEST_OUTPUT [<silent-execution>]; exit $?` */
if [ 0 -eq ${SUSUWU_STATUS} ] || [ false = ${SUSUWU_RELINK} ]; then
if [ -z "${CROSS_COMP}" ]; then #/* `if("" == CROSS_COMP)` */
if [ true = ${SUSUWU_S} ]; then
"${BINDIR}${OUTPUT}" 2>/dev/null 1>&2
else
"${BINDIR}${OUTPUT}"
fi
else #/* if `--mingw` */
if ! command -v wine >/dev/null; then
SUSUWU_PRINT "SUSUWU_TEST_OUTPUT()" "$(SUSUWU_SH_INFO)" "$(SUSUWU_SH_QUOTE "CODE" "wine not found"). Use $(SUSUWU_SH_QUOTE "CODE" "apt install wine")."
return 1
fi
if [ true = ${SUSUWU_S} ]; then
wine "${BINDIR}${OUTPUT}" 2>/dev/null 1>&2
else
wine "${BINDIR}${OUTPUT}"
fi
fi
SUSUWU_STATUS=$?
if [ 0 -eq ${SUSUWU_STATUS} ]; then
SUSUWU_PRINT "SUSUWU_TEST_OUTPUT()" "$(SUSUWU_SH_SUCCESS)" "$(SUSUWU_SH_QUOTE "CODE" "${BINDIR}${OUTPUT}") returned status SusuwuUnitTestsBitmask($(SUSUWU_SH_QUOTE "STATUS" "${SUSUWU_STATUS}"))."
else
SUSUWU_PRINT "SUSUWU_TEST_OUTPUT()" "$(SUSUWU_SH_ERROR)" "$(SUSUWU_SH_QUOTE "CODE" "${BINDIR}${OUTPUT}") returned status SusuwuUnitTestsBitmask($(SUSUWU_SH_QUOTE "STATUS" "${SUSUWU_STATUS}"))."
fi
fi
return ${SUSUWU_STATUS}
}
SUSUWU_TODO_LIST() ( #/* Usage: `SUSUWU_TODO_LIST "NameOfTool" "what to expect from future versions"` */
SUSUWU_PRINT "SUSUWU_TODO_LIST()" "$(SUSUWU_SH_ERROR)" "$(SUSUWU_SH_QUOTE "CODE" "${1}") ${2} is on a long \"TODO\" list. If you want this soon; you can [contribute](https://github.com/SwuduSusuwu/SusuLib/#Contributor-conventionsrules), or [ask for this](https://github.com/SwuduSusuwu/SusuLib/issues/new)."
exit 1
)
SUSUWU_FORMAT() ( #/* TODO */
SUSUWU_TODO_LIST "SUSUWU_FORMAT()" "(which is analogous to \`make format\`, and will use tools such as $(SUSUWU_SH_QUOTE "CODE" "prettier") or $(SUSUWU_SH_QUOTE "CODE" "clang-format"))"
)
SUSUWU_DOCS() ( #/* TODO */
SUSUWU_TODO_LIST "SUSUWU_DOCS()" "(which is analogous to \`make docs\`, and will use tools such as $(SUSUWU_SH_QUOTE "CODE" "sphinx") or $(SUSUWU_SH_QUOTE "CODE" "doxygen"))"
)
SUSUWU_HAS_USABLE_USRBIN() ( #/* Usage: `[ 0 -eq $(SUSUWU_HAS_USABLE_USRBIN("${USRBIN}")) ]`. Is just for internal use. */
if [ ! -d "${1}" ]; then
return 1
fi
if [ -z "${PATH}" ]; then
SUSUWU_PRINT "SUSUWU_HAS_USABLE_USRBIN()" "$(SUSUWU_SH_WARNING)" "$(SUSUWU_SH_QUOTE "CODE" "\${PATH}") not set; will use \`$(SUSUWU_SH_QUOTE "VAR" "USRBIN")=\"$(SUSUWU_SH_QUOTE "PROPOSED" "${1}")\"\` (first guessed path which exists)."
return 0
fi
IFS=":" #/* `PATH` format is `"PATH_0:PATH_1:PATH_N-1"`. */
for PATH_W in ${PATH}; do
if [ "${PATH_W}" = "${1}" ]; then
return 0
fi
done
return 1
)
SUSUWU_FIRST_PATH() ( #/* Usage: `USRBIN="$(SUSUWU_FIRST_PATH)"`. Is just for internal use. */
IFS=":" #/* `PATH` format is `"PATH_0:PATH_1:PATH_N-1"`. */
for PATH_W in ${PATH}; do #/* With `in "${PATH|"`, this will not loop */
if [ -d "${PATH_W}" ]; then
echo "${PATH_W}"
return 0
fi
done
SUSUWU_PRINT "SUSUWU_FIRST_PATH()" "$(SUSUWU_SH_ERROR)" "$(SUSUWU_SH_ARG "\${PATH}") not set."
return 1
)
SUSUWU_PROCESS_USRBIN() { #/* Usage: `SUSUWU_USRBIN ["SUSUWU_[UN]INSTALL"] ["/usr/bin"...]`. Is just for internal use. */
if [ -n "${2}" ]; then #/* function `${1}` was passed USRBIN as its first param (our second param) */
USRBIN="${2}"
USRBIN="$(SUSUWU_PATH_UNAMBIGUOUS "${USRBIN}")"
if [ -d "${USRBIN}" ]; then
return 0
else
SUSUWU_PRINT "SUSUWU_PROCESS_USRBIN()" "$(SUSUWU_SH_WARNING)" "$(SUSUWU_SH_QUOTE "CODE" "${1}") was passed \`$(SUSUWU_SH_QUOTE "VAR" "USRBIN")=\"$(SUSUWU_SH_QUOTE "CURRENT" "${USRBIN}")\"\` (which is not a directory); will autodetect \`$(SUSUWU_SH_QUOTE "VAR" "USRBIN")\`."
fi
fi
for USRBIN in "$(realpath -q ~/.local/bin)" "$(realpath -q ~/../usr/bin)" "$(realpath -q ~/../usr/local/bin)" "$(realpath -q ~/../local/bin)" "$(dirname "$(which sh)")" "/usr/local/bin" "/usr/bin" "/bin"; do
if SUSUWU_HAS_USABLE_USRBIN "${USRBIN}"; then
break
fi
USRBIN=""
done
if [ -z "${USRBIN}" ]; then #/* if array above is "", or does not have usable values */
SUSUWU_PRINT "SUSUWU_PROCESS_USRBIN()" "$(SUSUWU_SH_NOTICE)" "None of the usual paths for \`$(SUSUWU_SH_QUOTE "VAR" "USRBIN")\` suitable to use; will use the first directory from \`$(SUSUWU_SH_QUOTE "VAR" "\${PATH}")\` which exists."
USRBIN="$(SUSUWU_FIRST_PATH)"
fi
USRBIN="$(SUSUWU_PATH_UNAMBIGUOUS "${USRBIN}")"
if [ -e "${USRBIN}" ]; then
SUSUWU_PRINT "SUSUWU_PROCESS_USRBIN()" "$(SUSUWU_SH_NOTICE)" "Have set \`$(SUSUWU_SH_QUOTE "VAR" "USRBIN")=\"$(SUSUWU_SH_QUOTE "PROPOSED" "${USRBIN}")\"\`, since $(SUSUWU_SH_QUOTE "CODE FUNCTION" "${1}") was not passed \`$(SUSUWU_SH_QUOTE "VAR" "USRBIN")\`."
return 0
fi
SUSUWU_PRINT "SUSUWU_PROCESS_USRBIN()" "$(SUSUWU_SH_ERROR)" "Failed to autodetect \`$(SUSUWU_SH_QUOTE "VAR" "USRBIN")\` (which $(SUSUWU_SH_QUOTE "CODE FUNCTION" "${1}") uses)."
return 1 #/* Notice: `exit 1` would cause GitHub rubbers (with unknown install paths) to fail unit tests. */
}
SUSUWU_INSTALL() ( #/* Usage: `SUSUWU_INSTALL [USRBIN]`. Is analogous to `make install`. */
SUSUWU_PROCESS_USRBIN "SUSUWU_INSTALL()" "${1}" &&
cp --interactive "${BINDIR}${OUTPUT}" "${USRBIN}/${OUTPUT}"
#/* TODO: multiple output bins, support libs, configs */
) #/* returns result of `cp`; thus `SUSUWU_INSTALL && SUSUWU_UNINSTALL` won't remove files unless the interactive `SUSUWU_INSTALL` has total success */
SUSUWU_UNINSTALL() ( #/* Usage: `SUSUWU_UNINSTALL [USRBIN]`. Is analogous to `make uninstall`. */
SUSUWU_PROCESS_USRBIN "SUSUWU_UNINSTALL()" "${1}" &&
rm "${USRBIN}/${OUTPUT}"
)
SUSUWU_FIND_INCLUDE() { #/* Usage: `SUSUWU_FIND_INCLUDE "<include dir/>" ["<specific subpath/>"] */
for SUSUWU_FIND_INCLUDE_PATH in "./include/" "./include/${1}" "./${1}" "../${1}" "${HOME}/../usr/local/include/" "${HOME}/../usr/local/include/${1}" "${HOME}/../usr/include/" "${HOME}/../usr/include/${1}" "${HOME}/../usr/include/${1}" "/usr/local/include/" "/usr/local/include/${1}" "/usr/include/" "/usr/include/${1}"; do
if [ -e "${SUSUWU_FIND_INCLUDE_PATH}${2}" ]; then #/* If caller passes some `<specific subpath>` (`${2}`), assume that `<include dir>` (`${1}`) has collisions without `<specific subpath>` (`${2}`) */
echo "${SUSUWU_FIND_INCLUDE_PATH}" | sed 's|[^/]\+/../|/|g' | sed 's|//|/|g' #/* Squished path. Use `realpath "${SUSUWU_FIND_INCLUDE_PATH}"` for absolute path. */
return 0
fi
done
return 1
}
SUSUWU_DEPENDENCY_INCLUDE() { #/* Usage: `SUSUWU_DEPENDENCY_INCLUDE "<-I | -F>" "<package>" "<include dir>" "<include subdir>" ["<install-howto>"] */
SUSUWU_DEPENDENCY_INCLUDE_PATH="$(SUSUWU_FIND_INCLUDE "${3}" "${4}")"
if [ -d "${SUSUWU_DEPENDENCY_INCLUDE_PATH}" ]; then
export FLAGS_USER="${FLAGS_USER} ${1}${SUSUWU_DEPENDENCY_INCLUDE_PATH}"
SUSUWU_PRINT "SUSUWU_DEPENDENCY_INCLUDE()" "$(SUSUWU_SH_NOTICE)" "Found package $(SUSUWU_SH_QUOTE "CODE" "${2}"), $(SUSUWU_SH_QUOTE "VAR" "FLAGS_USER") will use $(SUSUWU_SH_QUOTE "CODE" "${1}${SUSUWU_DEPENDENCY_INCLUDE_PATH}")."
return 0
fi
SUSUWU_PRINT "SUSUWU_DEPENDENCY_INCLUDE()" "$(SUSUWU_SH_WARNING)" "Package $(SUSUWU_SH_QUOTE "CODE" "${2}") not found.${5:+" To install, use $(SUSUWU_SH_QUOTE "CODE" "${5}")"}" #/* For most packages, `<install-howto>` (`${5}`) is close to "sudo apt install ${2} || git clone https://github.com/${2}/${3}.git --depth 1" */
return 1
}
Termux output view
Related posts
Howto: use local static analysis + sandboxes + artificial CNS (central nervous systems) to secure computers.
[This post allows all uses.] [Version of post is #7805b6b]. For most new (which now includes TensorFlow implementation of cxx/ClassCns.hxx, view on GitHub.
uses this build system.