diff options
author | Denis Shienkov <denis.shienkov@gmail.com> | 2019-01-30 19:28:03 +0300 |
---|---|---|
committer | Denis Shienkov <denis.shienkov@gmail.com> | 2019-02-15 16:21:46 +0000 |
commit | c825878fee22b0d44f87464819f0e8580b453261 (patch) | |
tree | 0f816ccf6125e27b650887eb57d9ae41ea764cd2 | |
parent | bceae1097f6a57f1339ea3a4e6827d6195860d31 (diff) |
bare-metal: Add KEIL ARM toolchain support for Windows
This commit adds a basic support of the KEIL toolchain for the
ARM processors family.
The KEIL toolchain support only the Windows environment:
* http://www.keil.com/support/docs/1456.htm
To use it with Qt Creator, it is enough to add there a desired Kit
with a custom KEIL C/C++ compiler, and then set the following in the
Kit's Qbs profile settings:
* Key: qbs.toolchainType
* Value: keil
Tested with the KEIL uVision v5.23 on Windows using the
STM32 NUCLEO-F767ZI development board.
Change-Id: I93a30f38f9b0e31bf4d1e379a3bdc785e8474ecb
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
-rw-r--r-- | share/qbs/imports/qbs/Probes/KeilProbe.qbs | 4 | ||||
-rw-r--r-- | share/qbs/modules/cpp/keil.js | 380 | ||||
-rw-r--r-- | share/qbs/modules/cpp/keil.qbs | 22 |
3 files changed, 293 insertions, 113 deletions
diff --git a/share/qbs/imports/qbs/Probes/KeilProbe.qbs b/share/qbs/imports/qbs/Probes/KeilProbe.qbs index bc31a4149..70712ab61 100644 --- a/share/qbs/imports/qbs/Probes/KeilProbe.qbs +++ b/share/qbs/imports/qbs/Probes/KeilProbe.qbs @@ -35,6 +35,8 @@ PathProbe { // Inputs property string compilerFilePath; + property string _nullDevice: qbs.nullDevice + // Outputs property string architecture; property string endianness; @@ -48,7 +50,7 @@ PathProbe { return; } - var macros = KEIL.dumpMacros(compilerFilePath, qbs); + var macros = KEIL.dumpMacros(compilerFilePath, qbs, _nullDevice); architecture = KEIL.guessArchitecture(macros); endianness = KEIL.guessEndianness(macros); diff --git a/share/qbs/modules/cpp/keil.js b/share/qbs/modules/cpp/keil.js index 3394cfc16..2228f1c5f 100644 --- a/share/qbs/modules/cpp/keil.js +++ b/share/qbs/modules/cpp/keil.js @@ -43,38 +43,45 @@ function guessArchitecture(macros) { if (macros["__C51__"]) return "mcs51"; + else if (macros["__CC_ARM"] === 1) + return "arm"; } function guessEndianness(macros) { - // The 8051 processors are 8-bit. So, the data with an integer type - // represented by more than one byte is stored as big endian in the - // Keil toolchain. See for more info: - // * http://www.keil.com/support/man/docs/c51/c51_ap_2bytescalar.htm - // * http://www.keil.com/support/man/docs/c51/c51_ap_4bytescalar.htm - return "big"; + if (macros["__C51__"]) { + // The 8051 processors are 8-bit. So, the data with an integer type + // represented by more than one byte is stored as big endian in the + // Keil toolchain. See for more info: + // * http://www.keil.com/support/man/docs/c51/c51_ap_2bytescalar.htm + // * http://www.keil.com/support/man/docs/c51/c51_ap_4bytescalar.htm + return "big"; + } else if (macros["__ARMCC_VERSION"]) { + return macros["__BIG_ENDIAN"] ? "big" : "little"; + } } function guessVersion(macros) { - var version = macros["__C51__"]; - if (version) { - versionMajor = parseInt(version / 100); - versionMinor = parseInt(version % 100); - versionPatch = 0; - return { major: versionMajor, - minor: versionMinor, - patch: versionPatch, + if (macros["__C51__"]) { + var version = macros["__C51__"]; + return { major: parseInt(version / 100), + minor: parseInt(version % 100), + patch: 0, + found: true } + } else if (macros["__CC_ARM"]) { + var version = macros["__ARMCC_VERSION"]; + return { major: parseInt(version / 1000000), + minor: parseInt(version / 10000) % 100, + patch: parseInt(version) % 10000, found: true } } } -function dumpMacros(compilerFilePath, qbs) { - - // Note: The KEIL compiler (at least for 8051) does not support the predefined - // macros dumping. So, we do it with following trick where we try to compile - // a temporary file and to parse the console output. - +// Note: The KEIL 8051 compiler does not support the predefined +// macros dumping. So, we do it with following trick where we try +// to compile a temporary file and to parse the console output. +function dumpC51CompilerMacros(compilerFilePath, qbs) { function createDumpMacrosFile() { var td = new TemporaryDir(); var fn = FileInfo.fromNativeSeparators(td.path() + "/dump-macros.c"); @@ -91,7 +98,7 @@ function dumpMacros(compilerFilePath, qbs) { var fn = createDumpMacrosFile(); var p = new Process(); - p.exec(compilerFilePath, [ fn ], true); + p.exec(compilerFilePath, [ fn ], false); var map = {}; p.readStdOut().trim().split(/\r?\n/g).map(function(line) { var parts = line.split("\"|\"", 3); @@ -100,6 +107,32 @@ function dumpMacros(compilerFilePath, qbs) { return map; } +function dumpArmCompilerMacros(compilerFilePath, qbs, nullDevice) { + var p = new Process(); + p.exec(compilerFilePath, + [ "-E", "--list-macros", nullDevice ], + false); + var map = {}; + p.readStdOut().trim().split(/\r?\n/g).map(function (line) { + if (!line.startsWith("#define")) + return; + var parts = line.split(" ", 3); + map[parts[1]] = parts[2]; + }); + return map; +} + +function dumpMacros(compilerFilePath, qbs, nullDevice) { + var map1 = dumpC51CompilerMacros(compilerFilePath, qbs); + var map2 = dumpArmCompilerMacros(compilerFilePath, qbs, nullDevice); + var map = {}; + for (var attrname in map1) + map[attrname] = map1[attrname]; + for (var attrname in map2) + map[attrname] = map2[attrname]; + return map; +} + function adjustPathsToWindowsSeparators(sourcePaths) { var resulingPaths = []; sourcePaths.forEach(function(path) { @@ -108,23 +141,11 @@ function adjustPathsToWindowsSeparators(sourcePaths) { return resulingPaths; } -function generateDefineDirective(allDefines) { - var adjusted = adjustPathsToWindowsSeparators(allDefines); - return "DEFINE (" + adjusted.join(",") + ")"; -} - -function generateIncludePathsDirective(allIncludePaths) { - var adjusted = adjustPathsToWindowsSeparators(allIncludePaths); - return "INCDIR (" + adjusted.join(";") + ")"; -} - -function generateObjectsLinkerDirective(allObjectPaths) { - var adjusted = adjustPathsToWindowsSeparators(allObjectPaths); - return adjusted.join(","); -} - -function generateObjectOutputDirective(outputFilePath) { - return "OBJECT (" + FileInfo.toWindowsSeparators(outputFilePath) + ")"; +function getMaxExitCode(architecture) { + if (architecture === "mcs51") + return 1; + else if (architecture === "arm") + return 0; } function collectLibraryDependencies(product) { @@ -186,7 +207,7 @@ function filterStdOutput(cmd) { cmd.stdoutFilterFunction = function(output) { // Allow only the error and warning messages // with its sub-content. - var sourceLines = output.split('\n'); + var sourceLines = output.split("\n"); var filteredLines = []; for (var i in sourceLines) { if (sourceLines[i].startsWith("***") @@ -195,40 +216,15 @@ function filterStdOutput(cmd) { filteredLines.push(sourceLines[i]); } } - return filteredLines.join('\n'); + return filteredLines.join("\n"); }; } function compilerFlags(project, product, input, output, explicitlyDependsOn) { // Determine which C-language we"re compiling. var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(output.fileTags)); - + var architecture = input.qbs.architecture; var args = []; - args.push(FileInfo.toWindowsSeparators(input.filePath)); - args.push(generateObjectOutputDirective(output.filePath)); - - switch (input.cpp.optimization) { - case "small": - args.push("OPTIMIZE (SIZE)"); - break; - case "fast": - args.push("OPTIMIZE (SPEED)"); - break; - case "none": - args.push("OPTIMIZE (0)"); - break; - } - - if (input.cpp.debugInformation) - args.push("DEBUG"); - - var warnings = input.cpp.warningLevel; - if (warnings === "none") { - args.push("WARNINGLEVEL (0)"); - } else if (warnings === "all") { - args.push("WARNINGLEVEL (2)"); - args.push("FARWARNING"); - } var allDefines = []; var platformDefines = input.cpp.platformDefines; @@ -237,8 +233,6 @@ function compilerFlags(project, product, input, output, explicitlyDependsOn) { var defines = input.cpp.defines; if (defines) allDefines = allDefines.uniqueConcat(defines); - if (allDefines.length > 0) - args = args.concat(generateDefineDirective(allDefines)); var allIncludePaths = []; var includePaths = input.cpp.includePaths; @@ -250,8 +244,93 @@ function compilerFlags(project, product, input, output, explicitlyDependsOn) { var compilerIncludePaths = input.cpp.compilerIncludePaths; if (compilerIncludePaths) allIncludePaths = allIncludePaths.uniqueConcat(compilerIncludePaths); - if (allIncludePaths.length > 0) - args = args.concat(generateIncludePathsDirective(allIncludePaths)); + + if (architecture === "mcs51") { + args.push(FileInfo.toWindowsSeparators(input.filePath)); + args.push("OBJECT (" + FileInfo.toWindowsSeparators(output.filePath) + ")"); + + switch (input.cpp.optimization) { + case "small": + args.push("OPTIMIZE (SIZE)"); + break; + case "fast": + args.push("OPTIMIZE (SPEED)"); + break; + case "none": + args.push("OPTIMIZE (0)"); + break; + } + + if (input.cpp.debugInformation) + args.push("DEBUG"); + + var warnings = input.cpp.warningLevel; + if (warnings === "none") { + args.push("WARNINGLEVEL (0)"); + } else if (warnings === "all") { + args.push("WARNINGLEVEL (2)"); + args.push("FARWARNING"); + } + + if (allDefines.length > 0) + args = args.concat("DEFINE (" + allDefines.join(",") + ")"); + + if (allIncludePaths.length > 0) { + var adjusted = adjustPathsToWindowsSeparators(allIncludePaths); + args = args.concat("INCDIR (" + adjusted.join(";") + ")"); + } + } else if (architecture === "arm") { + args.push("-c", input.filePath); + args.push("-o", output.filePath); + + switch (input.cpp.optimization) { + case "small": + args.push("-Ospace") + break; + case "fast": + args.push("-Otime") + break; + case "none": + args.push("-O0") + break; + } + + if (input.cpp.debugInformation) { + args.push("--debug"); + args.push("-g"); + } + + var warnings = input.cpp.warningLevel; + if (warnings === "none") { + args.push("-W"); + } else if (warnings === "all") { + // By default all warnings are enabled. + } + + if (tag === "c") { + // Note: Here we use the '==' operator because the '===' + // operator does not work! + if (input.cpp.cLanguageVersion == "c99") + args.push("--c99"); + } else if (tag === "cpp") { + args.push("--cpp"); + // Note: Here we use the '==' operator because the '===' + // operator does not work! + if (input.cpp.cxxLanguageVersion == "c++11") + args.push("--cpp11"); + + var enableExceptions = input.cpp.enableExceptions; + if (enableExceptions !== undefined) + args.push(enableExceptions ? "--exceptions" : "--no_exceptions"); + + var enableRtti = input.cpp.enableRtti; + if (enableRtti !== undefined) + args.push(enableRtti ? "--rtti" : "--no_rtti"); + } + + args = args.concat(allDefines.map(function(define) { return '-D' + define })); + args = args.concat(allIncludePaths.map(function(include) { return '-I' + include })); + } args = args.concat(ModUtils.moduleProperty(input, "platformFlags"), ModUtils.moduleProperty(input, "flags"), @@ -264,13 +343,8 @@ function compilerFlags(project, product, input, output, explicitlyDependsOn) { function assemblerFlags(project, product, input, output, explicitlyDependsOn) { // Determine which C-language we"re compiling var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(output.fileTags)); - + var architecture = input.qbs.architecture; var args = []; - args.push(FileInfo.toWindowsSeparators(input.filePath)); - args.push(generateObjectOutputDirective(output.filePath)); - - if (input.cpp.debugInformation) - args.push("DEBUG"); var allDefines = []; var platformDefines = input.cpp.platformDefines; @@ -279,8 +353,6 @@ function assemblerFlags(project, product, input, output, explicitlyDependsOn) { var defines = input.cpp.defines; if (defines) allDefines = allDefines.uniqueConcat(defines); - if (allDefines.length > 0) - args = args.concat(generateDefineDirective(allDefines)); var allIncludePaths = []; var includePaths = input.cpp.includePaths; @@ -292,8 +364,51 @@ function assemblerFlags(project, product, input, output, explicitlyDependsOn) { var compilerIncludePaths = input.cpp.compilerIncludePaths; if (compilerIncludePaths) allIncludePaths = allIncludePaths.uniqueConcat(compilerIncludePaths); - if (allIncludePaths.length > 0) - args = args.concat(generateIncludePathsDirective(allIncludePaths)); + + if (architecture === "mcs51") { + args.push(FileInfo.toWindowsSeparators(input.filePath)); + args.push("OBJECT (" + FileInfo.toWindowsSeparators(output.filePath) + ")"); + + if (input.cpp.debugInformation) + args.push("DEBUG"); + + if (allDefines.length > 0) + args = args.concat("DEFINE (" + allDefines.join(",") + ")"); + + if (allIncludePaths.length > 0) { + var adjusted = adjustPathsToWindowsSeparators(allIncludePaths); + args = args.concat("INCDIR (" + adjusted.join(";") + ")"); + } + } else if (architecture === "arm") { + args.push(input.filePath); + args.push("-o", output.filePath); + + if (input.cpp.debugInformation) { + args.push("--debug"); + args.push("-g"); + } + + var warnings = input.cpp.warningLevel; + if (warnings === "none") + args.push("--no_warn"); + + var endianness = input.cpp.endianness; + if (endianness) + args.push((endianness === "little") ? "--littleend" : "--bigend"); + + allDefines.forEach(function(define) { + var parts = define.split("="); + args.push("--pd"); + if (parts[1] === undefined) + args.push(parts[0] + " SETA " + 1); + else if (parts[1].contains("\"")) + args.push(parts[0] + " SETS " + parts[1]); + else + args.push(parts[0] + " SETA " + parts[1]); + }); + + args = args.concat(allIncludePaths.map(function(include) { return '-I' + include })); + } args = args.concat(ModUtils.moduleProperty(input, "platformFlags", tag), ModUtils.moduleProperty(input, "flags", tag), @@ -303,63 +418,104 @@ function assemblerFlags(project, product, input, output, explicitlyDependsOn) { function linkerFlags(project, product, input, outputs) { var args = []; - var allObjectPaths = []; + var architecture = product.qbs.architecture; - function addObjectPath(obj) { - allObjectPaths.push(obj.filePath); - } + if (architecture === "mcs51") { + var allObjectPaths = []; + function addObjectPath(obj) { + allObjectPaths.push(obj.filePath); + } - // Add all object files. - if (inputs.obj) - inputs.obj.map(function(obj) { addObjectPath(obj) }); + if (inputs.obj) + inputs.obj.map(function(obj) { addObjectPath(obj) }); - // Add all library dependencies. - var libraryDependencies = collectLibraryDependencies(product); - if (libraryDependencies) { - libraryDependencies.forEach(function(staticLibrary) { - addObjectPath(staticLibrary); - }) - } + var libraryDependencies = collectLibraryDependencies(product); + libraryDependencies.forEach(function(dep) { addObjectPath(dep); }) + + var adjusted = adjustPathsToWindowsSeparators(allObjectPaths); + args = args.concat(adjusted.join(",")); + + // We need to wrap an output file name with quotes. Otherwise + // the linker will ignore a specified file name. + args.push("TO", '"' + FileInfo.toWindowsSeparators(outputs.application[0].filePath) + '"'); - args = args.concat(generateObjectsLinkerDirective(allObjectPaths)); + if (!product.cpp.generateMapFile) + args.push("NOMAP"); + } else if (architecture === "arm") { + if (inputs.obj) + args = args.concat(inputs.obj.map(function(obj) { return obj.filePath })); - // We need to wrap a output file name with quotes. Otherwise - // the linker will ignore a specified file name. - args.push("TO", '"' + FileInfo.toWindowsSeparators(outputs.application[0].filePath) + '"'); + args.push("--output", outputs.application[0].filePath); - if (!product.cpp.generateMapFile) - args.push("NOMAP"); + if (product.cpp.generateMapFile) + args.push("--list", outputs.map_file[0].filePath); + + var libraryPaths = product.cpp.libraryPaths; + if (libraryPaths) + args.push("--userlibpath=" + libraryPaths.join(",")); + + var libraryDependencies = collectLibraryDependencies(product); + args = args.concat(libraryDependencies.map(function(dep) { return dep.filePath; })); + + var linkerScripts = inputs.linkerscript + ? inputs.linkerscript.map(function(a) { return a.filePath; }) : []; + for (i in linkerScripts) + args.push("--scatter", linkerScripts[i]); + + if (product.cpp.entryPoint) + args.push("--entry", product.cpp.entryPoint); + + var debugInformation = product.cpp.debugInformation; + if (debugInformation !== undefined) + args.push(debugInformation ? "--debug" : "--no_debug"); + } args = args.concat(ModUtils.moduleProperty(product, "driverLinkerFlags")); return args; } function archiverFlags(project, product, input, outputs) { - var args = [ "TRANSFER" ]; - var allObjectPaths = []; + var args = []; + var architecture = product.qbs.architecture; - function addObjectPath(obj) { - allObjectPaths.push(obj.filePath); - } + if (architecture === "mcs51") { + args.push("TRANSFER"); + var allObjectPaths = []; + + function addObjectPath(obj) { + allObjectPaths.push(obj.filePath); + } - if (inputs.obj) - inputs.obj.map(function(obj) { addObjectPath(obj) }); + if (inputs.obj) + inputs.obj.map(function(obj) { addObjectPath(obj) }); - // We need to wrap a output file name with quotes. Otherwise - // the linker will ignore a specified file name. - args = args.concat(generateObjectsLinkerDirective(allObjectPaths)); + var adjusted = adjustPathsToWindowsSeparators(allObjectPaths); + args = args.concat(adjusted.join(",")); + + // We need to wrap a output file name with quotes. Otherwise + // the linker will ignore a specified file name. + args.push("TO", '"' + FileInfo.toWindowsSeparators(outputs.staticlibrary[0].filePath) + '"'); + } else if (architecture === "arm") { + args.push("--create", outputs.staticlibrary[0].filePath); + + if (inputs.obj) + args = args.concat(inputs.obj.map(function(obj) { return obj.filePath })); + + if (product.cpp.debugInformation) + args.push("--debug_symbols"); + } - args.push("TO", '"' + FileInfo.toWindowsSeparators(outputs.staticlibrary[0].filePath) + '"'); return args; } function prepareCompiler(project, product, inputs, outputs, input, output, explicitlyDependsOn) { var args = compilerFlags(project, product, input, output, explicitlyDependsOn); var compilerPath = input.cpp.compilerPath; + var architecture = input.cpp.architecture; var cmd = new Command(compilerPath, args) cmd.description = "compiling " + input.fileName; cmd.highlight = "compiler"; - cmd.maxExitCode = 1; + cmd.maxExitCode = getMaxExitCode(architecture); filterStdOutput(cmd); return [cmd]; } diff --git a/share/qbs/modules/cpp/keil.qbs b/share/qbs/modules/cpp/keil.qbs index 371d0535d..dab37801a 100644 --- a/share/qbs/modules/cpp/keil.qbs +++ b/share/qbs/modules/cpp/keil.qbs @@ -80,6 +80,8 @@ CppModule { switch (qbs.architecture) { case "mcs51": return "c51" + compilerExtension; + case "arm": + return "armcc" + compilerExtension; } } compilerPath: FileInfo.joinPaths(toolchainInstallPath, compilerName) @@ -88,6 +90,8 @@ CppModule { switch (qbs.architecture) { case "mcs51": return "a51" + compilerExtension; + case "arm": + return "armasm" + compilerExtension; } } assemblerPath: FileInfo.joinPaths(toolchainInstallPath, assemblerName) @@ -96,6 +100,8 @@ CppModule { switch (qbs.architecture) { case "mcs51": return "bl51" + compilerExtension; + case "arm": + return "armlink" + compilerExtension; } } linkerPath: FileInfo.joinPaths(toolchainInstallPath, linkerName) @@ -104,6 +110,8 @@ CppModule { switch (qbs.architecture) { case "mcs51": return "lib51" + compilerExtension; + case "arm": + return "armar" + compilerExtension; } } property string archiverPath: FileInfo.joinPaths(toolchainInstallPath, archiverName) @@ -114,6 +122,8 @@ CppModule { switch (qbs.architecture) { case "mcs51": return ".lib"; + case "arm": + return ".lib"; } } @@ -121,6 +131,8 @@ CppModule { switch (qbs.architecture) { case "mcs51": return ".abs"; + case "arm": + return ".axf"; } } @@ -128,6 +140,8 @@ CppModule { switch (qbs.architecture) { case "mcs51": return ".obj"; + case "arm": + return ".o"; } } @@ -137,6 +151,8 @@ CppModule { // Keil OMF51 or OMF2 Object Module Format (which is an // extension of the original Intel OMF51). return "omf"; + case "arm": + return "elf"; } } @@ -162,6 +178,12 @@ CppModule { fileTags: ["asm"] } + FileTagger { + condition: qbs.architecture === "arm"; + patterns: "*.s" + fileTags: ["asm"] + } + Rule { id: compiler inputs: ["cpp", "c"] |