diff options
17 files changed, 978 insertions, 2 deletions
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index dde93af8e..c9ba6cb96 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -532,6 +532,9 @@ jobs: - name: rx-elf-gcc-8_3_0_202004-GNURX run: QBS_AUTOTEST_PROFILE=rx-elf-gcc-8_3_0_202004-GNURX scripts/test-baremetal.sh release/install-root/bin shell: bash + - name: cosmic-4_3_11-arm + run: QBS_AUTOTEST_PROFILE=cosmic-4_3_11-arm scripts/test-baremetal.sh release/install-root/bin + shell: bash test-macos: name: ${{ matrix.config.name }} diff --git a/doc/reference/modules/qbs-module.qdoc b/doc/reference/modules/qbs-module.qdoc index f8d48ddb4..8568cb911 100644 --- a/doc/reference/modules/qbs-module.qdoc +++ b/doc/reference/modules/qbs-module.qdoc @@ -502,6 +502,9 @@ \li \c{"clang-cl"} \li \c{["clang-cl", "msvc"]} \row + \li \c{"cosmic"} + \li \c{["cosmic"]} + \row \li \c{"gcc"} \li \c{["gcc"]} \row diff --git a/share/qbs/imports/qbs/Probes/CosmicProbe.qbs b/share/qbs/imports/qbs/Probes/CosmicProbe.qbs new file mode 100644 index 000000000..7de781e6e --- /dev/null +++ b/share/qbs/imports/qbs/Probes/CosmicProbe.qbs @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2021 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 "../../../modules/cpp/cosmic.js" as COSMIC + +PathProbe { + // Inputs + property string compilerFilePath + property stringList enableDefinesByLanguage + + // Outputs + property string architecture + property string endianness + property int versionMajor + property int versionMinor + property int versionPatch + property stringList includePaths + property var compilerDefinesByLanguage + + configure: { + compilerDefinesByLanguage = {}; + + if (!File.exists(compilerFilePath)) { + found = false; + return; + } + + var languages = enableDefinesByLanguage; + if (!languages || languages.length === 0) + languages = ["c"]; + + // COSMIC compiler support only the C-language. + if (!languages.contains("c")) { + found = false; + return; + } + + var macros = COSMIC.dumpMacros(compilerFilePath); + if (!macros) { + found = false; + return; + } + + compilerDefinesByLanguage["c"] = macros; + + architecture = COSMIC.guessArchitecture(compilerFilePath); + endianness = COSMIC.guessEndianness(architecture); + var defaultPaths = COSMIC.dumpDefaultPaths(compilerFilePath, architecture); + includePaths = defaultPaths.includePaths; + + var version = COSMIC.dumpVersion(compilerFilePath); + if (version) { + versionMajor = version.major; + versionMinor = version.minor; + versionPatch = version.patch; + found = !!architecture && !!endianness; + } + } +} diff --git a/share/qbs/modules/cpp/cosmic.js b/share/qbs/modules/cpp/cosmic.js new file mode 100644 index 000000000..e11e4da28 --- /dev/null +++ b/share/qbs/modules/cpp/cosmic.js @@ -0,0 +1,475 @@ +/**************************************************************************** +** +** Copyright (C) 2021 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 Process = require("qbs.Process"); +var TemporaryDir = require("qbs.TemporaryDir"); +var TextFile = require("qbs.TextFile"); + +function compilerName(qbs) { + var architecture = qbs.architecture; + if (architecture.startsWith("arm")) + return "cxcorm"; + throw "Unable to deduce compiler name for unsupported architecture: '" + + architecture + "'"; +} + +function assemblerName(qbs) { + var architecture = qbs.architecture; + if (architecture.startsWith("arm")) + return "cacorm"; + throw "Unable to deduce assembler name for unsupported architecture: '" + + architecture + "'"; +} + +function linkerName(qbs) { + var architecture = qbs.architecture; + if (architecture.startsWith("arm")) + return "clnk"; + throw "Unable to deduce linker name for unsupported architecture: '" + + architecture + "'"; +} + +function listerName(qbs) { + var architecture = qbs.architecture; + if (architecture.startsWith("arm")) + return "clabs"; + throw "Unable to deduce lister name for unsupported architecture: '" + + architecture + "'"; +} + +function archiverName(qbs) { + var architecture = qbs.architecture; + if (architecture.startsWith("arm")) + return "clib"; + throw "Unable to deduce archiver name for unsupported architecture: '" + + architecture + "'"; +} + +function staticLibrarySuffix(qbs) { + var architecture = qbs.architecture; + if (architecture.startsWith("arm")) + return ".cxm"; + throw "Unable to deduce static library suffix for unsupported architecture: '" + + architecture + "'"; +} + +function executableSuffix(qbs) { + var architecture = qbs.architecture; + if (architecture.startsWith("arm")) + return ".cxm"; + throw "Unable to deduce executable suffix for unsupported architecture: '" + + architecture + "'"; +} + +function objectSuffix(qbs) { + var architecture = qbs.architecture; + if (architecture.startsWith("arm")) + return ".o"; + throw "Unable to deduce object file suffix for unsupported architecture: '" + + architecture + "'"; +} + +function imageFormat(qbs) { + var architecture = qbs.architecture; + if (architecture.startsWith("arm")) + return "cosmic"; + throw "Unable to deduce image format for unsupported architecture: '" + + architecture + "'"; +} + +function guessArchitecture(compilerFilePath) { + var baseName = FileInfo.baseName(compilerFilePath); + if (baseName === "cxcorm") + return "arm"; + throw "Unable to deduce architecture for unsupported compiler: '" + + baseName + "'"; +} + +function dumpMacros(compilerFilePath) { + // Note: The COSMIC 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: (""|"key"|"value"|""). + + var outputDirectory = new TemporaryDir(); + var outputFilePath = FileInfo.fromNativeSeparators(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) #var VALUE(var)"); + // The COSMIC compiler defines only one pre-defined macro + // (at least nothing is said about other macros in the documentation). + var keys = ["__CSMC__"]; + for (var i in keys) { + 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.exec(compilerFilePath, [outputFilePath], false); + File.remove(outputFilePath); + + var map = {}; + // COSMIC compiler use the errors output! + process.readStdErr().trim().split(/\r?\n/g).map(function(line) { + var match = line.match(/^#message \("(.+)" "(.+)"\)$/); + if (match) + map[match[1]] = match[2]; + }); + return map; +} + +function dumpVersion(compilerFilePath) { + var p = new Process(); + p.exec(compilerFilePath, ["-vers"]); + // COSMIC compiler use the errors output! + var output = p.readStdErr(); + var match = output.match(/^COSMIC.+V(\d+)\.?(\d+)\.?(\*|\d+)/); + if (match) { + var major = match[1] ? parseInt(match[1], 10) : 0; + var minor = match[2] ? parseInt(match[2], 10) : 0; + var patch = match[3] ? parseInt(match[3], 10) : 0; + return { major: major, minor: minor, patch: patch }; + } +} + +function guessEndianness(architecture) { + // There is no mention of supported endianness in the cosmic compiler. + if (architecture.startsWith("arm")) + return "big"; + throw "Unable to deduce endianness for unsupported architecture: '" + + architecture + "'"; +} + +function dumpDefaultPaths(compilerFilePath, architecture) { + var rootPath = FileInfo.path(compilerFilePath); + var includePaths = []; + if (architecture.startsWith("arm")) { + var includePath = FileInfo.joinPaths(rootPath, "hcorm"); + if (File.exists(includePath)) + includePaths.push(includePath); + } + + var libraryPaths = []; + var libraryPath = FileInfo.joinPaths(rootPath, "lib"); + if (File.exists(libraryPath)) + libraryPaths.push(libraryPath); + + return { + "includePaths": includePaths, + "libraryPaths": libraryPaths, + } +} + +function detectRelativePath(baseDirectory, filePath) { + if (FileInfo.isAbsolutePath(filePath)) + return FileInfo.relativePath(baseDirectory, filePath); + return filePath; +} + +function compilerFlags(project, product, input, outputs, explicitlyDependsOn) { + var args = []; + + // Up to 128 include files. + args = args.concat(Cpp.collectPreincludePaths(input).map(function(path) { + return input.cpp.preincludeFlag + detectRelativePath(product.buildDirectory, path); + })); + + // Defines. + args = args.concat(Cpp.collectDefines(input).map(function(define) { + return input.cpp.defineFlag + detectRelativePath(product.buildDirectory, define); + })); + + // Up to 128 include paths. + args = args.concat(Cpp.collectIncludePaths(input).map(function(path) { + return input.cpp.includeFlag + detectRelativePath(product.buildDirectory, path); + })); + args = args.concat(Cpp.collectSystemIncludePaths(input).map(function(path) { + return input.cpp.systemIncludeFlag + detectRelativePath(product.buildDirectory, path); + })); + + // Debug information flags. + if (input.cpp.debugInformation) + args.push("+debug"); + + var architecture = input.qbs.architecture; + var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(outputs.obj[0].fileTags)); + + // Warning level flags. + switch (input.cpp.warningLevel) { + case "none": + // Disabled by default. + break; + case "all": + // Highest warning level. + args.push("-pw7"); + break; + } + + // C language version flags. + if (tag === "c") { + var knownValues = ["c99"]; + var cLanguageVersion = Cpp.languageVersion( + input.cpp.cLanguageVersion, knownValues, "C"); + switch (cLanguageVersion) { + case "c99": + args.push("-p", "c99"); + break; + default: + break; + } + } + + // Objects output directory. + args.push("-co", detectRelativePath(product.buildDirectory, + FileInfo.path(outputs.obj[0].filePath))); + + // Listing files generation flag. + if (input.cpp.generateCompilerListingFiles) { + // Enable listings. + args.push("-l"); + // Listings output directory. + args.push("-cl", detectRelativePath(product.buildDirectory, + FileInfo.path(outputs.lst[0].filePath))); + } + + // Misc flags. + args = args.concat(Cpp.collectMiscCompilerArguments(input, tag), + Cpp.collectMiscDriverArguments(product)); + + // Input. + args.push(detectRelativePath(product.buildDirectory, input.filePath)); + return args; +} + +function assemblerFlags(project, product, input, outputs, explicitlyDependsOn) { + var args = []; + + // Up to 128 include paths. + args = args.concat(Cpp.collectIncludePaths(input).map(function(path) { + return input.cpp.includeFlag + detectRelativePath(product.buildDirectory, path); + })); + args = args.concat(Cpp.collectSystemIncludePaths(input).map(function(path) { + return input.cpp.systemIncludeFlag + detectRelativePath(product.buildDirectory, path); + })); + + // Debug information flags. + if (input.cpp.debugInformation) + args.push("-xx"); + + // Determine which C-language we"re compiling + var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(outputs.obj[0].fileTags)); + + // Misc flags. + args = args.concat(Cpp.collectMiscAssemblerArguments(input, tag)); + + // Listing files generation flag. + if (input.cpp.generateAssemblerListingFiles) { + args.push("-l"); + args.push("+l", detectRelativePath(product.buildDirectory, outputs.lst[0].filePath)); + } + + // Objects output file path. + args.push("-o", detectRelativePath(product.buildDirectory, outputs.obj[0].filePath)); + + // Input. + args.push(detectRelativePath(product.buildDirectory, input.filePath)); + return args; +} + +function linkerFlags(project, product, inputs, outputs) { + var args = []; + + // Library paths. + args = args.concat(Cpp.collectLibraryPaths(product).map(function(path) { + return product.cpp.libraryPathFlag + detectRelativePath(product.buildDirectory, path); + })); + + // Output. + args.push("-o", detectRelativePath(product.buildDirectory, outputs.application[0].filePath)); + + // Map file generation flag. + if (product.cpp.generateLinkerMapFile) + args.push("-m", detectRelativePath(product.buildDirectory, outputs.mem_map[0].filePath)); + + // Misc flags. + args = args.concat(Cpp.collectMiscEscapableLinkerArguments(product), + Cpp.collectMiscLinkerArguments(product), + Cpp.collectMiscDriverArguments(product)); + + // Linker scripts. + args = args.concat(Cpp.collectLinkerScriptPaths(inputs).map(function(path) { + return product.cpp.linkerScriptFlag + detectRelativePath(product.buildDirectory, path); + })); + + // Input objects. + args = args.concat(Cpp.collectLinkerObjectPaths(inputs).map(function(path) { + return detectRelativePath(product.buildDirectory, path); + })); + + // Library dependencies (order has matters). + args = args.concat(Cpp.collectLibraryDependencies(product).map(function(dep) { + return detectRelativePath(product.buildDirectory, dep.filePath); + })); + + return args; +} + +function archiverFlags(project, product, inputs, outputs) { + var args = ["-cl"]; + + // Output. + args.push(detectRelativePath(product.buildDirectory, outputs.staticlibrary[0].filePath)); + + // Input objects. + args = args.concat(Cpp.collectLinkerObjectPaths(inputs).map(function(path) { + return detectRelativePath(product.buildDirectory, path); + })); + + return args; +} + +function createPath(fullPath) { + var cmd = new JavaScriptCommand(); + cmd.fullPath = fullPath; + cmd.silent = true; + cmd.sourceCode = function() { + File.makePath(fullPath); + }; + return cmd; +} + +// It is a workaround to rename the generated object file to the desired name. +// Reason is that the Cosmic compiler always generates the object files in the +// format of 'module.o', but we expect it in flexible format, e.g. 'module.c.obj' +// or 'module.c.o' depending on the cpp.objectSuffix property. +function renameObjectFile(project, product, inputs, outputs, input, output) { + var object = outputs.obj[0]; + var cmd = new JavaScriptCommand(); + cmd.newObject = object.filePath; + cmd.oldObject = FileInfo.joinPaths(FileInfo.path(object.filePath), + object.baseName + ".o"); + cmd.silent = true; + cmd.sourceCode = function() { File.move(oldObject, newObject); }; + return cmd; +} + +// It is a workaround to rename the generated listing file to the desired name. +// Reason is that the Cosmic compiler always generates the listing files in the +// format of 'module.ls', but we expect it in flexible format, e.g. 'module.c.lst' +// or 'module.c.ls' depending on the cpp.compilerListingSuffix property. +function renameListingFile(project, product, inputs, outputs, input, output) { + var listing = outputs.lst[0]; + var cmd = new JavaScriptCommand(); + cmd.newListing = listing.filePath; + cmd.oldListing = FileInfo.joinPaths(FileInfo.path(listing.filePath), + listing.baseName + ".ls"); + cmd.silent = true; + cmd.sourceCode = function() { File.move(oldListing, newListing); }; + return cmd; +} + +function prepareCompiler(project, product, inputs, outputs, input, output, explicitlyDependsOn) { + var cmds = []; + + // Create output objects path, because the Cosmic doesn't do it. + var cmd = createPath(FileInfo.path(outputs.obj[0].filePath)); + cmds.push(cmd); + + // Create output listing path, because the Cosmic doesn't do it. + if (input.cpp.generateCompilerListingFiles) { + cmd = createPath(FileInfo.path(outputs.lst[0].filePath)); + cmds.push(cmd); + } + + var args = compilerFlags(project, product, input, outputs, explicitlyDependsOn); + cmd = new Command(input.cpp.compilerPath, args); + cmd.workingDirectory = product.buildDirectory; + cmd.description = "compiling " + input.fileName; + cmd.highlight = "compiler"; + cmds.push(cmd); + + cmds.push(renameObjectFile(project, product, inputs, outputs, input, output)); + + if (input.cpp.generateCompilerListingFiles) + cmds.push(renameListingFile(project, product, inputs, outputs, input, output)); + + return cmds; +} + +function prepareAssembler(project, product, inputs, outputs, input, output, explicitlyDependsOn) { + var cmds = []; + + // Create output objects path, because the Cosmic doesn't do it. + var cmd = createPath(FileInfo.path(outputs.obj[0].filePath)); + cmds.push(cmd); + + // Create output listing path, because the Cosmic doesn't do it. + if (input.cpp.generateCompilerListingFiles) { + cmd = createPath(FileInfo.path(outputs.lst[0].filePath)); + cmds.push(cmd); + } + + var args = assemblerFlags(project, product, input, outputs, explicitlyDependsOn); + cmd = new Command(input.cpp.assemblerPath, args); + cmd.workingDirectory = product.buildDirectory; + cmd.description = "assembling " + input.fileName; + cmd.highlight = "compiler"; + cmds.push(cmd); + return cmds; +} + +function prepareLinker(project, product, inputs, outputs, input, output) { + var primaryOutput = outputs.application[0]; + var args = linkerFlags(project, product, inputs, outputs); + var cmd = new Command(product.cpp.linkerPath, args); + cmd.workingDirectory = product.buildDirectory; + cmd.description = "linking " + primaryOutput.fileName; + cmd.highlight = "linker"; + return [cmd]; +} + +function prepareArchiver(project, product, inputs, outputs, input, output) { + var args = archiverFlags(project, product, inputs, outputs); + var cmd = new Command(product.cpp.archiverPath, args); + cmd.workingDirectory = product.buildDirectory; + cmd.description = "linking " + output.fileName; + cmd.highlight = "linker"; + return [cmd]; +} diff --git a/share/qbs/modules/cpp/cosmic.qbs b/share/qbs/modules/cpp/cosmic.qbs new file mode 100644 index 000000000..cbcf06af7 --- /dev/null +++ b/share/qbs/modules/cpp/cosmic.qbs @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2021 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.PathTools +import qbs.Probes +import qbs.Utilities +import "cosmic.js" as COSMIC +import "cpp.js" as Cpp + +CppModule { + condition: qbs.toolchain && qbs.toolchain.contains("cosmic") + + Probes.BinaryProbe { + id: compilerPathProbe + condition: !toolchainInstallPath && !_skipAllChecks + names: ["cxcorm"] + } + + Probes.CosmicProbe { + id: cosmicProbe + condition: !_skipAllChecks + compilerFilePath: compilerPath + enableDefinesByLanguage: enableCompilerDefinesByLanguage + } + + qbs.architecture: cosmicProbe.found ? cosmicProbe.architecture : original + qbs.targetPlatform: "none" + + compilerVersionMajor: cosmicProbe.versionMajor + compilerVersionMinor: cosmicProbe.versionMinor + compilerVersionPatch: cosmicProbe.versionPatch + endianness: cosmicProbe.endianness + + compilerDefinesByLanguage: cosmicProbe.compilerDefinesByLanguage + compilerIncludePaths: cosmicProbe.includePaths + + toolchainInstallPath: compilerPathProbe.found ? compilerPathProbe.path : undefined + + property string compilerExtension: qbs.hostOS.contains("windows") ? ".exe" : "" + + /* Work-around for QtCreator which expects these properties to exist. */ + property string cCompilerName: compilerName + property string cxxCompilerName: compilerName + + compilerName: COSMIC.compilerName(qbs) + compilerExtension + compilerPath: FileInfo.joinPaths(toolchainInstallPath, compilerName) + + assemblerName: COSMIC.assemblerName(qbs) + compilerExtension + assemblerPath: FileInfo.joinPaths(toolchainInstallPath, assemblerName) + + linkerName: COSMIC.linkerName(qbs) + compilerExtension + linkerPath: FileInfo.joinPaths(toolchainInstallPath, linkerName) + + property string archiverName: COSMIC.archiverName(qbs) + compilerExtension + property string archiverPath: FileInfo.joinPaths(toolchainInstallPath, archiverName) + + runtimeLibrary: "static" + + staticLibrarySuffix: COSMIC.staticLibrarySuffix(qbs) + executableSuffix: COSMIC.executableSuffix(qbs) + + property string objectSuffix: COSMIC.objectSuffix(qbs) + + imageFormat: COSMIC.imageFormat(qbs) + + enableExceptions: false + enableRtti: false + + defineFlag: "-d" + includeFlag: "-i" + systemIncludeFlag: "-i" + preincludeFlag: "-ph" + libraryDependencyFlag: "" + libraryPathFlag: "-l" + linkerScriptFlag: "" + + Rule { + id: assembler + inputs: ["asm"] + outputFileTags: Cpp.compilerOutputTags(generateAssemblerListingFiles) + outputArtifacts: Cpp.compilerOutputArtifacts(input, false) + prepare: COSMIC.prepareAssembler.apply(COSMIC, arguments) + } + + FileTagger { + patterns: ["*.s"] + fileTags: ["asm"] + } + + Rule { + id: compiler + inputs: ["cpp", "c"] + auxiliaryInputs: ["hpp"] + outputFileTags: Cpp.compilerOutputTags(generateCompilerListingFiles) + outputArtifacts: Cpp.compilerOutputArtifacts(input, true) + prepare: COSMIC.prepareCompiler.apply(COSMIC, arguments) + } + + Rule { + id: applicationLinker + multiplex: true + inputs: ["obj", "linkerscript"] + outputFileTags: Cpp.applicationLinkerOutputTags(generateLinkerMapFile) + outputArtifacts: Cpp.applicationLinkerOutputArtifacts(product) + prepare: COSMIC.prepareLinker.apply(COSMIC, arguments) + } + + Rule { + id: staticLibraryLinker + multiplex: true + inputs: ["obj"] + outputFileTags: Cpp.staticLibraryLinkerOutputTags() + outputArtifacts: Cpp.staticLibraryLinkerOutputArtifacts(product) + prepare: COSMIC.prepareArchiver.apply(COSMIC, arguments) + } +} diff --git a/src/app/qbs-setup-toolchains/CMakeLists.txt b/src/app/qbs-setup-toolchains/CMakeLists.txt index d23221c73..212c2b5dc 100644 --- a/src/app/qbs-setup-toolchains/CMakeLists.txt +++ b/src/app/qbs-setup-toolchains/CMakeLists.txt @@ -3,6 +3,8 @@ set(SOURCES clangclprobe.h commandlineparser.cpp commandlineparser.h + cosmicprobe.cpp + cosmicprobe.h gccprobe.cpp gccprobe.h iarewprobe.cpp diff --git a/src/app/qbs-setup-toolchains/cosmicprobe.cpp b/src/app/qbs-setup-toolchains/cosmicprobe.cpp new file mode 100644 index 000000000..f7f66eef6 --- /dev/null +++ b/src/app/qbs-setup-toolchains/cosmicprobe.cpp @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Copyright (C) 2021 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 "probe.h" +#include "cosmicprobe.h" + +#include "../shared/logging/consolelogger.h" + +#include <logging/translator.h> + +#include <tools/hostosinfo.h> +#include <tools/profile.h> + +#include <QtCore/qprocess.h> +#include <QtCore/qregularexpression.h> +#include <QtCore/qsettings.h> +#include <QtCore/qstandardpaths.h> + +using namespace qbs; +using Internal::Tr; +using Internal::HostOsInfo; + +static QStringList knownCosmicCompilerNames() +{ + return {QStringLiteral("cxcorm")}; +} + +static QString guessCosmicArchitecture(const QFileInfo &compiler) +{ + const auto baseName = compiler.baseName(); + if (baseName == QLatin1String("cxcorm")) + return QStringLiteral("arm"); + return {}; +} + +static Profile createCosmicProfileHelper(const ToolchainInstallInfo &info, + Settings *settings, + QString profileName = QString()) +{ + const QFileInfo compiler = info.compilerPath; + const QString architecture = guessCosmicArchitecture(compiler); + + // In case the profile is auto-detected. + if (profileName.isEmpty()) { + if (!info.compilerVersion.isValid()) { + profileName = QStringLiteral("cosmic-unknown-%1").arg(architecture); + } else { + const QString version = info.compilerVersion.toString(QLatin1Char('_'), + QLatin1Char('_')); + profileName = QStringLiteral("cosmic-%1-%2").arg( + version, architecture); + } + } + + Profile profile(profileName, settings); + profile.setValue(QLatin1String("cpp.toolchainInstallPath"), compiler.absolutePath()); + profile.setValue(QLatin1String("qbs.toolchainType"), QLatin1String("cosmic")); + if (!architecture.isEmpty()) + profile.setValue(QLatin1String("qbs.architecture"), architecture); + + qbsInfo() << Tr::tr("Profile '%1' created for '%2'.").arg( + profile.name(), compiler.absoluteFilePath()); + return profile; +} + +static Version dumpCosmicCompilerVersion(const QFileInfo &compiler) +{ + QProcess p; + QStringList args; + p.start(compiler.absoluteFilePath(), args); + p.waitForFinished(3000); + const auto es = p.exitStatus(); + if (es != QProcess::NormalExit) { + const QByteArray out = p.readAll(); + qbsWarning() << Tr::tr("Compiler dumping failed:\n%1") + .arg(QString::fromUtf8(out)); + return Version{}; + } + + const QByteArray output = p.readAllStandardError(); + const QRegularExpression re(QLatin1String("^COSMIC.+V(\\d+)\\.?(\\d+)\\.?(\\*|\\d+)")); + const QRegularExpressionMatch match = re.match(QString::fromLatin1(output)); + if (!match.hasMatch()) + return Version{}; + + const auto major = match.captured(1).toInt(); + const auto minor = match.captured(2).toInt(); + const auto patch = match.captured(3).toInt(); + return Version{major, minor, patch}; +} + +static std::vector<ToolchainInstallInfo> installedCosmicsFromPath() +{ + std::vector<ToolchainInstallInfo> infos; + const auto compilerNames = knownCosmicCompilerNames(); + for (const QString &compilerName : compilerNames) { + const QFileInfo cosmicPath( + findExecutable( + HostOsInfo::appendExecutableSuffix(compilerName))); + if (!cosmicPath.exists()) + continue; + const Version version = dumpCosmicCompilerVersion(cosmicPath); + infos.push_back({cosmicPath, version}); + } + std::sort(infos.begin(), infos.end()); + return infos; +} + +bool isCosmicCompiler(const QString &compilerName) +{ + return Internal::any_of(knownCosmicCompilerNames(), [compilerName](const QString &knownName) { + return compilerName.contains(knownName); + }); +} + +void createCosmicProfile(const QFileInfo &compiler, Settings *settings, + QString profileName) +{ + const ToolchainInstallInfo info = {compiler, Version{}}; + createCosmicProfileHelper(info, settings, std::move(profileName)); +} + +void cosmicProbe(Settings *settings, std::vector<Profile> &profiles) +{ + qbsInfo() << Tr::tr("Trying to detect COSMIC toolchains..."); + + const std::vector<ToolchainInstallInfo> allInfos = installedCosmicsFromPath(); + for (const ToolchainInstallInfo &info : allInfos) { + const auto profile = createCosmicProfileHelper(info, settings); + profiles.push_back(profile); + } + + if (allInfos.empty()) + qbsInfo() << Tr::tr("No COSMIC toolchains found."); +} diff --git a/src/app/qbs-setup-toolchains/cosmicprobe.h b/src/app/qbs-setup-toolchains/cosmicprobe.h new file mode 100644 index 000000000..9b7b4bb10 --- /dev/null +++ b/src/app/qbs-setup-toolchains/cosmicprobe.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2021 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_COSMICPROBE_H +#define QBS_SETUPTOOLCHAINS_COSMICPROBE_H + +#include <QtCore/qlist.h> + +QT_BEGIN_NAMESPACE +class QFileInfo; +QT_END_NAMESPACE + +namespace qbs { +class Profile; +class Settings; +} + +bool isCosmicCompiler(const QString &compilerName); + +void createCosmicProfile(const QFileInfo &compiler, qbs::Settings *settings, + QString profileName); + +void cosmicProbe(qbs::Settings *settings, std::vector<qbs::Profile> &profiles); + +#endif // Header guard diff --git a/src/app/qbs-setup-toolchains/probe.cpp b/src/app/qbs-setup-toolchains/probe.cpp index efa5d9b78..27c96ec89 100644 --- a/src/app/qbs-setup-toolchains/probe.cpp +++ b/src/app/qbs-setup-toolchains/probe.cpp @@ -39,6 +39,7 @@ #include "probe.h" #include "clangclprobe.h" +#include "cosmicprobe.h" #include "gccprobe.h" #include "iarewprobe.h" #include "keilprobe.h" @@ -117,6 +118,8 @@ QString toolchainTypeFromCompilerName(const QString &compilerName) return QStringLiteral("keil"); if (isSdccCompiler(compilerName)) return QStringLiteral("sdcc"); + if (isCosmicCompiler(compilerName)) + return QStringLiteral("cosmic"); return {}; } @@ -136,6 +139,7 @@ void probe(Settings *settings) iarProbe(settings, profiles); keilProbe(settings, profiles); sdccProbe(settings, profiles); + cosmicProbe(settings, profiles); if (profiles.empty()) { qStderr << Tr::tr("Could not detect any toolchains. No profile created.") << Qt::endl; @@ -175,6 +179,8 @@ void createProfile(const QString &profileName, const QString &toolchainType, createKeilProfile(compiler, settings, profileName); else if (toolchain.contains(QLatin1String("sdcc"))) createSdccProfile(compiler, settings, profileName); + else if (toolchain.contains(QLatin1String("cosmic"))) + createCosmicProfile(compiler, settings, profileName); else throw qbs::ErrorInfo(Tr::tr("Cannot create profile: Unknown toolchain type.")); } diff --git a/src/app/qbs-setup-toolchains/qbs-setup-toolchains.pro b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.pro index 6581bec63..de7f55eb1 100644 --- a/src/app/qbs-setup-toolchains/qbs-setup-toolchains.pro +++ b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.pro @@ -5,6 +5,7 @@ TARGET = qbs-setup-toolchains HEADERS += \ clangclprobe.h \ commandlineparser.h \ + cosmicprobe.h \ gccprobe.h \ iarewprobe.h \ keilprobe.h \ @@ -16,6 +17,7 @@ HEADERS += \ SOURCES += \ clangclprobe.cpp \ commandlineparser.cpp \ + cosmicprobe.cpp \ gccprobe.cpp \ iarewprobe.cpp \ keilprobe.cpp \ diff --git a/src/app/qbs-setup-toolchains/qbs-setup-toolchains.qbs b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.qbs index 3bc72700f..3b4f2d737 100644 --- a/src/app/qbs-setup-toolchains/qbs-setup-toolchains.qbs +++ b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.qbs @@ -8,6 +8,8 @@ QbsApp { "clangclprobe.h", "commandlineparser.cpp", "commandlineparser.h", + "cosmicprobe.cpp", + "cosmicprobe.h", "gccprobe.cpp", "gccprobe.h", "iarewprobe.cpp", diff --git a/tests/auto/blackbox/testdata-baremetal/BareMetalApplication.qbs b/tests/auto/blackbox/testdata-baremetal/BareMetalApplication.qbs index a52bb4c4e..f23cf1a39 100644 --- a/tests/auto/blackbox/testdata-baremetal/BareMetalApplication.qbs +++ b/tests/auto/blackbox/testdata-baremetal/BareMetalApplication.qbs @@ -1,3 +1,9 @@ BareMetalProduct { type: "application" + + Group { + condition: qbs.toolchain.contains("cosmic") + files: "cosmic.lkf" + fileTags: "linkerscript" + } } 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 5e8bbd62d..bfd10106d 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 @@ -2,6 +2,8 @@ import "../BareMetalApplication.qbs" as BareMetalApplication Project { property bool supportsCpp: { + if (qbs.toolchain.contains("cosmic")) + return false; if (qbs.toolchain.contains("sdcc")) return false; if (qbs.toolchain.contains("keil")) { diff --git a/tests/auto/blackbox/testdata-baremetal/cosmic.lkf b/tests/auto/blackbox/testdata-baremetal/cosmic.lkf new file mode 100644 index 000000000..90c254d1e --- /dev/null +++ b/tests/auto/blackbox/testdata-baremetal/cosmic.lkf @@ -0,0 +1 @@ +@* diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/arm-cosmic.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/arm-cosmic.s new file mode 100644 index 000000000..4b45cc989 --- /dev/null +++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/arm-cosmic.s @@ -0,0 +1,5 @@ +_main: + movs r0, #0 + bx lr + xdef _main + end diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs index ed38f79b6..3eba70cab 100644 --- a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs +++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs @@ -2,7 +2,10 @@ import "../BareMetalApplication.qbs" as BareMetalApplication BareMetalApplication { condition: { - if (qbs.toolchainType === "keil") { + if (qbs.toolchainType === "cosmic") { + if (qbs.architecture.startsWith("arm")) + return true; + } else if (qbs.toolchainType === "keil") { if (qbs.architecture.startsWith("arm")) return true; if (qbs.architecture === "mcs51") diff --git a/tests/auto/blackbox/testdata-baremetal/target-platform/target-platform.qbs b/tests/auto/blackbox/testdata-baremetal/target-platform/target-platform.qbs index 50be8e91a..d29e7e619 100644 --- a/tests/auto/blackbox/testdata-baremetal/target-platform/target-platform.qbs +++ b/tests/auto/blackbox/testdata-baremetal/target-platform/target-platform.qbs @@ -3,7 +3,8 @@ Product { condition: { if (qbs.toolchainType === "keil" || qbs.toolchainType === "iar" - || qbs.toolchainType === "sdcc") { + || qbs.toolchainType === "sdcc" + || qbs.toolchainType === "cosmic") { var hasNoPlatform = (qbs.targetPlatform === "none"); var hasNoOS = (qbs.targetOS.length === 1 && qbs.targetOS[0] === "none"); console.info("has no platform: " + hasNoPlatform); |