diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 6009d925..00000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,142 +0,0 @@ -name: "Build" - -on: [push] - -jobs: - - build_gcc9: - name: "GCC 9 (ubuntu-18.04)" - runs-on: ubuntu-18.04 - steps: - - uses: actions/checkout@v1 - - name: "Log environment" - run: ./scripts/log-env.sh - - run: sudo apt update - - name: "Install packages" - run: sudo apt-get install g++-9 libsdl1.2-dev libsdl-net1.2-dev libsdl-sound1.2-dev - - name: "Build" - env: - CC: gcc-9 - CXX: g++-9 - run: | - # build steps - set -x - $CXX --version - ./autogen.sh - ./configure CXXFLAGS="-Wall -fdiagnostics-color=always" - make -j "$(nproc)" |& tee build.log - - name: "Summarize warnings" - run: python3 ./scripts/count-warnings.py build.log - - build_gcc_ubuntu: - name: "GCC" - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-18.04, ubuntu-16.04] - steps: - - uses: actions/checkout@v1 - - name: "Log environment" - run: ./scripts/log-env.sh - - run: sudo apt update - - name: "Install packages" - run: sudo apt-get install libsdl1.2-dev libsdl-net1.2-dev libsdl-sound1.2-dev - - name: "Build" - run: | - # build steps - set -x - g++ --version - ./autogen.sh - ./configure CXXFLAGS="-Wall -fdiagnostics-color=always" - make -j "$(nproc)" |& tee build.log - - name: "Summarize warnings" - run: python3 ./scripts/count-warnings.py build.log - - build_clang: - name: "Clang (ubuntu-18.04)" - runs-on: ubuntu-18.04 - steps: - - uses: actions/checkout@v1 - - name: "Log environment" - run: ./scripts/log-env.sh - - run: sudo apt update - - name: "Install packages" - run: sudo apt-get install libsdl1.2-dev libsdl-net1.2-dev libsdl-sound1.2-dev - - name: "Build" - env: - CC: clang - CXX: clang++ - run: | - # build steps - set -x - $CXX --version - ./autogen.sh - ./configure CXXFLAGS="-Wall" - make -j "$(nproc)" |& tee build.log - - name: "Summarize warnings" - run: python3 ./scripts/count-warnings.py build.log - - build_macos: - name: "Clang (macOS-10.14)" - runs-on: macOS-10.14 - steps: - - uses: actions/checkout@v1 - - name: "Log environment" - run: ./scripts/log-env.sh - - run: brew update - - name: "Install packages" - run: brew install automake libpng sdl sdl_net sdl_sound - - name: "Build" - run: | - # build steps - set -x - clang++ --version - ./autogen.sh - ./configure CXXFLAGS="-Wall" - make -j "$(sysctl -n hw.physicalcpu)" 2>&1 | tee build.log - - name: "Summarize warnings" - run: python3 ./scripts/count-warnings.py build.log - - build_msvc_debug: - name: "MSVC 14 Debug (win-2019)" - runs-on: windows-2019 - steps: - - uses: actions/checkout@v1 - - name: "Log environment" - shell: pwsh - run: .\scripts\log-env.ps1 - - name: "Install packages" - shell: pwsh - run: | - vcpkg install libpng sdl1 sdl1-net - vcpkg integrate install - - name: "Build" - shell: pwsh - env: - PATH: '${env:PATH};C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\amd64' - run: | - # build steps - cd visualc_net - MSBuild -m dosbox.sln -p:Configuration=Debug - - build_msvc_release: - name: "MSVC 14 Release (win-2019)" - runs-on: windows-2019 - steps: - - uses: actions/checkout@v1 - - name: "Log environment" - shell: pwsh - run: .\scripts\log-env.ps1 - - name: "Install packages" - shell: pwsh - run: | - vcpkg install libpng sdl1 sdl1-net - vcpkg integrate install - - name: "Build" - shell: pwsh - env: - PATH: '${env:PATH};C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\amd64' - run: | - # build steps - cd visualc_net - MSBuild -m dosbox.sln -p:Configuration=Release diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml new file mode 100644 index 00000000..29bec371 --- /dev/null +++ b/.github/workflows/linux.yml @@ -0,0 +1,48 @@ +name: "Linux builds" +on: push +jobs: + + build_ubuntu_gcc9: + name: "GCC 9 (ubuntu-18.04)" + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v1 + - name: "Log environment" + run: ./scripts/log-env.sh + - name: Install dependencies + run: sudo apt-get update && sudo apt install -y $(./scripts/list-build-dependencies.sh --compiler-version 9) + - name: Build + run: ./scripts/build.sh --compiler-version 9 --lto + - name: "Summarize warnings" + run: ./scripts/count-warnings.py build.log + + build_ubuntu_gcc_defaults: + name: "GCC" + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-18.04, ubuntu-16.04] + steps: + - uses: actions/checkout@v1 + - name: "Log environment" + run: ./scripts/log-env.sh + - name: Install dependencies + run: sudo apt-get update && sudo apt install -y $(./scripts/list-build-dependencies.sh) + - name: Build + run: ./scripts/build.sh + - name: "Summarize warnings" + run: ./scripts/count-warnings.py build.log + + build_ubuntu_clang_8: + name: "Clang 8 (ubuntu-18.04)" + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v1 + - name: "Log environment" + run: ./scripts/log-env.sh + - name: Install dependencies + run: sudo apt-get update && sudo apt install -y $(./scripts/list-build-dependencies.sh --compiler clang --compiler-version 8) + - name: Build + run: ./scripts/build.sh --compiler clang --compiler-version 8 --lto + - name: "Summarize warnings" + run: ./scripts/count-warnings.py build.log diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml new file mode 100644 index 00000000..f4ce4cfb --- /dev/null +++ b/.github/workflows/macos.yml @@ -0,0 +1,16 @@ +name: "macOS builds" +on: push +jobs: + build_macos_clang: + name: "Clang (macOS-10.14)" + runs-on: macOS-10.14 + steps: + - uses: actions/checkout@v1 + - name: "Log environment" + run: ./scripts/log-env.sh + - name: Install dependencies + run: brew install $(./scripts/list-build-dependencies.sh --compiler clang) + - name: Build + run: ./scripts/build.sh --compiler clang --lto + - name: "Summarize warnings" + run: python3 ./scripts/count-warnings.py build.log diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml new file mode 100644 index 00000000..22b43802 --- /dev/null +++ b/.github/workflows/windows.yml @@ -0,0 +1,142 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +name: "Windows builds" +on: push +jobs: + + build_msvc_debug: + name: "MSVC 14 Debug (win-2019)" + runs-on: windows-2019 + steps: + - uses: actions/checkout@v1 + - name: "Log environment" + shell: pwsh + run: .\scripts\log-env.ps1 + - name: "Install packages" + shell: pwsh + run: | + vcpkg install libpng sdl1 sdl1-net + vcpkg integrate install + - name: "Build" + shell: pwsh + env: + PATH: '${env:PATH};C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\amd64' + run: | + # build steps + cd visualc_net + MSBuild -m dosbox.sln -p:Configuration=Debug + + + build_msvc_release: + name: "MSVC 14 Release (win-2019)" + runs-on: windows-2019 + steps: + - uses: actions/checkout@v1 + - name: "Log environment" + shell: pwsh + run: .\scripts\log-env.ps1 + - name: "Install packages" + shell: pwsh + run: | + vcpkg install libpng sdl1 sdl1-net + vcpkg integrate install + - name: "Build" + shell: pwsh + env: + PATH: '${env:PATH};C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\amd64' + run: | + # build steps + cd visualc_net + MSBuild -m dosbox.sln -p:Configuration=Release + + + build_msys2_gcc_64: + name: MSYS2 GCC-9 x86_64 + runs-on: windows-2019 + env: + CHERE_INVOKING: yes + steps: + - uses: actions/checkout@v1 + - name: Install msys2 environment + run: choco install msys2 --no-progress + + - name: "Log environment" + run: C:\tools\msys64\usr\bin\bash -lc "./scripts/log-env.sh" + + - name: Install dependencies + run: C:\tools\msys64\usr\bin\bash -lc "./scripts/list-build-dependencies.sh | xargs pacman -S --noconfirm" + + - name: Build + run: C:\tools\msys64\usr\bin\bash -lc "./scripts/build.sh --bin-path /mingw64/bin" + + - name: Summarize warnings + run: .\scripts\count-warnings.py build.log + + + build_msys2_gcc_32: + name: MSYS2 GCC-9 i686 + runs-on: windows-2019 + env: + CHERE_INVOKING: yes + steps: + - uses: actions/checkout@v1 + - name: Install msys2 environment + run: choco install msys2 --no-progress + + - name: "Log environment" + run: C:\tools\msys64\usr\bin\bash -lc "./scripts/log-env.sh" + + - name: Install dependencies + run: C:\tools\msys64\usr\bin\bash -lc "./scripts/list-build-dependencies.sh --bit-depth 32 | xargs pacman -S --noconfirm" + + - name: Build + run: C:\tools\msys64\usr\bin\bash -lc "./scripts/build.sh --bit-depth 32 --bin-path /mingw32/bin" + + - name: Summarize warnings + run: .\scripts\count-warnings.py build.log + + + build_msys2_clang_64: + name: MSYS2 Clang-8 x86_64 + runs-on: windows-2019 + env: + CHERE_INVOKING: yes + steps: + - uses: actions/checkout@v1 + - name: Install msys2 environment + run: choco install msys2 --no-progress + + - name: "Log environment" + run: C:\tools\msys64\usr\bin\bash -lc "./scripts/log-env.sh" + + - name: Install dependencies + run: C:\tools\msys64\usr\bin\bash -lc "./scripts/list-build-dependencies.sh --compiler clang | xargs pacman -S --noconfirm" + + - name: Build + run: C:\tools\msys64\usr\bin\bash -lc "./scripts/build.sh --compiler clang --bin-path /mingw64/bin" + + - name: Summarize warnings + run: .\scripts\count-warnings.py build.log + + + build_msys2_clang_32: + name: MSYS2 Clang-8 i686 + runs-on: windows-2019 + env: + CHERE_INVOKING: yes + steps: + - uses: actions/checkout@v1 + - name: Install msys2 environment + run: choco install msys2 --no-progress + + - name: "Log environment" + run: C:\tools\msys64\usr\bin\bash -lc "./scripts/log-env.sh" + + - name: Install dependencies + run: C:\tools\msys64\usr\bin\bash -lc "./scripts/list-build-dependencies.sh --compiler clang --bit-depth 32 | xargs pacman -S --noconfirm" + + - name: Build + run: C:\tools\msys64\usr\bin\bash -lc "./scripts/build.sh --compiler clang --bit-depth 32 --bin-path /mingw32/bin" + + - name: Summarize warnings + run: .\scripts\count-warnings.py build.log diff --git a/scripts/build.md b/scripts/build.md new file mode 100644 index 00000000..6749915f --- /dev/null +++ b/scripts/build.md @@ -0,0 +1,110 @@ +# DOSBox build script + +A helper-script that builds DOSBox with varying compilers, release types, and versions +on MacOS, Linux, and Windows. + +A second script, **list-build-dependencies.sh** prints a list of package and library +dependencies used to build and link DOSBox, customized for your hardware, operating system, +and selected compiler and its version. You can use this script's output to install +those necessary packages. + +## Requirements + +- **Windows newer than XP** + - **NTFS-based C:**, because msys2 doesn't work on FAT filesystems + - **Chocolately**, to install msys2 + +- **MacOS** 10.x + - **brew**, as the package manager + +- **Ubuntu** 16.04 or newer + - **apt**, as the package manager + - **sudo**, to permit installation of depedencies (optional) + +- **Build dependencies (all operating systems)** + - Per those listed by the accompanying list-build-dependencies.sh script + +## Windows Installation and Usage + +1. Download and install Chocolatey: https://chocolatey.org/install +1. Open a console and run Cholocatey's command line interface (CLI) to install msys2: + `choco install msys2 git --no-progress` + + ``` + Chocolatey v0.10.15 + Installing the following packages: + msys2 git + By installing you accept licenses for the packages. + + msys2 v20180531.0.0 [Approved] + msys2 package files install completed. Performing other installation steps. + Installing to: C:\tools\msys64 + Extracting 64-bit C:\ProgramData\chocolatey\lib\msys2\tools\msys2-base-x86_64-20180531.tar.xz to C:\tools\msys64... + C:\tools\msys64 + Extracting C:\tools\msys64\msys2-base-x86_64-20180531.tar to C:\tools\msys64... + C:\tools\msys64 + Starting initialization via msys2_shell.cmd + PATH environment variable does not have C:\tools\msys64 in it. Adding... + ``` + +1. Open a new console and clone the DOSBox staging repository: + `git clone https://github.com/dreamer/dosbox-staging.git` + +1. [optional] Install the build tools and package dependencies, if not yet done so: + ``` shell + cd dosbox-staging + SET CWD=%cd% + bash -lc "pacman -S --noconfirm $($CWD/scripts/list-build-dependencies.sh)" + ``` + +1. Launch the build script with default settings: + ``` shell + cd dosbox-staging + SET CWD=%cd% + bash -lc "$CWD/scripts/build.sh --bin-path /mingw64/bin --src-path $CWD" + ``` + +## MacOS Installation and Usage + +1. Download and install brew: https://brew.sh +1. Install git: `brew install git` +1. Clone the repository: `git clone https://github.com/dreamer/dosbox-staging.git` +1. Change directories into the repo: `cd dosbox-staging` +1. [optional] Install the build tools and package dependencies, if not yet done so: + ``` shell + brew update + brew install $(./scripts/list-build-dependencies.sh) + ``` +1. Build DOSBox: `./scripts/build.sh` + +## Linux (Ubuntu/Debian-based) Installation and Usage + +1. Install git: `sudo apt install -y git` +1. Clone the repository: `git clone https://github.com/dreamer/dosbox-staging.git` +1. Change directories into the repo: `cd dosbox-staging` +1. [optional] Install the build tools and package dependencies, if not yet done so: + ``` shell + sudo apt update -y + sudo apt install -y $(./scripts/list-build-dependencies.sh) + ``` +1. Build DOSBox: `./scripts/build.sh` + +## Additional Tips + +The compiler, version, and bit-depth can be selected by passing the following common +options to the **list-build-dependencies.sh** and **build.sh** scripts: +* `--compiler clang`, to use CLang instead of GCC +* `--compiler-version 8`, to use a specific version of compiler (if available in your package manager) +* `--bit-depth 32`, to build a 32-bit binary instead of 64-bit + +After building, your binary will reside inside the src/ directory. + +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. + +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 new file mode 100755 index 00000000..7dc6e4db --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,561 @@ +#!/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. +# + +set -euo pipefail +IFS=$'\n\t' + +function usage() { + if [ -n "${1}" ]; then + errcho "${1}" + fi + local script + script=$(basename "${0}") + 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 Used feedback-Directed Optimization data [$(print_var "${FDO}")]" + echo " -l, --lto Perform link-time-optimization [$(print_var "${LTO}")]" + echo " -p, --bin-path Prepend PATH with the one provided to find executables [$(print_var "${BIN_PATH}")]" + echo " -u, --compiler-version # Use a specific compiler version (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 Use a different source directory to build [$(print_var "${SRC_PATH}")]" + echo " -t, --threads # Override auto-detection of number of logical CPUs [$(print_var "${THREADS}")]" + echo " -v, --version Print the version of this script [$(print_var "${SCRIPT_VERSION}")]" + echo " -x, --clean Clean old object and build file before making [$(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 +} + +# parse params +function parse_args() { + defaults + 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;; + esac; done +} + +function 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="$(dirname "$(dirname "$(realpath -s "$0")")")" + 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="0.9" + 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 +} + +function uses() { + # assert + if [[ "${#}" != 1 ]]; then + bug "The 'uses' function was called without an argument" + fi + + # only handles function calls, so filter everything else + func="${1}" + if [[ "$(type -t "${func}")" != "function" ]]; then + bug "The 'uses' function was passed ${func}, which isn't a function" + fi + + local found_in_previous="false" + # has our function already been called? + for previous_func in "${CALL_CACHE[@]}"; do + if [[ "${previous_func}" == "${func}" ]]; then + found_in_previous="true" + break + fi + done + + # if it hasn't, 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 + if [[ "${SYSTEM}" == "Darwin" \ + || "${SYSTEM}" == "macos" ]]; then + SYSTEM="macos" + + elif [[ "${SYSTEM}" == "MSYS"* \ + || "${SYSTEM}" == "msys2" ]]; then + SYSTEM="msys2" + + elif [[ "${SYSTEM}" == "Linux" \ + || "${SYSTEM}" == "linux" ]]; then + SYSTEM="linux" + + else + error "Your system, ${SYSTEM}, is not currently supported" + fi +} + +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}" + if ! exists "${AR}"; then + AR="ar" + fi + + RANLIB="gcc-ranlib${VERSION_POSTFIX}" + 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 + + # CLANG and MSYS2 + if [[ "${SYSTEM}" == "msys2" ]]; then + CFLAGS_ARRAY+=("-DWIN32") + fi + fi + + # macOS universal + if [[ "${SYSTEM}" == "macos" ]]; then + AR="ar" + RANLIB="ranlib" + + # MSYS universal + elif [[ "${SYSTEM}" == "msys2" ]]; then + LIBS_ARRAY+=("-lwinmm" "-lws2_32") + fi +} + +function src_path() { + if [[ ! -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() { + uses system + + if [[ "${THREADS}" != "auto" ]]; then + if [[ "${THREADS}" -lt 1 || "${THREADS}" -gt 256 ]]; then + usage "The number of threads, ${THREADS}, needs to be between 1 and 256, or don't set it at all" + fi + else + if [[ -n "${NUMBER_OF_PROCESSORS:-}" && "${NUMBER_OF_PROCESSORS}" -gt 0 ]]; then + THREADS="${NUMBER_OF_PROCESSORS}" + elif [[ "${SYSTEM}" == "macos" ]]; then + THREADS="$(sysctl -n hw.physicalcpu || echo 4)" + elif exists nproc; then + THREADS="$(nproc)" + else + THREADS=4 # if auto-detection fails fallback to a reasonable + # number of logical CPUs for 2019 + fi + fi +} + +function fdo_flags() { + if [[ "${FDO}" != "true" ]]; then + return + fi + + uses compiler_type + 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="" + if [[ "${LTO}" == "true" ]]; then + lto_string="-LTO" + fi + + local fdo_string="" + 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 "$@" diff --git a/scripts/count-bugs.py b/scripts/count-bugs.py index 8bf09c35..b579decf 100755 --- a/scripts/count-bugs.py +++ b/scripts/count-bugs.py @@ -25,7 +25,6 @@ from bs4 import BeautifulSoup # MAX_ISSUES = 99 - def summary_values(summary_table): if not summary_table: return diff --git a/scripts/count-warnings.py b/scripts/count-warnings.py index bc76b49c..7c87086b 100755 --- a/scripts/count-warnings.py +++ b/scripts/count-warnings.py @@ -23,7 +23,7 @@ import sys # then script will return with status 1. Simply change this line if you # want to set a different limit. # -MAX_ISSUES = 375 +MAX_ISSUES = 420 # For recognizing warnings in GCC format in stderr: # diff --git a/scripts/list-build-dependencies.sh b/scripts/list-build-dependencies.sh new file mode 100755 index 00000000..cc5fe0d4 --- /dev/null +++ b/scripts/list-build-dependencies.sh @@ -0,0 +1,298 @@ +#!/bin/bash + +## +# Copyright (c) 2019 Kevin R. Croft +# SPDX-License-Identifier: GPL-2.0-or-later +# +# This script lists DOSBox package dependencies as determined by the runtime +# architecture, operating system, and selected compiler type and and its version. +# +# 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. +# + +set -euo pipefail +IFS=$'\n\t' + +function usage() { + if [ -n "${1}" ]; then + errcho "${1}" + fi + local script + script=$(basename "${0}") + echo "Usage: ${script} [-b 32|64] [-c gcc|clang] [-f linux|macos|msys2] [-o FILE] [-u #]" + 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 " -o, --output-file Write the packages to a text file instead of stdout [$(print_var "${OUTPUT_FILE}")]" + echo " -u, --compiler-version # Use a specific compiler version (ie: 9 -> gcc-9) [$(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 "" + echo "Example: ${script} --bit-depth 32 --compiler clang --compiler-version 8" + echo "" + echo "Note: the last value will take precendent if duplicate flags are provided." + exit 1 +} + +# parse params +function parse_args() { + defaults + while [[ "${#}" -gt 0 ]]; do case ${1} in + -b|--bit-depth) BITS="${2}"; shift;shift;; + -c|--compiler) COMPILER="${2}"; shift;shift;; + -f|--force-system) SYSTEM="${2}"; shift;shift;; + -o|--output-file) OUTPUT_FILE="${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;; + esac; done +} + +function defaults() { + # variables that are directly set via user arguments + BITS="64" + COMPILER="gcc" + COMPILER_VERSION="unset" + OUTPUT_FILE="unset" + SYSTEM="auto" + + # derived variables with initial values + VERSION_POSTFIX="" + MACHINE="unset" + CALL_CACHE=("") + + # read-only strings + readonly SCRIPT_VERSION="0.1" + readonly REPO_URL="https://github.com/dreamer/dosbox-staging" +} + +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 print_var() { + if [[ -z "${1}" ]]; then + echo "unset" + else + echo "${1}" + fi +} + +function uses() { + # assert + if [[ "${#}" != 1 ]]; then + bug "The 'uses' function was called without an argument" + fi + + # only handles function calls, so filter everything else + func="${1}" + if [[ "$(type -t "${func}")" != "function" ]]; then + bug "The 'uses' function was passed ${func}, which isn't a function" + fi + + local found_in_previous="false" + # has our function already been called? + for previous_func in "${CALL_CACHE[@]}"; do + if [[ "${previous_func}" == "${func}" ]]; then + found_in_previous="true" + break + fi + done + + # if it hasn't, 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 packages() { + # only proceed if the user wants to install packages + uses system + if [[ "${OUTPUT_FILE}" != "unset" ]]; then + "packages_for_${SYSTEM}" > "${OUTPUT_FILE}" + else + "packages_for_${SYSTEM}" + fi +} + +function packages_for_macos() { + + uses compiler_type + uses compiler_version + local compiler_package="" # for brew, the clang package doesn't exist, so stay empty in this case (?) + if [[ "${COMPILER}" == "gcc" ]]; then + compiler_package="gcc" + if [[ "${COMPILER_VERSION}" != "unset" ]]; then + compiler_package+="@${COMPILER_VERSION}" + fi + fi + + # Typical installation: + # - brew update + # - brew install + + local packages=( + "${compiler_package}" + coreutils + autogen + autoconf + automake + pkg-config + libpng + sdl + sdl_net + opusfile + speexdsp ) + echo "${packages[@]}" +} + +function packages_for_msys2() { + uses bits + local pkg_type="" + if [[ "${BITS}" == 64 ]]; then + pkg_type="x86_64" + else + pkg_type="i686" + fi + + uses compiler_type + uses compiler_version + local compiler_package="${COMPILER}" + compiler_package+="${VERSION_POSTFIX}" + + # Typical installation step: + # pacman -S --noconfirm + local packages=( + autogen + autoconf + base-devel + automake-wrapper + "mingw-w64-${pkg_type}-pkg-config" + "mingw-w64-${pkg_type}-${compiler_package}" + "mingw-w64-${pkg_type}-libtool" + "mingw-w64-${pkg_type}-libpng" + "mingw-w64-${pkg_type}-zlib" + "mingw-w64-${pkg_type}-SDL" + "mingw-w64-${pkg_type}-SDL_net" + "mingw-w64-${pkg_type}-opusfile" + "mingw-w64-${pkg_type}-speexdsp" ) + echo "${packages[@]}" +} + +function packages_for_linux() { + + # TODO: + # Convert this into an associative map between package + # managers and the list of respective package names. We + # should have coverage for the major distribution types, + # such as: + # - RPM-based (RHEL/CentOS, Fedora, and openSUSE) + # - Debian-based (Debian, Ubuntu, and Raspbian) + # - pacman-based (Arch and Manjero) + # + uses compiler_type + local compiler_package="" + if [[ "${COMPILER}" == "gcc" ]]; then + compiler_package="g++" + else + compiler_package="clang" + fi + + uses compiler_version + compiler_package+="${VERSION_POSTFIX}" + + # Typically install step + # sudo apt update -y + # sudo apt install -y + local packages=( + "${compiler_package}" + libtool + build-essential + libsdl1.2-dev + libsdl-net1.2-dev + libopusfile-dev + libspeexdsp-dev ) + echo "${packages[@]}" +} + +function system() { + if [[ "${MACHINE}" == "unset" ]]; then + MACHINE="$(uname -m)" + fi + + if [[ "${SYSTEM}" == "auto" ]]; then + SYSTEM="$(uname -s)" + fi + if [[ "${SYSTEM}" == "Darwin" \ + || "${SYSTEM}" == "macos" ]]; then + SYSTEM="macos" + + elif [[ "${SYSTEM}" == "MSYS"* \ + || "${SYSTEM}" == "msys2" \ + || "${SYSTEM}" == "MINGW"* ]]; then + SYSTEM="msys2" + + elif [[ "${SYSTEM}" == "Linux" \ + || "${SYSTEM}" == "linux" ]]; then + SYSTEM="linux" + + else + error "Your system, ${SYSTEM}, is not currently supported" + fi +} + +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 compiler_version() { + if [[ "${COMPILER_VERSION}" != "unset" ]]; then + VERSION_POSTFIX="-${COMPILER_VERSION}" + fi +} + +function main() { + parse_args "$@" + packages +} + +main "$@"