diff options
Diffstat (limited to 'share/qbs/modules/cpp/cosmic.js')
-rw-r--r-- | share/qbs/modules/cpp/cosmic.js | 475 |
1 files changed, 475 insertions, 0 deletions
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]; +} |