diff --git a/.gitignore b/.gitignore index 8357aa23..552a2a3a 100644 --- a/.gitignore +++ b/.gitignore @@ -53,6 +53,16 @@ stamp-h1 # Other compile build.log +clean.log +make.log +.previous_build +.current_build # Visual Studio .vs + +# Common editor temp/backup files +*~ +*.swp +*.tmp + diff --git a/scripts/automator/build/clang-darwin_x86_64 b/scripts/automator/build/clang-darwin_x86_64 new file mode 100644 index 00000000..9b46a13b --- /dev/null +++ b/scripts/automator/build/clang-darwin_x86_64 @@ -0,0 +1,8 @@ +ld="ld" + +TYPES+=("msan" "usan") +cflags_msan=("${cflags_debug[@]}" "-fsanitize-recover=all" "-fsanitize=memory" "-fno-omit-frame-pointer") +cflags_usan=("${cflags_debug[@]}" "-fsanitize-recover=all" "-fsanitize=undefined") + +MODIFIERS+=("lto") +cflags_lto=("-flto=thin") diff --git a/scripts/automator/build/clang-defaults b/scripts/automator/build/clang-defaults new file mode 100644 index 00000000..5792c396 --- /dev/null +++ b/scripts/automator/build/clang-defaults @@ -0,0 +1,16 @@ +# Tool overrides +cc="clang${postfix}" +cxx="clang++${postfix}" + +# Flag additions +TYPES+=("debug" "profile") +cflags+=("-fcolor-diagnostics") +cflags_release=("${cflags[@]}" "-Os") +cflags_debug=("${cflags[@]}" "-g" "-Og" "-fno-omit-frame-pointer") +cflags_profile=("${cflags_debug[@]}" "-fprofile-instr-generate" "-fcoverage-mapping") + + +# Modifier additions +MODIFIERS=("fdo") +ldflags_fdo=("-fprofile-instr-generate") +cflags_fdo=("-fprofile-sample-use=${FDO_FILE:-}") diff --git a/scripts/automator/build/clang-linux_x86_64 b/scripts/automator/build/clang-linux_x86_64 new file mode 100644 index 00000000..0b37c67d --- /dev/null +++ b/scripts/automator/build/clang-linux_x86_64 @@ -0,0 +1,13 @@ +# Tool additions +ar="llvm-ar${postfix}" +ld="llvm-link${postfix}" +ranlib="llvm-ranlib${postfix}" + +# Build additions +TYPES+=("msan" "usan") +cflags_msan=("${cflags_debug[@]}" "-fsanitize-recover=all" "-fsanitize=memory" "-fno-omit-frame-pointer") +cflags_usan=("${cflags_debug[@]}" "-fsanitize-recover=all" "-fsanitize=undefined") + +# Modifier additions +MODIFIERS+=("lto") +cflags_lto=("-O2" "-flto=thin") diff --git a/scripts/automator/build/clang-msys_nt_x86_64 b/scripts/automator/build/clang-msys_nt_x86_64 new file mode 100644 index 00000000..f183a990 --- /dev/null +++ b/scripts/automator/build/clang-msys_nt_x86_64 @@ -0,0 +1,5 @@ +# Flag additions +ar="llvm-ar${postfix}" +ld="llvm-link${postfix}" +ranlib="llvm-ranlib${postfix}" +ldflags+=("-static-libgcc" "-static-libstdc++") diff --git a/scripts/automator/build/compiler-clang b/scripts/automator/build/compiler-clang new file mode 100644 index 00000000..e69de29b diff --git a/scripts/automator/build/compiler-defaults b/scripts/automator/build/compiler-defaults new file mode 100644 index 00000000..39be7a47 --- /dev/null +++ b/scripts/automator/build/compiler-defaults @@ -0,0 +1,14 @@ +# Tools and flags for all compilers +VARIABLES=("ar" "cc" "cxx" "ld" "ranlib" "cflags" "ldflags" "libs") +ar="" +cc="" +cxx="" +ld="" +ranlib="" +cflags=("-Wall" "-pipe") +ldflags=("") +libs=("") + +# Builds for all compilers +TYPES=("release") +cflags_release=("${cflags[@]}") diff --git a/scripts/automator/build/compiler-gcc b/scripts/automator/build/compiler-gcc new file mode 100644 index 00000000..e69de29b diff --git a/scripts/automator/build/gcc-darwin_x86_64 b/scripts/automator/build/gcc-darwin_x86_64 new file mode 100644 index 00000000..1a55e14d --- /dev/null +++ b/scripts/automator/build/gcc-darwin_x86_64 @@ -0,0 +1,15 @@ +# Tool overrides +ar="ar" +ranlib="ranlib" + +# Build additions +TYPES+=("asan" "uasan" "usan" "tsan") +cflags_asan=("${cflags_debug[@]}" "-fsanitize=address") +cflags_uasan=("${cflags_debug[@]}" "-fsanitize=address,undefined" "-fsanitize-recover=signed-integer-overflow") +cflags_usan=("${cflags_debug[@]}" "-fsanitize=undefined" "-fsanitize-recover=signed-integer-overflow") +cflags_tsan=("${cflags_debug[@]}" "-fsanitize=thread") + +# Modifier additions +MODIFIERS+=("lto") +cflags_lto=("-flto") +ldflags_lto=("${cflags[@]}" "-flto") diff --git a/scripts/automator/build/gcc-defaults b/scripts/automator/build/gcc-defaults new file mode 100644 index 00000000..aa9e4cff --- /dev/null +++ b/scripts/automator/build/gcc-defaults @@ -0,0 +1,17 @@ +# Tool overrides +ar="gcc-ar${postfix}" +cc="gcc${postfix}" +cxx="g++${postfix}" +ld="gcc${postfix}" +ranlib="gcc-ranlib${postfix}" + +# Flag additions +TYPES+=("debug" "profile") +cflags+=("-fstack-protector" "-fdiagnostics-color=always") +cflags_release=("${cflags[@]}" "-Ofast" "-ffunction-sections" "-fdata-sections") +cflags_debug=("${cflags[@]}" "-g" "-Og" "-fno-omit-frame-pointer") +cflags_profile=("${cflags_debug[@]}" "-pg") + +# Modifier additions +MODIFIERS=("fdo") +cflags_fdo=("-fauto-profile=${FDO_FILE:-}") diff --git a/scripts/automator/build/gcc-linux_x86_64 b/scripts/automator/build/gcc-linux_x86_64 new file mode 100644 index 00000000..4da89b92 --- /dev/null +++ b/scripts/automator/build/gcc-linux_x86_64 @@ -0,0 +1,14 @@ +# Tool additions +ldflags+=("-Wl,--as-needed") + +# Build additions +TYPES+=("asan" "uasan" "usan" "tsan") +cflags_asan=("${cflags_debug[@]}" "-fsanitize=address") +cflags_uasan=("${cflags_debug[@]}" "-fsanitize=address,undefined" "-fsanitize-recover=signed-integer-overflow") +cflags_usan=("${cflags_debug[@]}" "-fsanitize=undefined" "-fsanitize-recover=signed-integer-overflow") +cflags_tsan=("${cflags_debug[@]}" "-fsanitize=thread") + +# Modifier additions +MODIFIERS+=("lto") +cflags_lto=("-flto") +ldflags_lto=("${cflags[@]}" "-flto=$(( $(nproc) + 2 ))") diff --git a/scripts/automator/build/gcc-msys_nt_x86_64 b/scripts/automator/build/gcc-msys_nt_x86_64 new file mode 100644 index 00000000..785485f2 --- /dev/null +++ b/scripts/automator/build/gcc-msys_nt_x86_64 @@ -0,0 +1,3 @@ +# Flag additions +ldflags+=("-Wl,--as-needed" "-static-libgcc" "-static-libstdc++") + diff --git a/scripts/automator/build/machine-x86_64 b/scripts/automator/build/machine-x86_64 new file mode 100644 index 00000000..052fa22c --- /dev/null +++ b/scripts/automator/build/machine-x86_64 @@ -0,0 +1,3 @@ +# Modifier additions universal for gcc and clang, but specific to x86_64: +MODIFIERS+=("native") +cflags_native=("-march=native") diff --git a/scripts/automator/build/os-darwin b/scripts/automator/build/os-darwin new file mode 100644 index 00000000..de969cee --- /dev/null +++ b/scripts/automator/build/os-darwin @@ -0,0 +1,5 @@ +# Tool additions and overrides for Darwin, regardless of compiler +ar="ar" +ranlib="ranlib" +function make_binary() { make -j$(sysctl -n hw.physicalcpu) 2>&1 | tee build.log; } +dependencies=("otool" "-L" "${executable}") diff --git a/scripts/automator/build/os-defaults b/scripts/automator/build/os-defaults new file mode 100644 index 00000000..cf0d6028 --- /dev/null +++ b/scripts/automator/build/os-defaults @@ -0,0 +1,57 @@ +# Steps in-common to all OSes, plus some helper variables and functions. + +STEPS=("pre_build" "clean" "autogen" "configure" "make_binary" "strip_binary" "dependencies" "post_build") + +executable="src/dosbox" +dependencies=("ldd" "${executable}") + +function pre_build() { + cd ../.. + underline "Environment" + echo "[$("${CC}" --version | head -1)]" + for v in "${VARIABLES[@]}"; do + vupper=$(upper "${v}") + echo "${vupper}=\"$(eval echo \$"${vupper}")\"" + done + underline "Launching" +} + +function stamp() { + echo "${compiler}-${selected_type}-${modifiers[*]}" +} + +function autogen() { + if [[ ! -f configure || configure.ac -nt configure ]]; then + ./autogen.sh + fi +} + +function configure() { + if [[ ! -f Makefile || ! -f .previous_build || configure -nt Makefile ]]; then + export CXXFLAGS="${CFLAGS}" + ./configure --enable-core-inline + fi +} + +function strip_binary() { + if [[ "${selected_type}" == "release" ]]; then + strip "${executable}" + fi +} + +function post_build() { + underline "Binary" "-" + ls -1lh "${executable}" + stamp > .previous_build +} + +function clean() { + stamp > .current_build + if [[ -f Makefile ]]; then + if [[ ! -f .previous_build ]] || ! diff -q .previous_build .current_build &> /dev/null; then + rm -f .previous_build + echo "cleaning, this is a different build type" + make clean &> clean.log || cat clean.log + fi + fi +} diff --git a/scripts/automator/build/os-linux b/scripts/automator/build/os-linux new file mode 100644 index 00000000..a831d4d3 --- /dev/null +++ b/scripts/automator/build/os-linux @@ -0,0 +1,2 @@ +function make_binary() { make -j$(nproc) 2>&1 | tee build.log; } + diff --git a/scripts/automator/build/os-msys_nt b/scripts/automator/build/os-msys_nt new file mode 100644 index 00000000..9cfcbc5a --- /dev/null +++ b/scripts/automator/build/os-msys_nt @@ -0,0 +1,3 @@ +function make_binary() { make -j$(nproc) 2>&1 | tee build.log; } +executable="src/dosbox.exe" + diff --git a/scripts/automator/main.sh b/scripts/automator/main.sh new file mode 100644 index 00000000..19b54a77 --- /dev/null +++ b/scripts/automator/main.sh @@ -0,0 +1,120 @@ +#!/bin/bash + +# Copyright (c) 2019 Kevin R Croft +# SPDX-License-Identifier: GPL-2.0-or-later + +# Automator is a generic automation tool that helps separate data +# from logical code. It uses one or more "variables" files placed +# in a single sub-directory, that are sourced in logical succession, +# such as variables defined in earler files are either added-to or +# overridden in subsequent files. +# +# For a thorough example, see scripts/build.sh and its data files +# in scripts/automator/build. +# +# TODO: This script manages variables using eval meta-programming. +# Although the syntax is quite readable under bash 4.x, +# we have limitted our syntax to the uglier and heavier bash 3.x +# because Apple is still only shipping bash 3.x, even on their +# latest operating system (Nov-2019). +# +# Future work might involve switching this to python and using YAML +# syntax in the variables files. Python's dictionaries can be +# merged or have their values overridden, and key names can be readily +# manipulated. +# +set -euo pipefail + +function underline() { + echo "" + echo "$1" + echo "${1//?/${2:--}}" +} + +function lower() { + echo "${1}" | tr '[:upper:]' '[:lower:]' +} + +function upper() { + echo "${1}" | tr '[:lower:]' '[:upper:]' +} + +function arg_error() { + local PURPLE='\033[0;34m' + local BLACK='\033[0;30m' + local ON_WHITE='\033[47m' + local NO_COLOR='\033[0m' + >&2 echo -e "Please specify the ${BLACK}${ON_WHITE}${1}${NO_COLOR} argument with one of: ${PURPLE}${2}${NO_COLOR}" + exit 1 +} + +function import() { # arguments: category instance + category=$(lower "${1}") + instance=$(lower "${2}") + if [[ -z "$instance" ]]; then + instances=( "$(find "${data_dir}" -name "${category}-*" -a ! -name '*defaults' | sed 's/.*-//' | xargs)" ) + arg_error "--${category}" "${instances[*]}" + else for instance in defaults "${instance}"; do + varfile="${data_dir}/${category}-${instance}" + # shellcheck disable=SC1090,SC1091 + if [[ -f "$varfile" ]]; then source "$varfile" + else >&2 echo "${varfile} does not exist, skipping"; fi; done + fi +} + +function construct_environment() { + # underline "Environment" + for base_var in "${VARIABLES[@]}"; do + # First check if we have a build-specific variable + # shellcheck disable=SC2154 + if [[ -n "$(eval 'echo ${'"${base_var}_${selected_type}"'[*]:-}')" ]]; then + var_name="${base_var}_${selected_type}" + # Otherwise fallback to the default variable + else var_name="${base_var}"; fi + + # Aggregate any modifiers, which are stand-alone arguments + mod_values="" + # shellcheck disable=SC2154 + for mod_upper in "${modifiers[@]}"; do + mod=$(lower "${mod_upper}") + # Mods can only add options to existing VARs, so we check for those + if [[ -n "$(eval 'echo ${'"${base_var}_${mod}"'[*]:-}')" ]]; then + var_with_mod="${base_var}_${mod}" + # shellcheck disable=SC2034 + mod_values=$(eval 'echo ${mod_values} ${'"${var_with_mod}"'[*]}') + fi + done + + # Expand our variable array (or scalar) and combine it with our aggregated + # modification string. Echo takes care of spacing out our variables without + # explicit padding, because it either drops empty variables or adds spaces + # between them if they exist. + # shellcheck disable=SC2034 + combined=$(eval 'echo ${'"${var_name}"'[*]} ${mod_values}') + env_var=$(upper "${base_var}") + eval 'export '"${env_var}"'="${combined}"' + # echo "${env_var}=\"$(eval echo \$"${env_var}")\"" + done +} + +function perform_steps() { + # underline "Launching" + for step in "${STEPS[@]}"; do + if type -t "$step" | grep -q function; then "$step" + else + # eval 'echo ${'"${step}"'[*]}' + eval '${'"${step}"'[*]}' + fi + done +} + +function main() { + cd "$(dirname "$0")/automator" + if [[ -z "${data_dir:-}" ]]; then data_dir="$(basename "$0" '.sh')"; fi + parse_args "$@" + construct_environment + perform_steps +} + +main "$@" + diff --git a/scripts/automator/packages/clang-brew b/scripts/automator/packages/clang-brew new file mode 100644 index 00000000..03221244 --- /dev/null +++ b/scripts/automator/packages/clang-brew @@ -0,0 +1,3 @@ +# Brew does not supply "clang", so exclude it here +compiler="" + diff --git a/scripts/automator/packages/clang-defaults b/scripts/automator/packages/clang-defaults new file mode 100644 index 00000000..3e13216f --- /dev/null +++ b/scripts/automator/packages/clang-defaults @@ -0,0 +1,2 @@ +compiler=(clang${postfix}) + diff --git a/scripts/automator/packages/clang-macports b/scripts/automator/packages/clang-macports new file mode 100644 index 00000000..087b8d4a --- /dev/null +++ b/scripts/automator/packages/clang-macports @@ -0,0 +1,2 @@ +# macports doesn't supply Clang, so knock it out here +compiler="" diff --git a/scripts/automator/packages/clang-msys2 b/scripts/automator/packages/clang-msys2 new file mode 100644 index 00000000..bf1117b2 --- /dev/null +++ b/scripts/automator/packages/clang-msys2 @@ -0,0 +1,4 @@ +# Under MSYS2, only the 'default' version is available, so we don't postfix the package +compiler="mingw-w64-${pkg_type}-${selected_type}" + + diff --git a/scripts/automator/packages/clang-vcpkg b/scripts/automator/packages/clang-vcpkg new file mode 100644 index 00000000..3ec49cc7 --- /dev/null +++ b/scripts/automator/packages/clang-vcpkg @@ -0,0 +1,4 @@ +# vcpkg doesn't provide clang or gcc, because it assumes you're using MS VisualStudio. +# So we knock out the compiler. +compiler="" + diff --git a/scripts/automator/packages/gcc-apt b/scripts/automator/packages/gcc-apt new file mode 100644 index 00000000..1b851dc9 --- /dev/null +++ b/scripts/automator/packages/gcc-apt @@ -0,0 +1,2 @@ +compiler=(g++${postfix}) + diff --git a/scripts/automator/packages/gcc-defaults b/scripts/automator/packages/gcc-defaults new file mode 100644 index 00000000..99173883 --- /dev/null +++ b/scripts/automator/packages/gcc-defaults @@ -0,0 +1 @@ +compiler=(gcc${postfix}) diff --git a/scripts/automator/packages/gcc-msys2 b/scripts/automator/packages/gcc-msys2 new file mode 100644 index 00000000..fb38a352 --- /dev/null +++ b/scripts/automator/packages/gcc-msys2 @@ -0,0 +1,3 @@ +# Under MSYS2 only the 'default' version of gcc is available, so we don't postfix it with the version +compiler="mingw-w64-${pkg_type}-${selected_type}" + diff --git a/scripts/automator/packages/gcc-vcpkg b/scripts/automator/packages/gcc-vcpkg new file mode 100644 index 00000000..533ed5dc --- /dev/null +++ b/scripts/automator/packages/gcc-vcpkg @@ -0,0 +1,4 @@ +# vcpkg doesn't provide clang or gcc, because it assumes you're using MS VisualStudio. +# So we knock out the compiler. +compiler="" + diff --git a/scripts/automator/packages/manager-apt b/scripts/automator/packages/manager-apt new file mode 100644 index 00000000..6c136b28 --- /dev/null +++ b/scripts/automator/packages/manager-apt @@ -0,0 +1,2 @@ +# Package repo: https://packages.ubuntu.com/ +packages+=(xvfb libtool build-essential libsdl1.2-dev libsdl-net1.2-dev libopusfile-dev) diff --git a/scripts/automator/packages/manager-brew b/scripts/automator/packages/manager-brew new file mode 100644 index 00000000..14d21645 --- /dev/null +++ b/scripts/automator/packages/manager-brew @@ -0,0 +1,3 @@ +# Package repo: https://formulae.brew.sh/ +delim="@" +packages+=(coreutils autogen autoconf automake pkg-config libpng sdl sdl_net opusfile) diff --git a/scripts/automator/packages/manager-defaults b/scripts/automator/packages/manager-defaults new file mode 100644 index 00000000..b18fa423 --- /dev/null +++ b/scripts/automator/packages/manager-defaults @@ -0,0 +1,11 @@ +VARIABLES=(packages bits delim compiler) +TYPES=(gcc clang) + +packages=(autoconf-archive zstd) +delim="-" +compiler="" + +STEPS=(print) +function print() { + echo "${PACKAGES[*]}" "${COMPILER}" +} diff --git a/scripts/automator/packages/manager-dnf b/scripts/automator/packages/manager-dnf new file mode 100644 index 00000000..442183fd --- /dev/null +++ b/scripts/automator/packages/manager-dnf @@ -0,0 +1,2 @@ +# Package repo: https://apps.fedoraproject.org/packages/ +packages+=(xvfb libtool SDL SDL_net-devel opusfile-devel) diff --git a/scripts/automator/packages/manager-macports b/scripts/automator/packages/manager-macports new file mode 100644 index 00000000..fe0b99ad --- /dev/null +++ b/scripts/automator/packages/manager-macports @@ -0,0 +1,3 @@ +# Package repo: https://www.macports.org/ports.php?by=name +delim="" +packages+=(coreutils autogen autoconf automake pkgconfig libpng libsdl libsdl_net opusfile) diff --git a/scripts/automator/packages/manager-msys2 b/scripts/automator/packages/manager-msys2 new file mode 100644 index 00000000..d5646a7a --- /dev/null +++ b/scripts/automator/packages/manager-msys2 @@ -0,0 +1,7 @@ +# Package repo: https://packages.msys2.org/base +# MSYS2 only supports the current latest releases of Clang and GCC, so we disable version customization +packages+=(autogen autoconf base-devel automake-wrapper binutils) +pkg_type=$([[ "${bits}" == "64" ]] && echo "x86_64" || echo "i686") +for pkg in pkg-config libtool libpng zlib SDL SDL_net opusfile; do + packages+=("mingw-w64-${pkg_type}-${pkg}") +done diff --git a/scripts/automator/packages/manager-pacman b/scripts/automator/packages/manager-pacman new file mode 100644 index 00000000..7ae2cb85 --- /dev/null +++ b/scripts/automator/packages/manager-pacman @@ -0,0 +1,4 @@ +# Package repo: https://www.archlinux.org/packages/ +# Arch offers 32-bit versions of SDL (but not others) +packages+=(xvfb libtool sdl_net opusfile) +[[ "${bits}" == "32" ]] && packages+=(lib32-sdl) || packages+=(sdl) diff --git a/scripts/automator/packages/manager-vcpkg b/scripts/automator/packages/manager-vcpkg new file mode 100644 index 00000000..7cff149d --- /dev/null +++ b/scripts/automator/packages/manager-vcpkg @@ -0,0 +1,2 @@ +# Package repo: https://repology.org/projects/?inrepo=vcpkg +packages+=(libpng sdl1 sdl1-net opusfile) diff --git a/scripts/automator/packages/manager-zypper b/scripts/automator/packages/manager-zypper new file mode 100644 index 00000000..44bcdbeb --- /dev/null +++ b/scripts/automator/packages/manager-zypper @@ -0,0 +1,10 @@ +# Package repo: https://pkgs.org/ +# openSUSE offers 32-bit versions of SDL and SDL_net (but not others) +packages+=(devel_basis xvfb libtool opusfile) + +if [[ "${bits}" == "32" ]]; then + packages+=(libSDL-devel-32bit libSDL_net-devel-32bit) +else + packages+=(SDL SDL_net) +fi + diff --git a/scripts/build.md b/scripts/build.md index c2b85d73..0db2bead 100644 --- a/scripts/build.md +++ b/scripts/build.md @@ -57,7 +57,7 @@ Use of both scripts is described below. `./scripts/list-build-dependencies.sh -p msys2 | xargs pacman -S --noconfirm` 1. Launch the build script with default settings: - `./scripts/build.sh --bin-path /mingw64/bin` + `./scripts/build/run.sh --bin-path /mingw64/bin` ## MacOS Installation and Usage @@ -131,10 +131,15 @@ options to the **list-build-dependencies.sh** and **build.sh** scripts: After building, your `dosbox` or `dosbox.exe` binary will reside inside `./dosbox-staging/src/`. Build flags you might be interested in: -* `--release debug`, to build a binary containing debug symbols (instead of **fast** or **small**) * `--lto`, perform optimizations across the entire object space instead of per-file (Only available on Mac and Linux) - -The above flags are othogonal and thus can be mixed-and-matched as desired. +* `--release debug`, to build a binary containing debug symbols + * You can run the resulting binary in the GNU debugger: `gdb /path/to/dosbox`, followed by `start mygame.bat` +* `--release profile`, to generate performance statistics + * Instructions are provided after the build completes, which describe how to generate and process the profiling data +* `--release `, to build a binary that performs dynamic code-analysis at runtime (Linux and macOS) + * see `./scripts/build.sh --help` for a list of sanitizer-types that are available + * Run your binary like normal and it will generate output describing problematic behavior + * Some sanitizers accept runtime options via an environment variables, such as `ASAN_OPTIONS`, described here: https://github.com/google/sanitizers/wiki/AddressSanitizerFlags If you want to run multiple back-to-back builds from the same directory with different settings then add the `--clean` flag to ensure previous objects and binaries are removed. diff --git a/scripts/build.sh b/scripts/build.sh index 21aa3863..a2f80c48 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -1,518 +1,61 @@ #!/bin/bash -## -# -# Copyright (c) 2019 Kevin R. Croft -# SPDX-License-Identifier: GPL-2.0-or-later -# -# This script builds DOSBox within supported environments including -# MacOS, Ubuntu Linux, and MSYS2 using specified compilers and release types. -# -# For automation without prompts, Windows should have User Account Control (UAC) -# disabled, which matches the configuration of GitHub'Windows VMs, described here: -# https://help.github.com/en/articles/virtual-environments-for-github-actions -# -# See the usage block below for details or run it with the -h or --help arguments. -# -# In general, this script adheres to Google's shell scripting style guide -# (https://google.github.io/styleguide/shell.xml), however some deviations (such as -# tab indents instead of two-spaces) are used to fit with DOSBox's in-practice" -# coding style. -# +# Copyright (c) 2019 Kevin R Croft +# SPDX-License-Identifier: GPL-2.0-or-later +# This script builds the software for the given build-type (release, +# debug, ... ) and compiler (gcc or clang). +# +# If run without arguments, the script asks for required arguments one +# by one, which includes the above two (--compiler and --build-type). +# +# Optional arguments include the version of compiler and additional +# build modifiers such as link-time-optimizations (--modifier lto), +# feedback-directed-optimizations (--modifier fdo), and taking advantage +# of the building-machine's full instructions sets (--modifier native). +# All modifiers are available simulatenously. +# +# Usage examples: +# $ ./build.sh # asks for a compiler +# $ ./build.sh --compiler gcc # asks for a build-type +# $ ./build.sh --compiler clang --build-type debug # builds! +# $ ./build.sh -c gcc -t release -m lto -m native # builds! +# +# This script makes use of the automator package, see automator/main.sh. +# set -euo pipefail -readonly INVOCATION="${0}" -readonly IFS=$'\n\t' - -function usage() { - if [ -n "${1}" ]; then - errcho "${1}" - fi - local script - script=$(basename "${INVOCATION}") - echo "Usage: ${script} [-b 32|64] [-c gcc|clang] [-f linux|macos|msys2] [-d] [-l]" - echo " [-p /custom/bin] [-u #] [-r fast|small|debug] [-s /your/src] [-t #]" - echo "" - echo " FLAG Description Default" - echo " ----------------------- ----------------------------------------------------- -------" - echo " -b, --bit-depth Build a 64 or 32 bit binary [$(print_var "${BITS}")]" - echo " -c, --compiler Choose either gcc or clang [$(print_var "${COMPILER}")]" - echo " -f, --force-system Force the system to be linux, macos, or msys2 [$(print_var "${SYSTEM}")]" - echo " -d, --fdo Use Feedback-Directed Optimization (FDO) data [$(print_var "${FDO}")]" - echo " -l, --lto Perform Link-Time-Optimizations (LTO) [$(print_var "${LTO}")]" - echo " -p, --bin-path Prepend PATH with the one provided to find executables [$(print_var "${BIN_PATH}")]" - echo " -u, --compiler-version Customize the compiler postfix (ie: 9 -> gcc-9) [$(print_var "${COMPILER_VERSION}")]" - echo " -r, --release Build a fast, small, or debug release [$(print_var "${RELEASE}")]" - echo " -s, --src-path Enter a different source directory before building [$(print_var "${SRC_PATH}")]" - echo " -t, --threads Override the number of threads with which to compile [$(print_var "${THREADS}")]" - echo " -v, --version Print the version of this script [$(print_var "${SCRIPT_VERSION}")]" - echo " -x, --clean Clean old objects prior to building [$(print_var "${CLEAN}")]" - echo " -h, --help Print this usage text" - echo "" - echo "Example: ${script} -b 32 --compiler clang -u 8 --bin-path /mingw64/bin -r small --lto" - echo "" - echo "Note: the last value will take precendent if duplicate flags are provided." - exit 1 -} function parse_args() { - set_defaults + # Gather the parameters that define this build + postfix="" + modifiers=("") while [[ "${#}" -gt 0 ]]; do case ${1} in - -b|--bit-depth) BITS="${2}"; shift;shift;; - -c|--compiler) COMPILER="${2}"; shift;shift;; - -d|--fdo) FDO="true"; shift;; - -f|--force-system) SYSTEM="${2}"; shift;shift;; - -l|--lto) LTO="true"; shift;; - -p|--bin-path) BIN_PATH="${2}"; shift;shift;; - -u|--compiler-version) COMPILER_VERSION="${2}";shift;shift;; - -r|--release) RELEASE="${2}"; shift;shift;; - -s|--src-path) SRC_PATH="${2}"; shift;shift;; - -t|--threads) THREADS="${2}"; shift;shift;; - -v|--version) print_version; shift;; - -x|--clean) CLEAN="true"; shift;; - -h|--help) usage "Show usage"; shift;; - *) usage "Unknown parameter: ${1}"; shift;shift;; + -c|--compiler) compiler="${2}"; shift 2;; + -v|--version-postfix) postfix="-${2}"; shift 2;; + -t|--build-type) selected_type="${2}"; shift 2;; + -m|--modifier) modifiers+=("${2}"); shift 2;; + -p|--bin-path) PATH="${2}:${PATH}"; shift 2;; + *) >&2 echo "Unknown parameter: ${1}"; exit 1; shift 2;; esac; done + + # Import our settings and report missing values + machine="$(uname -m | sed 's/-.*//')"; import machine "${machine}" + os="$(uname -s | sed 's/-.*//')"; import os "${os}" + import compiler "${compiler:-}" + import "${compiler:-}" "${os}_${machine}" + if [[ -z "${selected_type:-}" ]]; then arg_error "--build-type" "${TYPES[*]}"; fi + + # Create a pretty modifier string that we can add to our build-type + printf -v mod_string '+%s' "${modifiers[@]:1}" + if [[ "${mod_string}" == "+" ]]; then mod_string=""; fi + + # Print a summary of our build configuration + underline "Compiling a ${selected_type}${mod_string} build using "` + `"${compiler}${postfix} on ${os}-${machine}" "=" + + # Ensure our selected_type is lower-case before proceeding + selected_type=$(lower "${selected_type}") } -function set_defaults() { - # variables that are directly set via user arguments - BITS="64" - CLEAN="false" - COMPILER="gcc" - COMPILER_VERSION="unset" - FDO="false" - LTO="false" - BIN_PATH="unset" - RELEASE="fast" - SRC_PATH="unset" - SYSTEM="auto" - THREADS="auto" - - # derived variables with initial values - EXECUTABLE="unset" - VERSION_POSTFIX="" - MACHINE="unset" - CONFIGURE_OPTIONS=("--enable-core-inline") - CFLAGS_ARRAY=("-Wall" "-pipe") - LDFLAGS_ARRAY=("") - LIBS_ARRAY=("") - CALL_CACHE=("") - - # read-only strings - readonly SCRIPT_VERSION="1.0" - readonly REPO_URL="https://github.com/dreamer/dosbox-staging" - - # environment variables passed onto the build - export CC="CC_is_not_set" - export CXX="CXX_is_not_set" - export LD="LD_is_not_set" - export AR="AR_is_not_set" - export RANLIB="RANLIB_is_not_set" - export CFLAGS="" - export CXXFLAGS="" - export LDFLAGS="" - export LIBS="" - export GCC_COLORS="error=01;31:warning=01;35:note=01;36:range1=32:range2=34:locus=01:\ -quote=01:fixit-insert=32:fixit-delete=31:diff-filename=01:\ -diff-hunk=32:diff-delete=31:diff-insert=32:type-diff=01;32" -} - -function errcho() { - local CLEAR='\033[0m' - local RED='\033[0;91m' - >&2 echo "" - >&2 echo -e " ${RED}👉 ${*}${CLEAR}" "\\n" -} -function bug() { - local CLEAR='\033[0m' - local YELLOW='\033[0;33m' - >&2 echo -e " ${YELLOW}Please report the following at ${REPO_URL}${CLEAR}" - errcho "${@}" - exit 1 -} - -function error() { - errcho "${@}" - exit 1 -} - -function exists() { - command -v "${1}" &> /dev/null -} - -function print_var() { - if [[ -z "${1}" ]]; then - echo "unset" - else - echo "${1}" - fi -} - -## -# Uses -# ---- -# Alows function to indicate which other functions they depend on. -# For example: "uses system" indicates a function needs the system -# to be defined prior to running. -# This uses function acts like a call cache, ensuring each used function -# is only actually called once. This allows all functions to thoroughly -# employ the 'uses' mechanism without the performance-hit of repeatedly -# executing the same function. Likely, functions that are 'used' are -# atomic in that they will only be called once per script invocation. -# -function uses() { - # assert - if [[ "${#}" != 1 ]]; then - bug "The 'uses' function was called without an argument" - fi - - # only operate on functions in our call-scope, otherwise fail hard - func="${1}" - if [[ "$(type -t "${func}")" != "function" ]]; then - bug "The 'uses' function was passed ${func}, which isn't a function" - fi - - # Check the call cache to see if the function has already been called - local found_in_previous="false" - for previous_func in "${CALL_CACHE[@]}"; do - if [[ "${previous_func}" == "${func}" ]]; then - found_in_previous="true" - break - fi - done - - # if it hasn't been called then record it and run it - if [[ "${found_in_previous}" == "false" ]]; then - CALL_CACHE+=("${func}") - "${func}" - fi -} - - -function print_version() { - echo "${SCRIPT_VERSION}" - exit 0 -} - -function system() { - if [[ "${MACHINE}" == "unset" ]]; then MACHINE="$(uname -m)"; fi - if [[ "${SYSTEM}" == "auto" ]]; then SYSTEM="$(uname -s)"; fi - case "$SYSTEM" in - Darwin|macos) SYSTEM="macos" ;; - MSYS*|msys2) SYSTEM="msys2" ;; - Linux|linux) SYSTEM="linux" ;; - *) error "Your system, $SYSTEM, is not currently supported" ;; - esac -} - -function bits() { - if [[ "${BITS}" != 64 && "${BITS}" != 32 ]]; then - usage "A bit-depth of ${BITS} is not allowed; choose 64 or 32" - fi -} - -function compiler_type() { - if [[ "${COMPILER}" != "gcc" && "${COMPILER}" != "clang" ]]; then - usage "The choice of compiler (${COMPILER}) is not valid; choose gcc or clang" - fi -} - -function tools_and_flags() { - uses compiler_type - uses compiler_version - uses system - - # GCC universal - if [[ "${COMPILER}" == "gcc" ]]; then - CC="gcc${VERSION_POSTFIX}" - LD="gcc${VERSION_POSTFIX}" - CXX="g++${VERSION_POSTFIX}" - CFLAGS_ARRAY+=("-fstack-protector" "-fdiagnostics-color=always") - - # Prioritize versioned lib-tools over generics - AR="gcc-ar${VERSION_POSTFIX}" - RANLIB="gcc-ranlib${VERSION_POSTFIX}" - if ! exists "${AR}"; then AR="ar"; fi - if ! exists "${RANLIB}"; then RANLIB="ranlib"; fi - - # CLANG universal - elif [[ "${COMPILER}" == "clang" ]]; then - CC="clang${VERSION_POSTFIX}" - CXX="clang++${VERSION_POSTFIX}" - CFLAGS_ARRAY+=("-fcolor-diagnostics") - - # CLANG on Linux and MSYS2 - if [[ "${SYSTEM}" == "linux" || "${SYSTEM}" == "msys2" ]]; then - LD="llvm-link${VERSION_POSTFIX}" - AR="llvm-ar${VERSION_POSTFIX}" - RANLIB="llvm-ranlib${VERSION_POSTFIX}" - - # CLANG on MacOS - elif [[ "${SYSTEM}" == "macos" ]]; then LD="ld"; fi - fi - - # macOS universal - if [[ "${SYSTEM}" == "macos" ]]; then - AR="ar" - RANLIB="ranlib" - fi -} - -function src_path() { - if [[ "${SRC_PATH}" == "unset" ]]; then - SRC_PATH="$(cd "$(dirname "${INVOCATION}")" && cd .. && pwd -P)" - elif [[ ! -d "${SRC_PATH}" ]]; then - usage "The requested source directory (${SRC_PATH}) does not exist, is not a directory, or is not accessible" - fi - cd "${SRC_PATH}" -} - -function bin_path() { - uses system - uses compiler_type - - # By default, if we're on macOS and using GCC then always include /usr/local/bin, because - # that's where brew installs all the binaries. If the user adds their own --bin-path, - # that will be prefixed ahead of /usr/local/bin and take precedent. - if [[ "${SYSTEM}" == "macos" && "${COMPILER}" == "gcc" ]]; then - PATH="/usr/local/bin:${PATH}" - fi - - if [[ "${BIN_PATH}" != "unset" ]]; then - if [[ ! -d "${BIN_PATH}" ]]; then - usage "The requested PATH (${BIN_PATH}) does not exist, is not a directory, or is not accessible" - else - PATH="${BIN_PATH}:${PATH}" - fi - fi -} - -function check_build_tools() { - for tool in "${CC}" "${CXX}" "${LD}" "${AR}" "${RANLIB}"; do - if ! exists "${tool}"; then - error "${tool} was not found in your PATH or is not executable. If it's in a custom path, use --bin-path" - fi - done -} - -function compiler_version() { - if [[ "${COMPILER_VERSION}" != "unset" ]]; then - VERSION_POSTFIX="-${COMPILER_VERSION}" - fi -} - -function release_flags() { - uses compiler_type - - if [[ "${RELEASE}" == "fast" ]]; then CFLAGS_ARRAY+=("-Ofast") - elif [[ "${RELEASE}" == "small" ]]; then - CFLAGS_ARRAY+=("-Os") - if [[ "${COMPILER}" == "gcc" ]]; then - CFLAGS_ARRAY+=("-ffunction-sections" "-fdata-sections") - - # ld on MacOS doesn't understand --as-needed, so exclude it - uses system - if [[ "${SYSTEM}" != "macos" ]]; then LDFLAGS_ARRAY+=("-Wl,--as-needed"); fi - fi - elif [[ "${RELEASE}" == "debug" ]]; then CFLAGS_ARRAY+=("-g" "-O1") - else usage "The release type of ${RELEASE} is not allowed. Choose fast, small, or debug" - fi -} - -function threads() { - if [[ "${THREADS}" == "auto" ]]; then - if exists nproc; then THREADS="$(nproc)" - else THREADS="$(sysctl -n hw.physicalcpu || echo 4)"; fi - fi - # make presents a descriptive error message in the scenario where the user overrides - # THREADS with an illegal value: the '-j' option requires a positive integer argument. -} - -function fdo_flags() { - if [[ "${FDO}" != "true" ]]; then - return - fi - - uses compiler_type - uses src_path - local fdo_file="${SRC_PATH}/scripts/profile-data/${COMPILER}.profile" - if [[ ! -f "${fdo_file}" ]]; then - error "The Feedback-Directed Optimization file provided (${fdo_file}) does not exist or could not be accessed" - fi - - if [[ "${COMPILER}" == "gcc" ]]; then - # Don't let GCC 6.x and under use both FDO and LTO - uses compiler_version - if [[ ( "${COMPILER_VERSION}" == "unset" - && "$(2>&1 gcc -v | grep -Po '(?<=version )[^.]+')" -lt "7" - || "${COMPILER_VERSION}" -lt "7" ) - && "${LTO}" == "true" ]]; then - error "GCC versions 6 and under cannot handle FDO and LTO simultaneously; please change one or more these." - fi - CFLAGS_ARRAY+=("-fauto-profile=${fdo_file}") - - elif [[ "${COMPILER}" == "clang" ]]; then - CFLAGS_ARRAY+=("-fprofile-sample-use=${fdo_file}") - fi -} - -function lto_flags() { - if [[ "${LTO}" != "true" ]]; then - return - fi - - uses system - # Only allow LTO on Linux and MacOS; it currently fails under Windows - if [[ "${SYSTEM}" == "msys2" ]]; then - usage "LTO currently does not link or build on Windows with GCC or Clang" - fi - - uses compiler_type - if [[ "${COMPILER}" == "gcc" ]]; then - CFLAGS_ARRAY+=("-flto") - - # The linker performs the code optimizations across all objects, and therefore - # needs the same flags as the compiler would normally get - LDFLAGS_ARRAY+=("${CFLAGS_ARRAY[@]}") - - # GCC on MacOS fails to parse the thread flag, so we only provide it under Linux - # (error: unsupported argument 4 to option flto=) - if [[ "${SYSTEM}" == "linux" ]]; then - uses threads - LDFLAGS_ARRAY+=("-flto=$THREADS") - fi - - elif [[ "${COMPILER}" == "clang" ]]; then - CFLAGS_ARRAY+=("-flto=thin") - - # Clang LTO on Linux is incompatible with -Os so replace them with -O2 - # (ld: error: Optimization level must be between 0 and 3) - if [[ "${SYSTEM}" == "linux" ]]; then - CFLAGS_ARRAY=("${CFLAGS_ARRAY[@]/-Os/-O2}") - fi # clang-linux-lto-small exclusion - fi # gcc & clang -} - -function configure_options() { - uses system - if [[ "${MACHINE}" != *"86"* ]]; then - CONFIGURE_OPTIONS+=("--disable-dynamic-x86" "--disable-fpu-x86" "--disable-fpu-x64") - fi -} - -function do_autogen() { - uses src_path - if [[ ! -f autogen.sh ]]; then - error "autogen.sh doesn't exist in our current directory $PWD. If your DOSBox source is somewhere else set it with --src-path" - fi - - # Only autogen if needed .. - if [[ ! -f configure ]]; then - uses bin_path - ./autogen.sh - fi -} - -function do_configure() { - uses bin_path - uses src_path - uses tools_and_flags - uses release_flags - uses fdo_flags - uses lto_flags - uses check_build_tools - uses configure_options - - # Convert our arrays into space-delimited strings - LIBS=$( printf "%s " "${LIBS_ARRAY[@]}") - CFLAGS=$( printf "%s " "${CFLAGS_ARRAY[@]}") - CXXFLAGS=$(printf "%s " "${CFLAGS_ARRAY[@]}") - LDFLAGS=$( printf "%s " "${LDFLAGS_ARRAY[@]}") - - local lto_string="" - local fdo_string="" - if [[ "${LTO}" == "true" ]]; then lto_string="-LTO"; fi - if [[ "${FDO}" == "true" ]]; then fdo_string="-FDO"; fi - - echo "" - echo "Launching with:" - echo "" - echo " CC = ${CC}" - echo " CXX = ${CXX}" - echo " LD = ${LD}" - echo " AR = ${AR}" - echo " RANLIB = ${RANLIB}" - echo " CFLAGS = ${CFLAGS}" - echo " CXXFLAGS = ${CXXFLAGS}" - echo " LIBS = ${LIBS}" - echo " LDFLAGS = ${LDFLAGS}" - echo " THREADS = ${THREADS}" - echo "" - echo "Build type: ${SYSTEM}-${MACHINE}-${BITS}bit-${COMPILER}-${RELEASE}${lto_string}${fdo_string}" - echo "" - "${CC}" --version - sleep 5 - - if [[ ! -f configure ]]; then - error "configure script doesn't exist in $PWD. If the source is somewhere else, set it with --src-path" - - elif ! ./configure "${CONFIGURE_OPTIONS[@]}"; then - >&2 cat "config.log" - error "configure failed, see config.log output above" - fi -} - -function executable() { - uses src_path - EXECUTABLE="src/" - if [[ "${SYSTEM}" == "msys2" ]]; then EXECUTABLE+="dosbox.exe" - else EXECUTABLE+="dosbox" - fi - - if [[ ! -f "${EXECUTABLE}" ]]; then - error "${EXECUTABLE} does not exist or hasn't been created yet" - fi -} - -function build() { - uses src_path - uses bin_path - uses threads - - if [[ "${CLEAN}" == "true" && -f "Makefile" ]]; then - make clean 2>&1 | tee -a build.log - fi - do_autogen - do_configure - make -j "${THREADS}" 2>&1 | tee -a build.log -} - -function strip_binary() { - if [[ "${RELEASE}" == "debug" ]]; then - echo "[skipping strip] Debug symbols will be left in the binary because it's a debug release" - else - uses bin_path - uses executable - strip "${EXECUTABLE}" - fi -} - -function show_binary() { - uses bin_path - uses system - uses executable - - if [[ "$SYSTEM" == "macos" ]]; then otool -L "${EXECUTABLE}" - else ldd "${EXECUTABLE}"; fi - ls -1lh "${EXECUTABLE}" -} - -function main() { - parse_args "$@" - build - strip_binary - show_binary -} - -main "$@" +# shellcheck source=scripts/automator/main.sh +source "$(dirname "$0")/automator/main.sh" diff --git a/scripts/list-build-dependencies.sh b/scripts/list-build-dependencies.sh index 1fa311d4..a929077d 100755 --- a/scripts/list-build-dependencies.sh +++ b/scripts/list-build-dependencies.sh @@ -1,196 +1,55 @@ #!/bin/bash -## -# Copyright (c) 2019 Kevin R. Croft -# SPDX-License-Identifier: GPL-2.0-or-later -# -# This script lists development packages and DOSBox dependencies needed to build DOSBox. -# This package names provided are tailor based on the provided package manager, -# choice of compiler, and optionally its bit-depth. -# -# See the usage arguments below for details or run it with the -h or --help. -# -# In general, this script adheres to Google's shell scripting style guide -# (https://google.github.io/styleguide/shell.xml), however some deviations (such as -# tab indents instead of two-spaces) are used to fit DOSBox's in-practice coding style. -# +# Copyright (c) 2019 Kevin R Croft +# SPDX-License-Identifier: GPL-2.0-or-later +# This script prints package dependencies for a given package manmager, +# compiler (gcc or clang), and bit-depth (32 or 64). +# +# If run without arguments, the script asks for required arguments one +# by one, which includes the package manager (--manager) and --compiler. +# +# Optional arguments include the bit-depth, defaults to 64-bit; +# and the compiler version, which defaults to the package manager's +# default version. +# +# Usage examples: +# $ ./list-build-dependencies.sh # asks for --manager +# $ ./list-build-dependencies.sh --manager apt # asks for --compiler +# $ ./list-build-dependencies.sh --manager apt --compiler gcc -v 9 +# $ ./list-build-dependencies.sh --manager zypper --compiler clang +# $ ./list-build-dependencies.sh --manager msy2 --compiler gcc -b 32 +# +# This script makes use of the automator package, see automator/main.sh. +# set -euo pipefail -IFS=$'\n\t' -function usage() { - if [ -n "${1}" ]; then - errcho "${1}" - fi - local script - script=$(basename "${0}") - echo "Usage: ${script} -p apt|xcode|brew|macports|msys2|vcpkg [-b 32|64] [-c gcc|clang] [-o FILE] [-u #]" - echo "" - echo " FLAG Description Default" - echo " ----------------------- --------------------------------------------------- -------" - echo " -b, --bit-depth Choose either a 64 or 32 bit compiler [$(print_var "${BITS}")]" - echo " -c, --compiler Choose either gcc or clang [$(print_var "${COMPILER}")]" - echo " -u, --compiler-version <#> Customize the compiler version (if available) [$(print_var "${COMPILER_VERSION}")]" - echo " -v, --version Print the version of this script [$(print_var "${SCRIPT_VERSION}")]" - echo " -h, --help Print this usage text" - echo " -p, --package-manager Choose one of the following:" - echo " - apt (Linux: Debian, Ubuntu, Raspbian)" - echo " - dnf (Linux: Fedora, RedHat, CentOS)" - echo " - pacman (Linux: Arch, Manjaro)" - echo " - zypper (Linux: SuSE, OpenSUSE)" - echo " - xcode (OS X: Apple-supported)" - echo " - brew (OS X: Homebrew community-supported)" - echo " - macports (OS X: MacPorts community-supported)" - echo " - msys2 (Windows: Cygwin-based, community-support)" - echo " - vcpkg (Windows: Visual Studio builds)" - echo " ----------------------- --------------------------------------------------- -------" - echo "" - echo "Example: ${script} --package-manager apt --compiler clang --compiler-version 8" - echo "" - echo "Note: the last value will take precendent if duplicate flags are provided." - exit 1 -} - -function defaults() { - BITS="64" - COMPILER="gcc" - COMPILER_VERSION="" - PACKAGE_MANAGER="unset" - readonly SCRIPT_VERSION="0.2" -} - -# parse params function parse_args() { - defaults + # Gather the parameters that define this build + bits="64" + modifiers=("") + # shellcheck disable=SC2034 while [[ "${#}" -gt 0 ]]; do case ${1} in - -b|--bit-depth) BITS="${2}"; shift;shift;; - -c|--compiler) COMPILER="${2}"; shift;shift;; - -p|--package-manager) PACKAGE_MANAGER="${2}"; shift;shift;; - -u|--compiler-version) COMPILER_VERSION="${2}"; shift;shift;; - -v|--version) print_version; shift;; - -h|--help) usage "Show usage"; shift;; - *) usage "Unknown parameter: ${1}"; shift;shift;; + -m|--manager) manager="${2}"; shift 2;; + -c|--compiler) selected_type=$(lower "${2}"); shift 2;; + -b|--bit-depth) bits="${2}"; shift 2;; + -v|--compiler-version) version="${2}"; shift 2;; + -a|--addon) modifiers+=("${2}"); shift 2;; + *) >&2 echo "Unknown parameter: ${1}"; exit 1; shift 2;; esac; done - # Check mandatory arguments - if [[ "${PACKAGE_MANAGER}" == "unset" ]]; then - usage "A choice of package manager must be provided; use '-p ' or '--package-manager '" - fi + # Import our settings and report missing values + import manager "${manager:-}" + if [[ -z "${selected_type:-}" ]]; then arg_error "--compiler" "${TYPES[*]}"; fi + + # If a version was provided then construct a postfix string given the manager's + # potential unique delimeter (ie: gcc@9 for brew, gcc-9 for apt). + # shellcheck disable=SC2034,SC2154 + postfix=$([[ -n "${version:-}" ]] && echo "${delim}${version}" || echo "") + import "${selected_type:-}" "${manager:-}" } +# shellcheck disable=SC2034 +data_dir="packages" -function errcho() { - local CLEAR='\033[0m' - local RED='\033[0;91m' - >&2 echo "" - >&2 echo -e " ${RED}👉 ${*}${CLEAR}" "\\n" -} - -function print_var() { - if [[ -z "${1}" ]]; then - echo "unset" - else - echo "${1}" - fi -} - -function list_packages() { - VERSION_DELIM="" - case "$1" in - - apt) - # Apt separates GCC into the gcc and g++ pacakges, the latter which depends on the prior. - # Therefore, we provide g++ in-place of gcc. - VERSION_DELIM="-" - if [[ "${COMPILER}" == "gcc" ]]; then - COMPILER="g++" - fi - PACKAGES=(libtool build-essential autoconf-archive libsdl1.2-dev libsdl-net1.2-dev libopusfile-dev) - ;; - - dnf) - VERSION_DELIM="-" - PACKAGES=(libtool autoconf-archive SDL SDL_net-devel opusfile-devel) - ;; - - pacman) - # Arch offers 32-bit versions of SDL (but not others) - PACKAGES=(libtool autoconf-archive sdl_net opusfile) - if [[ "${BITS}" == 32 ]]; then - PACKAGES+=(lib32-sdl) - else - PACKAGES+=(sdl) - fi - ;; - - zypper) - # OpenSUSE offers 32-bit versions of SDL and SDL_net (but not others) - PACKAGES=(devel_basis libtool autoconf-archive opusfile) - if [[ "${BITS}" == 32 ]]; then - PACKAGES+=(libSDL-devel-32bit libSDL_net-devel-32bit) - else - PACKAGES+=(SDL SDL_net) - fi - ;; - - xcode) - # If the user doesn't want to use Homebrew or MacPorts, then they are limited to - # Apple's Clang plus the provided command line development tools provided by Xcode. - COMPILER="" - echo "Execute the following:" - echo " xcode-select --install # to install command line development tools" - echo " sudo xcodebuild -license # to accept Apple's license agreement" - echo "" - echo "Now download, build, and install the following manually to avoid using Homebrew or MacPorts:" - echo " - coreutils autogen autoconf automake pkg-config libpng sdl sdl_net opusfile" - ;; - - brew) - # If the user wants Clang, we knock it out because it's provided provided out-of-the-box - VERSION_DELIM="@" - if [[ "${COMPILER}" == "clang" ]]; then - COMPILER="" - fi - PACKAGES=(coreutils autogen autoconf autoconf-archive automake pkg-config libpng sdl sdl_net opusfile) - ;; - - macports) - PACKAGES=(coreutils autogen automake autoconf autoconf-archive pkgconfig libpng libsdl libsdl_net opusfile) - ;; - - msys2) - # MSYS2 only supports the current latest releases of Clang and GCC, so we disable version customization - COMPILER_VERSION="" - local pkg_type - pkg_type="x86_64" - if [[ "${BITS}" == 32 ]]; then - pkg_type="i686" - fi - PACKAGES=(autogen autoconf autoconf-archive base-devel automake-wrapper) - for pkg in pkg-config libtool libpng zlib SDL SDL_net opusfile; do - PACKAGES+=("mingw-w64-${pkg_type}-${pkg}") - done - COMPILER="mingw-w64-${pkg_type}-${COMPILER}" - ;; - - vcpkg) - # VCPKG doesn't provide Clang or GCC, so we knock out the compiler and just give packages - COMPILER="" - PACKAGES=(libpng sdl1 sdl1-net opusfile) - ;; - *) - usage "Unknown package manager ${1}" - ;; - esac - - if [[ -n "${COMPILER_VERSION}" && -n "${COMPILER}" ]]; then - COMPILER+="${VERSION_DELIM}${COMPILER_VERSION}" - fi - echo "${COMPILER} ${PACKAGES[*]}" -} - -function main() { - parse_args "$@" - list_packages "${PACKAGE_MANAGER}" -} - -main "$@" +# shellcheck source=scripts/automator/main.sh +source "$(dirname "$0")/automator/main.sh"