aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/main.yml3
-rw-r--r--doc/reference/modules/qbs-module.qdoc3
-rw-r--r--share/qbs/imports/qbs/Probes/CosmicProbe.qbs87
-rw-r--r--share/qbs/modules/cpp/cosmic.js475
-rw-r--r--share/qbs/modules/cpp/cosmic.qbs146
-rw-r--r--src/app/qbs-setup-toolchains/CMakeLists.txt2
-rw-r--r--src/app/qbs-setup-toolchains/cosmicprobe.cpp171
-rw-r--r--src/app/qbs-setup-toolchains/cosmicprobe.h61
-rw-r--r--src/app/qbs-setup-toolchains/probe.cpp6
-rw-r--r--src/app/qbs-setup-toolchains/qbs-setup-toolchains.pro2
-rw-r--r--src/app/qbs-setup-toolchains/qbs-setup-toolchains.qbs2
-rw-r--r--tests/auto/blackbox/testdata-baremetal/BareMetalApplication.qbs6
-rw-r--r--tests/auto/blackbox/testdata-baremetal/compiler-defines-by-language/compiler-defines-by-language.qbs2
-rw-r--r--tests/auto/blackbox/testdata-baremetal/cosmic.lkf1
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/arm-cosmic.s5
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs5
-rw-r--r--tests/auto/blackbox/testdata-baremetal/target-platform/target-platform.qbs3
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);