diff options
19 files changed, 1470 insertions, 6 deletions
diff --git a/.github/actions/download-ow/action.yml b/.github/actions/download-ow/action.yml new file mode 100644 index 000000000..a93a229cc --- /dev/null +++ b/.github/actions/download-ow/action.yml @@ -0,0 +1,15 @@ +name: 'Download OpenWatcom' +description: 'Downloads OpenWatcom' +inputs: + version: + description: 'OpenWatcom version' + required: false + default: '2.0' +runs: + using: "composite" + steps: + - name: Install OpenWatcom + run: | + OW_DIR=$(./scripts/install-ow.sh -d $HOME/watcom --version ${{ inputs.version }}) + (cygpath -w ${OW_DIR} 2>/dev/null || echo ${OW_DIR}) >> ${GITHUB_PATH} + shell: bash diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 81a3e4721..8dd4d1dfb 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -787,3 +787,52 @@ jobs: - name: Run Tests run: ${{ matrix.config.script }} ./release/install-root/bin shell: bash + + test-windows-extra: + name: ${{ matrix.config.name }} + runs-on: windows-latest + timeout-minutes: 60 + needs: build-windows + strategy: + fail-fast: false + matrix: + config: + - { + name: 'Run Windows tests (OpenWatcom)', + testProfile: 'watcom-2_0_0-x86', + script: './scripts/test-baremetal.sh', + } + env: + QTEST_FUNCTION_TIMEOUT: 9000000 + QBS_AUTOTEST_PROFILE: 'extra' + QBS_TEST_SOURCE_ROOT: 'tests' + QT_ASSUME_STDERR_HAS_CONSOLE: 1 + steps: + - uses: actions/checkout@v1 + - name: Download artifact + uses: actions/download-artifact@v1 + with: + name: qbs-windows-${{ github.run_id }}.zip + path: ./ + - name: Unpack artifact + run: | + mkdir -p release/install-root + cd release/install-root + 7z x ../../qbs-windows-${{ github.run_id }}.zip + shell: bash + - name: Update PATH + run: echo "./release/install-root/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + - name: Install required packages + run: choco install -y pkgconfiglite --download-checksum=6004df17818f5a6dbf19cb335cc92702 + - name: Install OpenWatcom + uses: ./.github/actions/download-ow + - name: Setup Qbs + run: | + qbs setup-toolchains --detect + qbs config profiles.extra.baseProfile ${{ matrix.config.testProfile }} + qbs config defaultProfile extra + qbs config --list + shell: bash + - name: Run Tests + run: ${{ matrix.config.script }} ./release/install-root/bin + shell: bash diff --git a/doc/reference/cli/cli-options.qdocinc b/doc/reference/cli/cli-options.qdocinc index a993f6d12..db8eeae48 100644 --- a/doc/reference/cli/cli-options.qdocinc +++ b/doc/reference/cli/cli-options.qdocinc @@ -501,6 +501,7 @@ \li \c sdcc \li \c cosmic \li \c dmc + \li \c watcom \endlist //! [type] diff --git a/doc/reference/items/probe/watcom-probe.qdoc b/doc/reference/items/probe/watcom-probe.qdoc new file mode 100644 index 000000000..9c1c7486c --- /dev/null +++ b/doc/reference/items/probe/watcom-probe.qdoc @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2022 Denis Shienkov <denis.shienkov@gmail.com> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \qmltype WatcomProbe + \inqmlmodule QbsProbes + \ingroup list-of-items + \keyword QML.WatcomProbe + \inherits PathProbe + \brief Collects Oopen Watcom toolchain compiler information. + \since Qbs 1.22 + \internal + + Detects the version, supported architecture, the platform + endianness, and other stuff for the specified compiler executable + from the \l{https://github.com/open-watcom}{Open Watcom} toolchain. +*/ + +/*! + \qmlproperty string WatcomProbe::compilerFilePath + + An input property which is a full path to the Digital Mars compiler + executable. + + \nodefaultvalue +*/ + +/*! + \qmlproperty string WatcomProbe::architecture + + Detected architecture of the target platform's processor. + + The only possible value is \c "x86". + + \nodefaultvalue +*/ + +/*! + \qmlproperty string WatcomProbe::endianness + + Detected endianness of the target platform's processor architecture. + + The possible values are \c "big" and \c "little". + + \nodefaultvalue +*/ + +/*! + \qmlproperty string WatcomProbe::targetPlatform + + Detected target platform. + + The possible values are \c "windows" and \c "linux". + + \nodefaultvalue +*/ + +/*! + \qmlproperty int WatcomProbe::versionMajor + + Detected major compiler version. + + \nodefaultvalue +*/ + +/*! + \qmlproperty int WatcomProbe::versionMinor + + Detected minor compiler version. + + \nodefaultvalue +*/ + +/*! + \qmlproperty int WatcomProbe::versionPatch + + Detected patch compiler version. + + \nodefaultvalue +*/ + +/*! + \qmlproperty stringList WatcomProbe::includePaths + + Detected compiler include paths. + + \nodefaultvalue +*/ + +/*! + \qmlproperty var WatcomProbe::compilerDefinesByLanguage + + Detected set of compiler pre-defined macros depending + on the \c "C" or \c "C++" language. + + \nodefaultvalue +*/ + +/*! + \qmlproperty var WatcomProbe::environment + + Detected compiler run environment. + + \nodefaultvalue +*/ diff --git a/doc/reference/modules/qbs-module.qdoc b/doc/reference/modules/qbs-module.qdoc index 06f68cf5f..029f6f2cd 100644 --- a/doc/reference/modules/qbs-module.qdoc +++ b/doc/reference/modules/qbs-module.qdoc @@ -536,6 +536,9 @@ \li \c{"sdcc"} \li \c{["sdcc"]} \row + \li \c{"watcom"} + \li \c{["watcom"]} + \row \li \c{"xcode"} \li \c{["xcode", "clang", "llvm", "gcc"]} \endtable diff --git a/scripts/install-ow.sh b/scripts/install-ow.sh new file mode 100755 index 000000000..4c8784277 --- /dev/null +++ b/scripts/install-ow.sh @@ -0,0 +1,193 @@ +#!/usr/bin/env bash +############################################################################# +## +## Copyright (C) 2022 Denis Shienkov <denis.shienkov@gmail.com> +## Contact: https://www.qt.io/licensing/ +## +## This file is part of Qbs. +## +## $QT_BEGIN_LICENSE:LGPL$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 3 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL3 included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 3 requirements +## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 2.0 or (at your option) the GNU General +## Public license version 3 or any later version approved by the KDE Free +## Qt Foundation. The licenses are as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-2.0.html and +## https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# +set -eu + +function show_help() { + cat <<EOF +usage: install-ow [options] [components] + +Examples + ./install-ow.sh --platform win --architecture x64 + +Options + -d, --directory <directory> + Root directory where to install the components. + Maps to c:/watcom on Windows, /opt/watcom on Linux + by default. + + --platform <platform-os> + The host platform. Can be one of linux, win. Auto-detected by default. + + --architecture <architecture-os> + The host architecture. Can be one of x86, x64. Auto-detected by default. + + --version <version> + The desired toolchain version. Currently supported only + 1.9 and 2.0 versions. + +EOF +} + +VERSION=2.0 # Default latest version (a fork of original toolchain).. + +case "$OSTYPE" in + *linux*) + PLATFORM="linux" + INSTALL_DIR="/opt/watcom" + EXE_SUFFIX= + ;; + msys) + PLATFORM="win" + INSTALL_DIR="/c/watcom" + EXE_SUFFIX=".exe" + ;; + *) + PLATFORM= + INSTALL_DIR= + EXE_SUFFIX= + ;; +esac + +case "$HOSTTYPE" in + x86_64) + ARCHITECTURE="x64" + ;; + x86) + ARCHITECTURE="x86" + ;; + *) + ARCHITECTURE= + ;; +esac + +while [ $# -gt 0 ]; do + case "$1" in + --directory|-d) + INSTALL_DIR="$2" + shift + ;; + --platform) + PLATFORM="$2" + shift + ;; + --architecture) + ARCHITECTURE="$2" + shift + ;; + --version) + VERSION="$2" + shift + ;; + --help|-h) + show_help + exit 0 + ;; + *) + ;; + esac + shift +done + +if [ -z "${INSTALL_DIR}" ]; then + echo "No --directory specified or auto-detection failed." >&2 + exit 1 +fi + +if [ -z "${PLATFORM}" ]; then + echo "No --platform specified or auto-detection failed." >&2 + exit 1 +fi + +if [ -z "${ARCHITECTURE}" ]; then + echo "No --architecture specified or auto-detection failed." >&2 + exit 1 +fi + +if [ -z "${VERSION}" ]; then + echo "No --version specified." >&2 + exit 1 +fi + +DOWNLOAD_DIR=`mktemp -d 2>/dev/null || mktemp -d -t 'ow-installer'` + +BASE_URL_PREFIX="https://github.com/open-watcom/open-watcom-" + +if [[ "${VERSION}" == "1.9" ]]; then + # Original old OW v1.9 release supports only the 32-bit packages! + if [[ "${PLATFORM}" =~ "linux" ]]; then + BIN_DIR="binl" + HOST="linux" + elif [[ "${PLATFORM}" =~ "win" ]]; then + HOST="win32" + BIN_DIR="binnt" + fi + URL="${BASE_URL_PREFIX}${VERSION}/releases/download/ow1.9/open-watcom-c-${HOST}-1.9${EXE_SUFFIX}" +else + if [[ "${PLATFORM}" =~ "linux" ]]; then + if [[ "${ARCHITECTURE}" =~ "x86" ]]; then + BIN_DIR="binl" + elif [[ "${ARCHITECTURE}" =~ "x64" ]]; then + BIN_DIR="binl" + fi + elif [[ "${PLATFORM}" =~ "win" ]]; then + if [[ "${ARCHITECTURE}" =~ "x86" ]]; then + BIN_DIR="binnt" + elif [[ "${ARCHITECTURE}" =~ "x64" ]]; then + BIN_DIR="binnt64" + fi + fi + # Default URL for the latest OW v2.0 fork. + URL="${BASE_URL_PREFIX}v2/releases/download/Current-build/open-watcom-2_0-c-${PLATFORM}-${ARCHITECTURE}${EXE_SUFFIX}" +fi + +INSTALLER="${DOWNLOAD_DIR}/setup${EXE_SUFFIX}" + +echo "Downloading from ${URL}..." >&2 +curl --progress-bar -L -o ${INSTALLER} ${URL} >&2 + +echo "Installing to ${INSTALL_DIR}..." >&2 +if [[ "${PLATFORM}" =~ "linux" ]]; then + chmod +777 ${INSTALLER} +fi + +${INSTALLER} -dDstDir=${INSTALL_DIR} -i -np -ns +echo "${INSTALL_DIR}/${BIN_DIR}" + +rm -f ${INSTALLER} diff --git a/share/qbs/imports/qbs/ModUtils/utils.js b/share/qbs/imports/qbs/ModUtils/utils.js index f01ee1070..0745c4588 100644 --- a/share/qbs/imports/qbs/ModUtils/utils.js +++ b/share/qbs/imports/qbs/ModUtils/utils.js @@ -538,7 +538,7 @@ function guessArchitecture(m) { break; } } - } else if (hasAnyOf(m, ["__i386", "__i386__", "_M_IX86"])) { + } else if (hasAnyOf(m, ["__i386", "__i386__", "_M_IX86", "__386__"])) { architecture = "x86"; } else if (hasAnyOf(m, ["__x86_64", "__x86_64__", "__amd64", "_M_X64", "_M_AMD64"])) { architecture = "x86_64"; @@ -618,7 +618,7 @@ function guessTargetPlatform(m) { return "hpux"; if (hasAnyOf(m, ["__sun", "sun"])) return "solaris"; - if (hasAnyOf(m, ["__linux__", "__linux"])) + if (hasAnyOf(m, ["__linux__", "__linux", "__LINUX__"])) return "linux"; if (hasAnyOf(m, ["__FreeBSD__", "__DragonFly__", "__FreeBSD_kernel__"])) return "freebsd"; diff --git a/share/qbs/imports/qbs/Probes/WatcomProbe.qbs b/share/qbs/imports/qbs/Probes/WatcomProbe.qbs new file mode 100644 index 000000000..34be235b1 --- /dev/null +++ b/share/qbs/imports/qbs/Probes/WatcomProbe.qbs @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2022 Denis Shienkov <denis.shienkov@gmail.com> +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs.File +import qbs.ModUtils +import "../../../modules/cpp/watcom.js" as WATCOM + +PathProbe { + // Inputs + property string compilerFilePath + property stringList enableDefinesByLanguage + + property string _pathListSeparator + property string _toolchainInstallPath + property string _targetPlatform + + // Outputs + property string architecture + property string endianness + property string targetPlatform + property int versionMajor + property int versionMinor + property int versionPatch + property stringList includePaths + property var compilerDefinesByLanguage + property var environment + + configure: { + compilerDefinesByLanguage = {}; + + if (!File.exists(compilerFilePath)) { + found = false; + return; + } + + var languages = enableDefinesByLanguage; + if (!languages || languages.length === 0) + languages = ["c"]; + + environment = WATCOM.guessEnvironment(_targetPlatform, _toolchainInstallPath, + _pathListSeparator); + + includePaths = environment["INCLUDE"].split(_pathListSeparator).filter(function(path) { + return File.exists(path); + }); + + for (var i = 0; i < languages.length; ++i) { + var tag = languages[i]; + compilerDefinesByLanguage[tag] = WATCOM.dumpMacros(environment, compilerFilePath, tag); + } + + var macros = compilerDefinesByLanguage["c"] + || compilerDefinesByLanguage["cpp"]; + + endianness = macros["__BIG_ENDIAN"] ? "big" : "little"; + architecture = ModUtils.guessArchitecture(macros); + targetPlatform = ModUtils.guessTargetPlatform(macros); + + var version = WATCOM.guessVersion(macros); + if (version) { + versionMajor = version.major; + versionMinor = version.minor; + versionPatch = version.patch; + found = !!version.found && !!architecture && !!targetPlatform; + } + } +} diff --git a/share/qbs/modules/cpp/cpp.js b/share/qbs/modules/cpp/cpp.js index 315b902ee..846a4cfad 100644 --- a/share/qbs/modules/cpp/cpp.js +++ b/share/qbs/modules/cpp/cpp.js @@ -51,9 +51,15 @@ function languageVersion(versionArray, knownValues, lang) { return version; } -function extractMacros(output) { +function extractMacros(output, regexp) { var m = {}; - output.trim().split(/\r?\n/g).map(function (line) { + output.trim().split(/\r?\n/g).map(function(line) { + if (regexp) { + var match = regexp.exec(line); + if (!match) + return; + line = match[1]; + } var prefix = "#define "; if (!line.startsWith(prefix)) return; diff --git a/share/qbs/modules/cpp/watcom.js b/share/qbs/modules/cpp/watcom.js new file mode 100644 index 000000000..c186fe814 --- /dev/null +++ b/share/qbs/modules/cpp/watcom.js @@ -0,0 +1,509 @@ +/**************************************************************************** +** +** Copyright (C) 2022 Denis Shienkov <denis.shienkov@gmail.com> +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +var Cpp = require("cpp.js"); +var Environment = require("qbs.Environment"); +var File = require("qbs.File"); +var FileInfo = require("qbs.FileInfo"); +var ModUtils = require("qbs.ModUtils"); +var PathTools = require("qbs.PathTools"); +var Process = require("qbs.Process"); +var TemporaryDir = require("qbs.TemporaryDir"); +var TextFile = require("qbs.TextFile"); +var Utilities = require("qbs.Utilities"); + +function toolchainDetails(qbs) { + var targetPlatform = qbs.targetPlatform; + var details = {}; + if (targetPlatform.contains("windows")) { + details.imageFormat = "pe"; + details.executableSuffix = ".exe"; + details.dynamicLibrarySuffix = ".dll"; + } else if (targetPlatform.contains("linux")) { + details.imageFormat = "elf"; + details.executableSuffix = ""; + details.dynamicLibrarySuffix = ".so"; + } + return details; +} + +function languageFlag(tag) { + if (tag === "c") + return "-xc"; + else if (tag === "cpp") + return "-xc++"; +} + +function guessVersion(macros) { + var version = parseInt(macros["__WATCOMC__"], 10) + || parseInt(macros["__WATCOM_CPLUSPLUS__"], 10); + if (version) { + return { major: parseInt((version - 1100) / 100), + minor: parseInt(version / 10) % 10, + patch: ((version % 10) > 0) ? parseInt(version % 10) : 0 } + } +} + +function guessEnvironment(targetPlatform, toolchainInstallPath, pathListSeparator) { + var toolchainRootPath = FileInfo.path(toolchainInstallPath); + if (!File.exists(toolchainRootPath)) { + throw "Unable to deduce environment due to compiler root directory: '" + + toolchainRootPath + "' does not exist"; + } + + var env = {}; + + function setVariable(key, properties, path, separator) { + var values = []; + for (var i = 0; i < properties.length; ++i) { + if (path) { + var fullpath = FileInfo.joinPaths(path, properties[i]); + values.push(FileInfo.toNativeSeparators(fullpath)); + } else { + values.push(properties[i]); + } + } + env[key] = values.join(separator); + } + + setVariable("WATCOM", [toolchainRootPath], undefined, pathListSeparator); + setVariable("EDPATH", ["eddat"], toolchainRootPath, pathListSeparator); + if (targetPlatform === "windows") { + setVariable("WIPFC", ["wipfc"], toolchainRootPath, pathListSeparator); + setVariable("PATH", ["binw", "binnt", "binnt64"], toolchainRootPath, pathListSeparator); + setVariable("INCLUDE", ["h", "h/nt", "h/nt/directx", "h/nt/ddk"], + toolchainRootPath, pathListSeparator); + setVariable("WHTMLHELP", ["binnt/help"], toolchainRootPath, pathListSeparator); + } else if (targetPlatform === "linux") { + setVariable("PATH", ["binl64", "binl"], toolchainRootPath, pathListSeparator); + setVariable("INCLUDE", ["lh"], toolchainRootPath, pathListSeparator); + } else { + throw "Unable to deduce environment for unsupported target platform: '" + + targetPlatform + "'"; + } + return env; +} + +function dumpMacros(environment, compilerPath, tag) { + // Note: The Open Watcom compiler does not support the predefined + // macros dumping. So, we do it with the following trick, where we try + // to create and compile a special temporary file and to parse the console + // output with the own magic pattern: #define <key> <value>. + + var outputDirectory = new TemporaryDir(); + var outputFilePath = FileInfo.joinPaths(outputDirectory.path(), "dump-macros.c"); + var outputFile = new TextFile(outputFilePath, TextFile.WriteOnly); + outputFile.writeLine("#define VALUE_TO_STRING(x) #x"); + outputFile.writeLine("#define VALUE(x) VALUE_TO_STRING(x)"); + outputFile.writeLine("#define VAR_NAME_VALUE(var) \"#define \"#var\" \"VALUE(var)"); + // Declare all available pre-defined macros of Watcon compiler. + var keys = [ + // Prepare the DOS target macros. + "__DOS__", "_DOS", "MSDOS", + // Prepare the OS/2 target macros. + "__OS2__", + // Prepare the QNX target macros. + "__QNX__", + // Prepare the Netware target macros. + "__NETWARE__", "__NETWARE_386__", + // Prepare the Windows target macros. + "__NT__", "__WINDOWS__", "_WINDOWS", "__WINDOWS_386__", + // Prepare the Linux and Unix target macros. + "__LINUX__", "__UNIX__", + // Prepare the 16-bit target specific macros. + "__I86__", "M_I86", "_M_I86", "_M_IX86", + // Prepare the 32-bit target specific macros. + "__386__", "M_I386", "_M_I386", "_M_IX86", + // Prepare the indicated options macros. + "_MT", "_DLL", "__FPI__", "__CHAR_SIGNED__", "__INLINE_FUNCTIONS__", + "_CPPRTTI", "_CPPUNWIND", "NO_EXT_KEYS", + // Prepare the common memory model macros. + "__FLAT__", "__SMALL__", "__MEDIUM__", + "__COMPACT__", "__LARGE__", "__HUGE__", + // Prepare the 16-bit memory model macros. + "M_I86SM", "_M_I86SM", "M_I86MM", "_M_I86MM", "M_I86CM", + "_M_I86CM", "M_I86LM", "_M_I86LM", "M_I86HM", "_M_I86HM", + // Prepare the 32-bit memory model macros. + "M_386FM", "_M_386FM", "M_386SM", "M_386MM", "_M_386MM", + "M_386CM", "_M_386CM", "M_386LM", "_M_386LM", + // Prepare the compiler macros. + "__X86__", "__cplusplus", "__WATCOMC__", "__WATCOM_CPLUSPLUS__", + "_INTEGRAL_MAX_BITS", "_PUSHPOP_SUPPORTED", "_STDCALL_SUPPORTED", + // Prepare the other macros. + "__3R__", "_based", "_cdecl", "cdecl", "_export", "_far16", "_far", "far", + "_fastcall", "_fortran", "fortran", "_huge", "huge", "_inline", "_interrupt", + "interrupt", "_loadds", "_near", "near", "_pascal", "pascal", "_saveregs", + "_segment", "_segname", "_self", "SOMDLINK", "_STDCALL_SUPPORTED", "__SW_0", + "__SW_3R", "__SW_5", "__SW_FP287", "__SW_FP2", "__SW_FP387", "__SW_FP3", + "__SW_FPI", "__SW_MF", "__SW_MS", "__SW_ZDP", "__SW_ZFP", "__SW_ZGF", + "__SW_ZGP", "_stdcall", "_syscall", "__BIG_ENDIAN" + ]; + for (var i = 0; i < keys.length; ++i) { + var key = keys[i]; + outputFile.writeLine("#if defined(" + key + ")"); + outputFile.writeLine("#pragma message (VAR_NAME_VALUE(" + key + "))"); + outputFile.writeLine("#endif"); + } + outputFile.close(); + + var process = new Process(); + process.setWorkingDirectory(outputDirectory.path()); + for (var envkey in environment) + process.setEnv(envkey, environment[envkey]); + + var args = [ outputFilePath ].concat(languageFlag(tag)); + process.exec(compilerPath, args, false); + var m = Cpp.extractMacros(process.readStdOut(), /"?(#define(\s\w+){1,2})"?$/); + if (tag === "cpp" && m["__cplusplus"] === "1") + return m; + else if (tag === "c") + return m; +} + +function effectiveLinkerPath(product) { + if (product.cpp.linkerMode === "automatic") { + var compilerPath = product.cpp.compilerPath; + if (compilerPath) + return compilerPath; + console.log("Found no C-language objects, choosing system linker for " + product.name); + } + return product.cpp.linkerPath; +} + +function useCompilerDriverLinker(product) { + var linker = effectiveLinkerPath(product); + var compiler = product.cpp.compilerPath; + return linker === compiler; +} + +function escapeLinkerFlags(useCompilerDriver, linkerFlags) { + if (!linkerFlags || linkerFlags.length === 0) + return []; + + if (useCompilerDriver) { + var sep = ","; + return [["-Wl"].concat(linkerFlags).join(sep)]; + } + return linkerFlags; +} + +function assemblerFlags(project, product, input, outputs, explicitlyDependsOn) { + var args = [FileInfo.toNativeSeparators(input.filePath)]; + args.push("-fo=" + FileInfo.toNativeSeparators(outputs.obj[0].filePath)); + + args = args.concat(Cpp.collectPreincludePaths(input).map(function(path) { + return "-fi" + FileInfo.toNativeSeparators(path); + })); + + args = args.concat(Cpp.collectDefinesArguments(input, "-d")); + + var includePaths = [].concat(Cpp.collectIncludePaths(input)).concat( + Cpp.collectSystemIncludePaths(input)); + args = args.concat(includePaths.map(function(path) { + return "-i" + FileInfo.toNativeSeparators(path); + })); + + if (input.cpp.debugInformation) + args.push("-d1"); + + var warnings = input.cpp.warningLevel + if (warnings === "none") + args.push("-w0"); + else if (warnings === "all") + args.push("-wx"); + if (input.cpp.treatWarningsAsErrors) + args.push("-we"); + + args.push("-zq"); // Silent. + args = args.concat(Cpp.collectMiscAssemblerArguments(input)); + return args; +} + +function compilerFlags(project, product, input, outputs, explicitlyDependsOn) { + var args = ["-g" + (input.cpp.debugInformation ? "3" : "0")]; + + var targetPlatform = product.qbs.targetPlatform; + if (product.type.contains("application")) { + if (targetPlatform === "windows") { + var consoleApplication = product.consoleApplication; + args.push(consoleApplication ? "-mconsole" : "-mwindows"); + } + } else if (product.type.contains("dynamiclibrary")) { + args.push("-shared"); + } + + var optimization = input.cpp.optimization + if (optimization === "fast") + args.push("-Ot"); + else if (optimization === "small") + args.push("-Os"); + else if (optimization === "none") + args.push("-O0"); + + var warnings = input.cpp.warningLevel + if (warnings === "none") { + args.push("-w"); + } else if (warnings === "all") { + args.push("-Wall"); + args.push("-Wextra"); + } + if (input.cpp.treatWarningsAsErrors) + args.push("-Werror"); + + var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(outputs.obj[0].fileTags)); + + var langFlag = languageFlag(tag); + if (langFlag) + args.push(langFlag); + + if (tag === "cpp") { + var enableExceptions = input.cpp.enableExceptions; + if (enableExceptions) { + var ehModel = input.cpp.exceptionHandlingModel; + switch (ehModel) { + case "direct": + args.push("-feh-direct"); + break; + case "table": + args.push("-feh-table"); + break; + default: + args.push("-feh"); + break; + } + } else { + args.push("-fno-eh"); + } + + var enableRtti = input.cpp.enableRtti; + args.push(enableRtti ? "-frtti" : "-fno-rtti"); + } else if (tag === "c") { + var knownValues = ["c99", "c89"]; + var cLanguageVersion = Cpp.languageVersion(input.cpp.cLanguageVersion, knownValues, "C"); + switch (cLanguageVersion) { + case "c89": + args.push("-std=c89"); + break; + case "c99": + args.push("-std=c99"); + break; + } + } + + var preincludePaths = Cpp.collectPreincludePaths(input); + for (var i = 0; i < preincludePaths.length; ++i) + args.push(input.cpp.preincludeFlag, preincludePaths[i]); + + args = args.concat(Cpp.collectDefinesArguments(input)); + + args = args.concat(Cpp.collectIncludePaths(input).map(function(path) { + return input.cpp.includeFlag + FileInfo.toNativeSeparators(path); + })); + args = args.concat(Cpp.collectSystemIncludePaths(input).map(function(path) { + return input.cpp.systemIncludeFlag + FileInfo.toNativeSeparators(path); + })); + + args = args.concat(Cpp.collectMiscCompilerArguments(input, tag), + Cpp.collectMiscDriverArguments(input)); + + args.push("-o", FileInfo.toNativeSeparators(outputs.obj[0].filePath)); + args.push("-c", FileInfo.toNativeSeparators(input.filePath)); + + return args; +} + +function resourceCompilerFlags(project, product, input, outputs) { + var args = [input.filePath]; + args.push("-fo=" + FileInfo.toNativeSeparators(outputs.res[0].filePath)); + args = args.concat(Cpp.collectDefinesArguments(input, "-d")); + + args = args.concat(Cpp.collectIncludePaths(input).map(function(path) { + return input.cpp.includeFlag + FileInfo.toNativeSeparators(path); + })); + args = args.concat(Cpp.collectSystemIncludePaths(input).map(function(path) { + return input.cpp.includeFlag + FileInfo.toNativeSeparators(path); + })); + args.push("-q", "-ad", "-r"); + return args; +} + +function linkerFlags(project, product, inputs, outputs) { + var args = []; + var useCompilerDriver = useCompilerDriverLinker(product); + if (useCompilerDriver) { + var targetPlatform = product.qbs.targetPlatform; + if (product.type.contains("application")) { + if (targetPlatform === "windows") + args.push("-bnt") + else if (targetPlatform === "linux") + args.push("-blinux") + args.push("-o", FileInfo.toNativeSeparators(outputs.application[0].filePath)); + if (product.cpp.generateLinkerMapFile) + args.push("-fm=" + FileInfo.toNativeSeparators(outputs.mem_map[0].filePath)); + } else if (product.type.contains("dynamiclibrary")) { + if (targetPlatform === "windows") { + args.push("-bnt_dll") + args.push("-Wl, option implib=" + FileInfo.toNativeSeparators( + outputs.dynamiclibrary_import[0].filePath)); + } + args.push("-o", FileInfo.toNativeSeparators(outputs.dynamiclibrary[0].filePath)) + } + + var escapableLinkerFlags = []; + var targetLinkerFlags = product.cpp.targetLinkerFlags; + if (targetLinkerFlags) + escapableLinkerFlags = escapableLinkerFlags.concat(targetLinkerFlags); + + escapableLinkerFlags = escapableLinkerFlags.concat( + Cpp.collectMiscEscapableLinkerArguments(product)); + + var escapedLinkerFlags = escapeLinkerFlags(useCompilerDriver, escapableLinkerFlags); + if (escapedLinkerFlags) + args = args.concat(escapedLinkerFlags); + + args = args.concat(Cpp.collectLibraryPaths(product).map(function(path) { + return product.cpp.libraryPathFlag + FileInfo.toNativeSeparators(path); + })); + args = args.concat(Cpp.collectLinkerObjectPaths(inputs).map(function(path) { + return FileInfo.toNativeSeparators(path); + })); + + var libraryDependencies = Cpp.collectLibraryDependencies(product); + for (var i = 0; i < libraryDependencies.length; ++i) { + var lib = libraryDependencies[i].filePath; + if (FileInfo.isAbsolutePath(lib) || lib.startsWith('@')) + args.push(FileInfo.toNativeSeparators(lib)); + else + args.push("-Wl, libfile " + lib); + } + + var resourcePaths = Cpp.collectResourceObjectPaths(inputs).map(function(path) { + return FileInfo.toNativeSeparators(path); + }); + if (resourcePaths.length > 0) + args = args.concat("-Wl, resource " + resourcePaths.join(",")); + } + + args = args.concat(Cpp.collectMiscLinkerArguments(product), + Cpp.collectMiscDriverArguments(product)); + return args; +} + +function libraryManagerFlags(project, product, inputs, outputs) { + var args = ["-b", "-n", "-q"]; + args = args.concat(Cpp.collectLinkerObjectPaths(inputs).map(function(path) { + return "+" + FileInfo.toNativeSeparators(path); + })); + args.push("-o", FileInfo.toNativeSeparators(outputs.staticlibrary[0].filePath)); + return args; +} + +function disassemblerFlags(project, product, inputs, outputs) { + var objectPath = Cpp.relativePath(product.buildDirectory, outputs.obj[0].filePath); + var listingPath = Cpp.relativePath(product.buildDirectory, outputs.lst[0].filePath); + var args = []; + args.push(FileInfo.toNativeSeparators(objectPath)); + args.push("-l=" + FileInfo.toNativeSeparators(listingPath)); + args.push("-s", "-a"); + return args; +} + +function generateCompilerListing(project, product, inputs, outputs, input, output) { + var args = disassemblerFlags(project, product, input, outputs); + var cmd = new Command(input.cpp.disassemblerPath, args); + cmd.workingDirectory = product.buildDirectory; + cmd.silent = true; + cmd.jobPool = "watcom_job_pool"; + return cmd; +} + +function prepareAssembler(project, product, inputs, outputs, input, output, explicitlyDependsOn) { + var cmds = []; + var args = assemblerFlags(project, product, input, outputs); + var cmd = new Command(input.cpp.assemblerPath, args); + cmd.workingDirectory = product.buildDirectory; + cmd.description = "assembling " + input.fileName; + cmd.highlight = "compiler"; + cmd.jobPool = "watcom_job_pool"; + cmds.push(cmd); + if (input.cpp.generateAssemblerListingFiles) + cmds.push(generateCompilerListing(project, product, inputs, outputs, input, output)); + return cmds; +} + +function prepareCompiler(project, product, inputs, outputs, input, output, explicitlyDependsOn) { + var cmds = []; + var args = compilerFlags(project, product, input, outputs, explicitlyDependsOn); + var cmd = new Command(input.cpp.compilerPath, args); + cmd.workingDirectory = product.buildDirectory; + cmd.description = "compiling " + input.fileName; + cmd.highlight = "compiler"; + cmd.jobPool = "watcom_job_pool"; + cmds.push(cmd); + if (input.cpp.generateCompilerListingFiles) + cmds.push(generateCompilerListing(project, product, inputs, outputs, input, output)); + return cmds; +} + +function prepareResourceCompiler(project, product, inputs, outputs, input, output, + explicitlyDependsOn) { + var args = resourceCompilerFlags(project, product, input, outputs); + var cmd = new Command(input.cpp.resourceCompilerPath, args); + // Set working directory to source directory as a workaround + // to make the resources compilable by resource compiler (it is magic). + cmd.workingDirectory = product.sourceDirectory; + cmd.description = "compiling " + input.fileName; + cmd.highlight = "compiler"; + cmd.jobPool = "watcom_job_pool"; + return [cmd]; +} + +function prepareLinker(project, product, inputs, outputs, input, output) { + var primaryOutput = outputs.dynamiclibrary ? outputs.dynamiclibrary[0] + : outputs.application[0]; + var args = linkerFlags(project, product, inputs, outputs); + var linkerPath = effectiveLinkerPath(product); + var cmd = new Command(linkerPath, args); + cmd.workingDirectory = product.buildDirectory; + cmd.description = "linking " + primaryOutput.fileName; + cmd.highlight = "linker"; + cmd.jobPool = "watcom_job_pool"; + return [cmd]; +} + +function prepareLibraryManager(project, product, inputs, outputs, input, output) { + var args = libraryManagerFlags(project, product, inputs, outputs); + var cmd = new Command(product.cpp.libraryManagerPath, args); + cmd.workingDirectory = product.buildDirectory; + cmd.description = "linking " + outputs.staticlibrary[0].fileName; + cmd.highlight = "linker"; + cmd.jobPool = "watcom_job_pool"; + return [cmd]; +} diff --git a/share/qbs/modules/cpp/watcom.qbs b/share/qbs/modules/cpp/watcom.qbs new file mode 100644 index 000000000..d180b1455 --- /dev/null +++ b/share/qbs/modules/cpp/watcom.qbs @@ -0,0 +1,195 @@ +/**************************************************************************** +** +** Copyright (C) 2022 Denis Shienkov <denis.shienkov@gmail.com> +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs 1.0 +import qbs.File +import qbs.FileInfo +import qbs.ModUtils +import qbs.Probes +import "cpp.js" as Cpp +import "watcom.js" as WATCOM + +CppModule { + condition: qbs.toolchain && qbs.toolchain.contains("watcom") + + Probes.BinaryProbe { + id: compilerPathProbe + condition: !toolchainInstallPath && !_skipAllChecks + names: ["owcc"] + } + + Probes.WatcomProbe { + id: watcomProbe + condition: !_skipAllChecks + compilerFilePath: compilerPath + enableDefinesByLanguage: enableCompilerDefinesByLanguage + _pathListSeparator: qbs.pathListSeparator + _toolchainInstallPath: toolchainInstallPath + _targetPlatform: qbs.targetPlatform + } + + qbs.architecture: watcomProbe.found ? watcomProbe.architecture : original + qbs.targetPlatform: watcomProbe.found ? watcomProbe.targetPlatform : original + + compilerVersionMajor: watcomProbe.versionMajor + compilerVersionMinor: watcomProbe.versionMinor + compilerVersionPatch: watcomProbe.versionPatch + endianness: watcomProbe.endianness + + compilerDefinesByLanguage: watcomProbe.compilerDefinesByLanguage + compilerIncludePaths: watcomProbe.includePaths + + toolchainInstallPath: compilerPathProbe.found ? compilerPathProbe.path : undefined + + /* Work-around for QtCreator which expects these properties to exist. */ + property string cCompilerName: compilerName + property string cxxCompilerName: compilerName + + compilerName: "owcc" + compilerExtension + compilerPath: FileInfo.joinPaths(toolchainInstallPath, compilerName) + + assemblerName: "wasm" + compilerExtension + assemblerPath: FileInfo.joinPaths(toolchainInstallPath, assemblerName) + + linkerName: "wlink" + compilerExtension + linkerPath: FileInfo.joinPaths(toolchainInstallPath, linkerName) + + property string disassemblerName: "wdis" + compilerExtension + property string disassemblerPath: FileInfo.joinPaths(toolchainInstallPath, + disassemblerName) + property string resourceCompilerName: "wrc" + compilerExtension + property string resourceCompilerPath: FileInfo.joinPaths(toolchainInstallPath, + resourceCompilerName) + property string libraryManagerName: "wlib" + compilerExtension + property string libraryManagerPath: FileInfo.joinPaths(toolchainInstallPath, + libraryManagerName) + + runtimeLibrary: "dynamic" + + staticLibrarySuffix: ".lib" + dynamicLibrarySuffix: toolchainDetails.dynamicLibrarySuffix + executableSuffix: toolchainDetails.executableSuffix + objectSuffix: ".obj" + + imageFormat: toolchainDetails.imageFormat + + defineFlag: "-D" + includeFlag: "-I" + systemIncludeFlag: "-I" + preincludeFlag: "-include" + libraryDependencyFlag: "-l" + libraryPathFlag: "-L" + linkerScriptFlag: "" + + toolchainDetails: WATCOM.toolchainDetails(qbs) + + knownArchitectures: ["x86"] + + property var buildEnv: watcomProbe.environment + setupBuildEnvironment: { + for (var key in product.cpp.buildEnv) { + var v = new ModUtils.EnvironmentVariable(key, product.qbs.pathListSeparator); + v.prepend(product.cpp.buildEnv[key]); + v.set(); + } + } + + Rule { + id: assembler + inputs: ["asm"] + outputFileTags: Cpp.assemblerOutputTags(generateAssemblerListingFiles) + outputArtifacts: Cpp.assemblerOutputArtifacts(input) + prepare: WATCOM.prepareAssembler.apply(WATCOM, arguments) + } + + FileTagger { + patterns: ["*.asm"] + fileTags: ["asm"] + } + + Rule { + id: compiler + inputs: ["cpp", "c"] + auxiliaryInputs: ["hpp"] + outputFileTags: Cpp.compilerOutputTags(generateCompilerListingFiles) + outputArtifacts: Cpp.compilerOutputArtifacts(input) + prepare: WATCOM.prepareCompiler.apply(WATCOM, arguments) + } + + Rule { + id: rccCompiler + inputs: ["rc"] + auxiliaryInputs: ["hpp"] + outputFileTags: Cpp.resourceCompilerOutputTags() + outputArtifacts: Cpp.resourceCompilerOutputArtifacts(input) + prepare: WATCOM.prepareResourceCompiler.apply(WATCOM, arguments) + } + + FileTagger { + patterns: ["*.rc"] + fileTags: ["rc"] + } + + Rule { + id: applicationLinker + multiplex: true + inputs: ["obj", "res", "linkerscript"] + inputsFromDependencies: ["staticlibrary", "dynamiclibrary_import"] + outputFileTags: Cpp.applicationLinkerOutputTags(generateLinkerMapFile) + outputArtifacts: Cpp.applicationLinkerOutputArtifacts(product) + prepare: WATCOM.prepareLinker.apply(WATCOM, arguments) + } + + Rule { + id: dynamicLibraryLinker + condition: qbs.targetOS.contains("windows") + multiplex: true + inputs: ["obj", "res"] + inputsFromDependencies: ["staticlibrary", "dynamiclibrary_import"] + outputFileTags: Cpp.dynamicLibraryLinkerOutputTags(); + outputArtifacts: Cpp.dynamicLibraryLinkerOutputArtifacts(product) + prepare: WATCOM.prepareLinker.apply(WATCOM, arguments) + } + + Rule { + id: libraryManager + multiplex: true + inputs: ["obj"] + inputsFromDependencies: ["staticlibrary", "dynamiclibrary_import"] + outputFileTags: Cpp.staticLibraryLinkerOutputTags() + outputArtifacts: Cpp.staticLibraryLinkerOutputArtifacts(product) + prepare: WATCOM.prepareLibraryManager.apply(WATCOM, arguments) + } + + JobLimit { + jobPool: "watcom_job_pool" + jobCount: 1 + } +} diff --git a/src/app/qbs-setup-toolchains/CMakeLists.txt b/src/app/qbs-setup-toolchains/CMakeLists.txt index bb287be52..64347cd6c 100644 --- a/src/app/qbs-setup-toolchains/CMakeLists.txt +++ b/src/app/qbs-setup-toolchains/CMakeLists.txt @@ -20,6 +20,8 @@ set(SOURCES probe.h sdccprobe.cpp sdccprobe.h + watcomprobe.cpp + watcomprobe.h xcodeprobe.cpp xcodeprobe.h ) diff --git a/src/app/qbs-setup-toolchains/probe.cpp b/src/app/qbs-setup-toolchains/probe.cpp index 5a04232e1..ceb95948b 100644 --- a/src/app/qbs-setup-toolchains/probe.cpp +++ b/src/app/qbs-setup-toolchains/probe.cpp @@ -46,6 +46,7 @@ #include "keilprobe.h" #include "msvcprobe.h" #include "sdccprobe.h" +#include "watcomprobe.h" #include "xcodeprobe.h" #include <logging/translator.h> @@ -123,6 +124,8 @@ QString toolchainTypeFromCompilerName(const QString &compilerName) return QStringLiteral("cosmic"); if (isDmcCompiler(compilerName)) return QStringLiteral("dmc"); + if (isWatcomCompiler(compilerName)) + return QStringLiteral("watcom"); return {}; } @@ -144,6 +147,7 @@ void probe(Settings *settings) sdccProbe(settings, profiles); cosmicProbe(settings, profiles); dmcProbe(settings, profiles); + watcomProbe(settings, profiles); if (profiles.empty()) { qStderr << Tr::tr("Could not detect any toolchains. No profile created.") << Qt::endl; @@ -187,6 +191,8 @@ void createProfile(const QString &profileName, const QString &toolchainType, createCosmicProfile(compiler, settings, profileName); else if (toolchain.contains(QLatin1String("dmc"))) createDmcProfile(compiler, settings, profileName); + else if (toolchain.contains(QLatin1String("watcom"))) + createWatcomProfile(compiler, settings, profileName); else throw qbs::ErrorInfo(Tr::tr("Cannot create profile: Unknown toolchain type.")); } diff --git a/src/app/qbs-setup-toolchains/probe.h b/src/app/qbs-setup-toolchains/probe.h index e97530285..827171fb2 100644 --- a/src/app/qbs-setup-toolchains/probe.h +++ b/src/app/qbs-setup-toolchains/probe.h @@ -80,8 +80,6 @@ int extractVersion(const QByteArray ¯oDump, const QByteArray &keyToken); bool isSameExecutable(const QString &exe1, const QString &exe2); using MacrosMap = QMap<QString, QString>; -using DefinesList = QVector<QByteArray>; - MacrosMap dumpMacros(const std::function<QStringList()> &func); #endif // Header guard diff --git a/src/app/qbs-setup-toolchains/qbs-setup-toolchains.pro b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.pro index 1ae1c710d..69d6552ee 100644 --- a/src/app/qbs-setup-toolchains/qbs-setup-toolchains.pro +++ b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.pro @@ -13,6 +13,7 @@ HEADERS += \ msvcprobe.h \ probe.h \ sdccprobe.h \ + watcomprobe.h \ xcodeprobe.h \ SOURCES += \ @@ -27,6 +28,7 @@ SOURCES += \ msvcprobe.cpp \ probe.cpp \ sdccprobe.cpp \ + watcomprobe.cpp \ xcodeprobe.cpp \ mingw { diff --git a/src/app/qbs-setup-toolchains/qbs-setup-toolchains.qbs b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.qbs index 2daf916e9..6987f3717 100644 --- a/src/app/qbs-setup-toolchains/qbs-setup-toolchains.qbs +++ b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.qbs @@ -23,6 +23,8 @@ QbsApp { "probe.h", "sdccprobe.cpp", "sdccprobe.h", + "watcomprobe.cpp", + "watcomprobe.h", "xcodeprobe.cpp", "xcodeprobe.h", ] diff --git a/src/app/qbs-setup-toolchains/watcomprobe.cpp b/src/app/qbs-setup-toolchains/watcomprobe.cpp new file mode 100644 index 000000000..9765f7424 --- /dev/null +++ b/src/app/qbs-setup-toolchains/watcomprobe.cpp @@ -0,0 +1,200 @@ +/**************************************************************************** +** +** Copyright (C) 2022 Denis Shienkov <denis.shienkov@gmail.com> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "watcomprobe.h" +#include "probe.h" + +#include "../shared/logging/consolelogger.h" + +#include <logging/translator.h> + +#include <tools/hostosinfo.h> +#include <tools/profile.h> + +#include <QtCore/qdir.h> +#include <QtCore/qmap.h> +#include <QtCore/qprocess.h> +#include <QtCore/qregularexpression.h> +#include <QtCore/qsettings.h> +#include <QtCore/qtemporaryfile.h> + +using namespace qbs; +using Internal::HostOsInfo; +using Internal::Tr; + +static QStringList knownWatcomCompilerNames() +{ + return {QStringLiteral("owcc")}; +} + +static QStringList dumpOutput(const QFileInfo &compiler, const QStringList &keys) +{ + const QString filePath = QDir(QDir::tempPath()).absoluteFilePath( + QLatin1String("watcom-dump.c")); + QFile fakeIn(filePath); + if (!fakeIn.open(QIODevice::Truncate | QIODevice::WriteOnly | QIODevice::Text)) { + qbsWarning() << Tr::tr("Unable to open temporary file %1 for output: %2") + .arg(fakeIn.fileName(), fakeIn.errorString()); + return QStringList{}; + } + fakeIn.write("#define VALUE_TO_STRING(x) #x\n"); + fakeIn.write("#define VALUE(x) VALUE_TO_STRING(x)\n"); + fakeIn.write("#define VAR_NAME_VALUE(var) \"#define \" #var\" \"VALUE(var)\n"); + for (const QString &key : keys) { + fakeIn.write("#if defined(" + key.toLatin1() + ")\n"); + fakeIn.write("#pragma message (VAR_NAME_VALUE(" + key.toLatin1() + "))\n"); + fakeIn.write("#endif\n"); + } + fakeIn.close(); + QProcess p; + p.start(compiler.absoluteFilePath(), {QDir::toNativeSeparators(filePath)}); + p.waitForFinished(3000); + fakeIn.remove(); + const QStringList lines = QString::fromUtf8(p.readAllStandardOutput()) + .split(QRegularExpression(QLatin1String("\\r?\\n"))); + return lines; +} + +static QString guessWatcomArchitecture(const QFileInfo &compiler) +{ + const QStringList keys = {QStringLiteral("__I86__"), QStringLiteral("__386__")}; + const auto macros = dumpMacros([&compiler, &keys]() { return dumpOutput(compiler, keys); }); + for (auto index = 0; index < keys.count(); ++index) { + const auto &key = keys.at(index); + if (macros.contains(key) && macros.value(key) == QLatin1String("1")) { + switch (index) { + case 0: + return QLatin1String("x86_16"); + case 1: + return QLatin1String("x86"); + default: + break; + } + } + } + return QLatin1String("unknown"); +} + +static Profile createWatcomProfileHelper(const ToolchainInstallInfo &info, + Settings *settings, + QString profileName = QString()) +{ + const QFileInfo compiler = info.compilerPath; + const QString architecture = guessWatcomArchitecture(compiler); + + // In case the profile is auto-detected. + if (profileName.isEmpty()) { + if (!info.compilerVersion.isValid()) { + profileName = QStringLiteral("watcom-unknown-%1").arg(architecture); + } else { + const QString version = info.compilerVersion.toString(QLatin1Char('_'), + QLatin1Char('_')); + profileName = QStringLiteral("watcom-%1-%2").arg(version, architecture); + } + } + + Profile profile(profileName, settings); + profile.setValue(QStringLiteral("cpp.toolchainInstallPath"), compiler.absolutePath()); + profile.setValue(QStringLiteral("qbs.toolchainType"), QStringLiteral("watcom")); + if (!architecture.isEmpty()) + profile.setValue(QStringLiteral("qbs.architecture"), architecture); + + qbsInfo() << Tr::tr("Profile '%1' created for '%2'.") + .arg(profile.name(), compiler.absoluteFilePath()); + return profile; +} + +static Version dumpWatcomVersion(const QFileInfo &compiler) +{ + const QStringList keys = {QStringLiteral("__WATCOMC__"), + QStringLiteral("__WATCOM_CPLUSPLUS__")}; + const auto macros = dumpMacros([&compiler, &keys]() { return dumpOutput(compiler, keys); }); + for (const auto ¯o : macros) { + const int verCode = macro.toInt(); + return Version{(verCode - 1100) / 100, + (verCode / 10) % 10, + ((verCode % 10) > 0) ? (verCode % 10) : 0}; + } + qbsWarning() << Tr::tr("No __WATCOMC__ or __WATCOM_CPLUSPLUS__ tokens was found" + " in the compiler dump"); + return Version{}; +} + +static std::vector<ToolchainInstallInfo> installedWatcomsFromPath() +{ + std::vector<ToolchainInstallInfo> infos; + const auto compilerNames = knownWatcomCompilerNames(); + for (const QString &compilerName : compilerNames) { + const QFileInfo watcomPath(findExecutable( + HostOsInfo::appendExecutableSuffix(compilerName))); + if (!watcomPath.exists()) + continue; + const Version version = dumpWatcomVersion(watcomPath); + infos.push_back({watcomPath, version}); + } + std::sort(infos.begin(), infos.end()); + return infos; +} + +bool isWatcomCompiler(const QString &compilerName) +{ + return Internal::any_of(knownWatcomCompilerNames(), [compilerName](const QString &knownName) { + return compilerName.contains(knownName); + }); +} + +void createWatcomProfile(const QFileInfo &compiler, Settings *settings, QString profileName) +{ + const ToolchainInstallInfo info = {compiler, Version{}}; + createWatcomProfileHelper(info, settings, std::move(profileName)); +} + +void watcomProbe(Settings *settings, std::vector<Profile> &profiles) +{ + qbsInfo() << Tr::tr("Trying to detect WATCOM toolchains..."); + + const std::vector<ToolchainInstallInfo> allInfos = installedWatcomsFromPath(); + if (allInfos.empty()) { + qbsInfo() << Tr::tr("No WATCOM toolchains found."); + return; + } + + qbs::Internal::transform(allInfos, profiles, [settings](const auto &info) { + return createWatcomProfileHelper(info, settings); }); +} diff --git a/src/app/qbs-setup-toolchains/watcomprobe.h b/src/app/qbs-setup-toolchains/watcomprobe.h new file mode 100644 index 000000000..26e75bbc3 --- /dev/null +++ b/src/app/qbs-setup-toolchains/watcomprobe.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2022 Denis Shienkov <denis.shienkov@gmail.com> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_SETUPTOOLCHAINS_WATCOMPROBE_H +#define QBS_SETUPTOOLCHAINS_WATCOMPROBE_H + +#include <QtCore/qlist.h> + +QT_BEGIN_NAMESPACE +class QFileInfo; +QT_END_NAMESPACE + +namespace qbs { +class Profile; +class Settings; +} // namespace qbs + +bool isWatcomCompiler(const QString &compilerName); +void createWatcomProfile(const QFileInfo &compiler, qbs::Settings *settings, QString profileName); +void watcomProbe(qbs::Settings *settings, std::vector<qbs::Profile> &profiles); + +#endif // Header guard diff --git a/tests/auto/blackbox/testdata-baremetal/compiler-defines-by-language/compiler-defines-by-language.qbs b/tests/auto/blackbox/testdata-baremetal/compiler-defines-by-language/compiler-defines-by-language.qbs index bfd10106d..7eb2af6db 100644 --- a/tests/auto/blackbox/testdata-baremetal/compiler-defines-by-language/compiler-defines-by-language.qbs +++ b/tests/auto/blackbox/testdata-baremetal/compiler-defines-by-language/compiler-defines-by-language.qbs @@ -38,6 +38,7 @@ Project { name: "cpp_language" files: ["app.c", "cpptest.cpp"] cpp.enableCompilerDefinesByLanguage: ["cpp"] + cpp.enableExceptions: false property var foo: { if (!cpp.compilerDefinesByLanguage) throw "ASSERT cpp.compilerDefinesByLanguage: " @@ -56,6 +57,7 @@ Project { name: "c_and_cpp_language" files: ["app.c", "ctest.c", "cpptest.cpp"] cpp.enableCompilerDefinesByLanguage: ["c", "cpp"] + cpp.enableExceptions: false property var foo: { if (!cpp.compilerDefinesByLanguage) throw "ASSERT cpp.compilerDefinesByLanguage: " |