diff options
Diffstat (limited to 'share/qbs/modules/cpp')
35 files changed, 4908 insertions, 1969 deletions
diff --git a/share/qbs/modules/cpp/CppModule.qbs b/share/qbs/modules/cpp/CppModule.qbs index bdd6d2750..bd1eaa242 100644 --- a/share/qbs/modules/cpp/CppModule.qbs +++ b/share/qbs/modules/cpp/CppModule.qbs @@ -29,10 +29,11 @@ ****************************************************************************/ // base for Cpp modules +import qbs.FileInfo import qbs.ModUtils import qbs.Utilities import qbs.WindowsUtils - +import "cpp.js" as Cpp import "setuprunenv.js" as SetupRunEnv Module { @@ -128,7 +129,7 @@ Module { defaults will be used." } - property string minimumAndroidVersion + property string minimumAndroidVersion // not used, undocumented PropertyOptions { name: "minimumAndroidVersion" description: "a version number in the format [major].[minor] indicating the earliest \ @@ -136,7 +137,7 @@ Module { version which is then written to AndroidManifest.xml." } - property string maximumAndroidVersion + property string maximumAndroidVersion // not used, undocumented PropertyOptions { name: "maximumAndroidVersion" description: "a version number in the format [major].[minor] indicating the latest \ @@ -145,6 +146,12 @@ Module { be set." } + property string toolchainInstallPath + PropertyOptions { + name: "toolchainInstallPath" + description: "a path to the directory where the toolchain executable files are located." + } + property pathList includePaths property pathList systemIncludePaths property pathList distributionIncludePaths @@ -173,12 +180,19 @@ Module { property string executablePrefix: "" property string staticLibrarySuffix: "" property string dynamicLibrarySuffix: "" + property string archSuffix: "" property string loadableModuleSuffix: "" property string executableSuffix: "" property string debugInfoSuffix: "" property string debugInfoBundleSuffix: "" property string variantSuffix: "" property string dynamicLibraryImportSuffix: ".lib" + property string objectSuffix: ".o" + property string linkerMapSuffix: ".map" + property string compilerListingSuffix: ".lst" + property string assemblerListingSuffix: ".lst" + property string resourceSuffix: ".res" + property string precompiledHeaderSuffix property bool createSymlinks: true property stringList dynamicLibraries // list of names, will be linked with -lname property stringList staticLibraries // list of static library files @@ -193,6 +207,33 @@ Module { property bool discardUnusedData property bool removeDuplicateLibraries: true + property string defineFlag + property string includeFlag + property string systemIncludeFlag + property string preincludeFlag + property string libraryDependencyFlag + property string libraryPathFlag + property string linkerScriptFlag + + property stringList knownArchitectures: [] + property var toolchainDetails + property string compilerExtension: FileInfo.executableSuffix() + + property string linkerMode: "automatic" + PropertyOptions { + name: "linkerMode" + allowedValues: ["automatic", "manual"] + description: "Controls whether to automatically use an appropriate compiler frontend " + + "in place of the system linker when linking binaries. The default is \"automatic\", " + + "which chooses either the C++ compiler, C compiler, or system linker specified by " + + "the linkerName/linkerPath properties, depending on the type of object files " + + "present on the linker command line. \"manual\" allows you to explicitly specify " + + "the linker using the linkerName/linkerPath properties, and allows linker flags " + + "passed to the linkerFlags and platformLinkerFlags properties to be escaped " + + "manually (using -Wl or -Xlinker) instead of automatically based on the selected " + + "linker." + } + property stringList assemblerFlags PropertyOptions { name: "assemblerFlags" @@ -299,14 +340,12 @@ Module { property stringList cLanguageVersion PropertyOptions { name: "cLanguageVersion" - allowedValues: ["c89", "c99", "c11"] description: "The version of the C standard with which the code must comply." } property stringList cxxLanguageVersion PropertyOptions { name: "cxxLanguageVersion" - allowedValues: ["c++98", "c++11", "c++14", "c++17"] description: "The version of the C++ standard with which the code must comply." } @@ -369,13 +408,14 @@ Module { property bool combineObjcSources: false property bool combineObjcxxSources: false + // Those are set internally by different cpp module implementations property stringList targetAssemblerFlags property stringList targetDriverFlags property stringList targetLinkerFlags property bool _skipAllChecks: false // Internal - property bool validateTargetTriple: true + property bool validateTargetTriple: true // undocumented // TODO: The following four rules could use a convenience base item if rule properties // were available in Artifact items and prepare scripts. @@ -508,6 +548,13 @@ Module { return '"' + a + '"'; }).join(", ") + ". See https://docs.microsoft.com/en-us/windows/desktop/SysInfo/operating-system-version"); } + + if (knownArchitectures && knownArchitectures.length > 0) { + var isSupported = Cpp.supportsArchitecture(qbs.architecture, knownArchitectures); + if (!isSupported) { + throw ModUtils.ModuleError("Unsupported architecture: '" + qbs.architecture + "'"); + } + } } } diff --git a/share/qbs/modules/cpp/DarwinGCC.qbs b/share/qbs/modules/cpp/DarwinGCC.qbs index 9332603ec..e20973acc 100644 --- a/share/qbs/modules/cpp/DarwinGCC.qbs +++ b/share/qbs/modules/cpp/DarwinGCC.qbs @@ -43,7 +43,7 @@ import "gcc.js" as Gcc UnixGCC { condition: false - Depends { name: "xcode"; required: qbs.toolchain && qbs.toolchain.contains("xcode") } + Depends { name: "xcode"; required: qbs.toolchain && qbs.toolchain.includes("xcode") } Probes.BinaryProbe { id: lipoProbe @@ -76,7 +76,7 @@ UnixGCC { dynamicLibrarySuffix: ".dylib" Properties { - condition: product.multiplexByQbsProperties.contains("buildVariants") + condition: product.multiplexByQbsProperties.includes("buildVariants") && qbs.buildVariants && qbs.buildVariants.length > 1 && (!product.aggregate || !!product.multiplexConfigurationId) && qbs.buildVariant !== "release" @@ -98,7 +98,7 @@ UnixGCC { setupBuildEnvironment: { for (var key in product.cpp.buildEnv) { - v = new ModUtils.EnvironmentVariable(key); + var v = new ModUtils.EnvironmentVariable(key); v.value = product.cpp.buildEnv[key]; v.set(); } @@ -107,32 +107,42 @@ UnixGCC { property var defaultInfoPlist: { var dict = {}; - if (qbs.targetOS.contains("macos")) { + if (qbs.targetOS.includes("macos")) { dict["NSPrincipalClass"] = "NSApplication"; // needed for Retina display support + // QBS-1670: set this flag by default to avoid extensive GPU usage + dict["NSSupportsAutomaticGraphicsSwitching"] = true; + if (minimumMacosVersion) dict["LSMinimumSystemVersion"] = minimumMacosVersion; } + if (qbs.targetOS.includes("ios") && minimumIosVersion) + dict["MinimumOSVersion"] = minimumIosVersion; + else if (qbs.targetOS.includes("tvos") && minimumTvosVersion) + dict["MinimumOSVersion"] = minimumTvosVersion; + else if (qbs.targetOS.includes("watchos") && minimumWatchosVersion) + dict["MinimumOSVersion"] = minimumWatchosVersion; + if (qbs.targetOS.containsAny(["ios", "tvos"])) { dict["LSRequiresIPhoneOS"] = true; if (xcode.platformType === "device") { - if (qbs.targetOS.contains("ios")) { + if (qbs.targetOS.includes("ios")) { if (qbs.architecture === "arm64") dict["UIRequiredDeviceCapabilities"] = ["arm64"]; else dict["UIRequiredDeviceCapabilities"] = ["armv7"]; } - if (qbs.targetOS.contains("tvos")) + if (qbs.targetOS.includes("tvos")) dict["UIRequiredDeviceCapabilities"] = ["arm64"]; } } if (xcode.present) { var targetDevices = DarwinTools.targetedDeviceFamily(xcode.targetDevices); - if (qbs.targetOS.contains("ios")) + if (qbs.targetOS.includes("ios")) dict["UIDeviceFamily"] = targetDevices; if (qbs.targetOS.containsAny(["ios", "watchos"])) { @@ -143,13 +153,13 @@ UnixGCC { "UIInterfaceOrientationLandscapeRight" ]; - if (targetDevices.contains("ipad")) + if (targetDevices.includes("ipad")) dict["UISupportedInterfaceOrientations~ipad"] = orientations; - if (targetDevices.contains("watch")) + if (targetDevices.includes("watch")) dict["UISupportedInterfaceOrientations"] = orientations.slice(0, 2); - if (targetDevices.contains("iphone")) { + if (targetDevices.includes("iphone")) { orientations.splice(1, 1); dict["UISupportedInterfaceOrientations"] = orientations; } @@ -189,13 +199,13 @@ UnixGCC { // Set the corresponding environment variable even if the minimum OS version is undefined, // because this indicates the default deployment target for that OS - if (qbs.targetOS.contains("ios") && minimumIosVersion) + if (qbs.targetOS.includes("ios") && minimumIosVersion) env["IPHONEOS_DEPLOYMENT_TARGET"] = minimumIosVersion; - if (qbs.targetOS.contains("macos") && minimumMacosVersion) + if (qbs.targetOS.includes("macos") && minimumMacosVersion) env["MACOSX_DEPLOYMENT_TARGET"] = minimumMacosVersion; - if (qbs.targetOS.contains("watchos") && minimumWatchosVersion) + if (qbs.targetOS.includes("watchos") && minimumWatchosVersion) env["WATCHOS_DEPLOYMENT_TARGET"] = minimumWatchosVersion; - if (qbs.targetOS.contains("tvos") && minimumTvosVersion) + if (qbs.targetOS.includes("tvos") && minimumTvosVersion) env["TVOS_DEPLOYMENT_TARGET"] = minimumTvosVersion; if (xcode.present) @@ -208,7 +218,7 @@ UnixGCC { property string minimumDarwinVersionCompilerFlag property string minimumDarwinVersionLinkerFlag - property bool libcxxAvailable: qbs.toolchain.contains("clang") && cxxLanguageVersion !== "c++98" + property bool libcxxAvailable: qbs.toolchain.includes("clang") && cxxLanguageVersion !== "c++98" Rule { condition: enableAggregationRules @@ -216,7 +226,8 @@ UnixGCC { multiplex: true outputFileTags: ["bundle.input", "application", "primary", "debuginfo_app", - "debuginfo_bundle", "bundle.variant_symlink", "debuginfo_plist"] + "debuginfo_bundle", "bundle.variant_symlink", "debuginfo_plist", + "codesign.signed_artifact"] outputArtifacts: Darwin.lipoOutputArtifacts(product, inputs, "application", "app") prepare: Darwin.prepareLipo.apply(Darwin, arguments) @@ -227,7 +238,8 @@ UnixGCC { inputsFromDependencies: ["loadablemodule"] multiplex: true - outputFileTags: ["bundle.input", "loadablemodule", "primary", "debuginfo_loadablemodule"] + outputFileTags: ["bundle.input", "loadablemodule", "primary", "debuginfo_loadablemodule", + "debuginfo_bundle", "debuginfo_plist", "codesign.signed_artifact"] outputArtifacts: Darwin.lipoOutputArtifacts(product, inputs, "loadablemodule", "loadablemodule") @@ -241,7 +253,7 @@ UnixGCC { outputFileTags: ["bundle.input", "dynamiclibrary", "dynamiclibrary_symbols", "primary", "debuginfo_dll","debuginfo_bundle","bundle.variant_symlink", - "debuginfo_plist"] + "debuginfo_plist", "codesign.signed_artifact"] outputArtifacts: Darwin.lipoOutputArtifacts(product, inputs, "dynamiclibrary", "dll") prepare: Darwin.prepareLipo.apply(Darwin, arguments) @@ -252,14 +264,14 @@ UnixGCC { inputsFromDependencies: ["staticlibrary"] multiplex: true - outputFileTags: ["bundle.input", "staticlibrary", "primary"] + outputFileTags: ["bundle.input", "staticlibrary", "primary", "codesign.signed_artifact"] outputArtifacts: Darwin.lipoOutputArtifacts(product, inputs, "staticlibrary") prepare: Darwin.prepareLipo.apply(Darwin, arguments) } Rule { - condition: qbs.targetOS.contains("darwin") + condition: qbs.targetOS.includes("darwin") multiplex: true Artifact { diff --git a/share/qbs/modules/cpp/GenericGCC.qbs b/share/qbs/modules/cpp/GenericGCC.qbs index 63d5db7b8..f84028940 100644 --- a/share/qbs/modules/cpp/GenericGCC.qbs +++ b/share/qbs/modules/cpp/GenericGCC.qbs @@ -30,6 +30,7 @@ import qbs.File import qbs.FileInfo +import qbs.Host import qbs.ModUtils import qbs.PathTools import qbs.Probes @@ -38,12 +39,15 @@ import qbs.TextFile import qbs.Utilities import qbs.UnixUtils import qbs.WindowsUtils +import 'cpp.js' as Cpp import 'gcc.js' as Gcc CppModule { - condition: qbs.toolchain && qbs.toolchain.contains("gcc") + condition: qbs.toolchain && qbs.toolchain.includes("gcc") priority: -100 + Depends { name: "codesign" } + Probes.GccBinaryProbe { id: compilerPathProbe condition: !toolchainInstallPath && !_skipAllChecks @@ -114,9 +118,14 @@ CppModule { compilerFrameworkPaths: gccProbe.frameworkPaths compilerLibraryPaths: gccProbe.libraryPaths - property bool compilerHasTargetOption: qbs.toolchain.contains("clang") + staticLibraryPrefix: "lib" + staticLibrarySuffix: ".a" + + precompiledHeaderSuffix: ".gch" + + property bool compilerHasTargetOption: qbs.toolchain.includes("clang") && Utilities.versionCompare(compilerVersion, "3.1") >= 0 - property bool assemblerHasTargetOption: qbs.toolchain.contains("xcode") + property bool assemblerHasTargetOption: qbs.toolchain.includes("xcode") && Utilities.versionCompare(compilerVersion, "7") >= 0 property string target: targetArch ? [targetArch, targetVendor, targetSystem, targetAbi].join("-") @@ -131,8 +140,7 @@ CppModule { property string toolchainPrefix: compilerPathProbe.found ? compilerPathProbe.tcPrefix : undefined - property string toolchainInstallPath: compilerPathProbe.found ? compilerPathProbe.path - : undefined + toolchainInstallPath: compilerPathProbe.found ? compilerPathProbe.path : undefined property string binutilsPath: binutilsProbe.found ? binutilsProbe.path : toolchainInstallPath assemblerName: 'as' + compilerExtension @@ -148,21 +156,6 @@ CppModule { property string syslibroot: sysroot property stringList sysrootFlags: sysroot ? ["--sysroot=" + sysroot] : [] - property string linkerMode: "automatic" - PropertyOptions { - name: "linkerMode" - allowedValues: ["automatic", "manual"] - description: "Controls whether to automatically use an appropriate compiler frontend " - + "in place of the system linker when linking binaries. The default is \"automatic\", " - + "which chooses either the C++ compiler, C compiler, or system linker specified by " - + "the linkerName/linkerPath properties, depending on the type of object files " - + "present on the linker command line. \"manual\" allows you to explicitly specify " - + "the linker using the linkerName/linkerPath properties, and allows linker flags " - + "passed to the linkerFlags and platformLinkerFlags properties to be escaped " - + "manually (using -Wl or -Xlinker) instead of automatically based on the selected " - + "linker." - } - property string exportedSymbolsCheckMode: "ignore-undefined" PropertyOptions { name: "exportedSymbolsCheckMode" @@ -177,7 +170,7 @@ CppModule { property string linkerVariant PropertyOptions { name: "linkerVariant" - allowedValues: ["bfd", "gold", "lld"] + allowedValues: ["bfd", "gold", "lld", "mold"] description: "Allows to specify the linker variant. Maps to gcc's and clang's -fuse-ld " + "option." } @@ -189,10 +182,9 @@ CppModule { property string toolchainPathPrefix: Gcc.pathPrefix(toolchainInstallPath, toolchainPrefix) property string binutilsPathPrefix: Gcc.pathPrefix(binutilsPath, toolchainPrefix) - property string compilerExtension: qbs.hostOS.contains("windows") ? ".exe" : "" - property string cCompilerName: (qbs.toolchain.contains("clang") ? "clang" : "gcc") + property string cCompilerName: (qbs.toolchain.includes("clang") ? "clang" : "gcc") + compilerExtension - property string cxxCompilerName: (qbs.toolchain.contains("clang") ? "clang++" : "g++") + property string cxxCompilerName: (qbs.toolchain.includes("clang") ? "clang++" : "g++") + compilerExtension compilerPathByLanguage: ({ @@ -216,13 +208,22 @@ CppModule { property stringList dsymutilFlags property bool alwaysUseLipo: false - property string includeFlag: "-I" - property string systemIncludeFlag: "-isystem" + defineFlag: "-D" + includeFlag: "-I" + systemIncludeFlag: "-isystem" + preincludeFlag: "-include" + libraryPathFlag: "-L" + linkerScriptFlag: "-T" readonly property bool shouldCreateSymlinks: { - return createSymlinks && internalVersion && ["macho", "elf"].contains(cpp.imageFormat); + return createSymlinks && internalVersion && ["macho", "elf"].includes(imageFormat); } + readonly property bool shouldSignArtifacts: codesign._canSignArtifacts + && codesign.enableCodeSigning + // codesigning is done during the lipo step + && !product.multiplexed + property string internalVersion: { if (product.version === undefined) return undefined; @@ -250,18 +251,18 @@ CppModule { property var buildEnv: { var env = {}; - if (qbs.toolchain.contains("mingw")) - env.PATH = [toolchainInstallPath]; // For libwinpthread etc + if (qbs.toolchain.includes("mingw")) + env.PATH = toolchainInstallPath; // For libwinpthread etc return env; } exceptionHandlingModel: { - if (qbs.toolchain.contains("mingw")) { + if (qbs.toolchain.includes("mingw")) { // https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html claims // __USING_SJLJ_EXCEPTIONS__ is defined as 1 when using SJLJ exceptions, but there don't // seem to be defines for the other models, so use the presence of the DLLs for now. var prefix = toolchainInstallPath; - if (!qbs.hostOS.contains("windows")) + if (!Host.os().includes("windows")) prefix = FileInfo.joinPaths(toolchainInstallPath, "..", "lib", "gcc", toolchainPrefix, [compilerVersionMajor, compilerVersionMinor].join(".")); @@ -313,7 +314,7 @@ CppModule { if (gccProbe.targetPlatform) { // Can't differentiate Darwin OSes at the compiler level alone if (gccProbe.targetPlatform === "darwin" - ? !qbs.targetOS.contains("darwin") + ? !qbs.targetOS.includes("darwin") : qbs.targetPlatform !== gccProbe.targetPlatform) isWrongTriple = true; } else if (qbs.targetPlatform) { @@ -350,7 +351,7 @@ CppModule { var validateFlagsFunction = function (value) { if (value) { for (var i = 0; i < value.length; ++i) { - if (["-target", "-triple", "-arch"].contains(value[i])) + if (["-target", "-triple", "-arch"].includes(value[i])) return false; } } @@ -400,28 +401,33 @@ CppModule { condition: product.cpp.shouldLink multiplex: true inputs: { - var tags = ["obj", "linkerscript", "versionscript"]; + var tags = ["obj", "res", "linkerscript", "versionscript"]; if (product.bundle && product.bundle.embedInfoPlist - && product.qbs.targetOS.contains("darwin")) { + && product.qbs.targetOS.includes("darwin")) { tags.push("aggregate_infoplist"); } return tags; } inputsFromDependencies: ["dynamiclibrary_symbols", "staticlibrary", "dynamiclibrary_import"] - outputFileTags: [ - "bundle.input", - "dynamiclibrary", "dynamiclibrary_symlink", "dynamiclibrary_symbols", "debuginfo_dll", - "debuginfo_bundle","dynamiclibrary_import", "debuginfo_plist", - ] + outputFileTags: { + var tags = ["bundle.input", "dynamiclibrary", "dynamiclibrary_symlink", + "dynamiclibrary_symbols", "debuginfo_dll", "debuginfo_bundle", + "dynamiclibrary_import", "debuginfo_plist"]; + if (shouldSignArtifacts) + tags.push("codesign.signed_artifact"); + return tags; + } outputArtifacts: { var artifacts = [{ - filePath: product.destinationDirectory + "/" - + PathTools.dynamicLibraryFilePath(product), - fileTags: ["bundle.input", "dynamiclibrary"], + filePath: FileInfo.joinPaths(product.destinationDirectory, + PathTools.dynamicLibraryFilePath(product)), + fileTags: ["bundle.input", "dynamiclibrary"] + .concat(product.cpp.shouldSignArtifacts + ? ["codesign.signed_artifact"] : []), bundle: { - _bundleFilePath: product.destinationDirectory + "/" - + PathTools.bundleExecutableFilePath(product) + _bundleFilePath: FileInfo.joinPaths(product.destinationDirectory, + PathTools.bundleExecutableFilePath(product)) } }]; if (product.cpp.imageFormat === "pe") { @@ -445,9 +451,9 @@ CppModule { var maxVersionParts = product.cpp.internalVersion ? 3 : 1; for (var i = 0; i < maxVersionParts; ++i) { var symlink = { - filePath: product.destinationDirectory + "/" - + PathTools.dynamicLibraryFilePath(product, undefined, undefined, - i), + filePath: FileInfo.joinPaths(product.destinationDirectory, + PathTools.dynamicLibraryFilePath( + product, undefined, undefined, i)), fileTags: ["dynamiclibrary_symlink"] }; if (i > 0 && artifacts[i-1].filePath == symlink.filePath) @@ -460,16 +466,14 @@ CppModule { return artifacts; } - prepare: { - return Gcc.prepareLinker.apply(Gcc, arguments); - } + prepare: Gcc.prepareLinker.apply(Gcc, arguments) } Rule { name: "staticLibraryLinker" condition: product.cpp.shouldLink multiplex: true - inputs: ["obj", "linkerscript"] + inputs: ["obj", "res", "linkerscript"] inputsFromDependencies: ["dynamiclibrary_symbols", "dynamiclibrary_import", "staticlibrary"] outputFileTags: ["bundle.input", "staticlibrary", "c_staticlibrary", "cpp_staticlibrary"] @@ -479,9 +483,9 @@ CppModule { var objCount = objs ? objs.length : 0; for (var i = 0; i < objCount; ++i) { var ft = objs[i].fileTags; - if (ft.contains("c_obj")) + if (ft.includes("c_obj")) tags.push("c_staticlibrary"); - if (ft.contains("cpp_obj")) + if (ft.includes("cpp_obj")) tags.push("cpp_staticlibrary"); } return [{ @@ -499,6 +503,8 @@ CppModule { var args = ['rcs', output.filePath]; for (var i in inputs.obj) args.push(inputs.obj[i].filePath); + for (var i in inputs.res) + args.push(inputs.res[i].filePath); var cmd = new Command(product.cpp.archiverPath, args); cmd.description = 'creating ' + output.fileName; cmd.highlight = 'linker' @@ -513,22 +519,29 @@ CppModule { condition: product.cpp.shouldLink multiplex: true inputs: { - var tags = ["obj", "linkerscript"]; + var tags = ["obj", "res", "linkerscript"]; if (product.bundle && product.bundle.embedInfoPlist - && product.qbs.targetOS.contains("darwin")) { + && product.qbs.targetOS.includes("darwin")) { tags.push("aggregate_infoplist"); } return tags; } inputsFromDependencies: ["dynamiclibrary_symbols", "dynamiclibrary_import", "staticlibrary"] - outputFileTags: ["bundle.input", "loadablemodule", "debuginfo_loadablemodule", - "debuginfo_bundle","debuginfo_plist"] + outputFileTags: { + var tags = ["bundle.input", "loadablemodule", "debuginfo_loadablemodule", + "debuginfo_bundle", "debuginfo_plist"]; + if (shouldSignArtifacts) + tags.push("codesign.signed_artifact"); + return tags; + } outputArtifacts: { var app = { filePath: FileInfo.joinPaths(product.destinationDirectory, PathTools.loadableModuleFilePath(product)), - fileTags: ["bundle.input", "loadablemodule"], + fileTags: ["bundle.input", "loadablemodule"] + .concat(product.cpp.shouldSignArtifacts + ? ["codesign.signed_artifact"] : []), bundle: { _bundleFilePath: FileInfo.joinPaths(product.destinationDirectory, PathTools.bundleExecutableFilePath(product)) @@ -541,9 +554,7 @@ CppModule { return artifacts; } - prepare: { - return Gcc.prepareLinker.apply(Gcc, arguments); - } + prepare: Gcc.prepareLinker.apply(Gcc, arguments) } Rule { @@ -551,22 +562,30 @@ CppModule { condition: product.cpp.shouldLink multiplex: true inputs: { - var tags = ["obj", "linkerscript"]; + var tags = ["obj", "res", "linkerscript"]; if (product.bundle && product.bundle.embedInfoPlist - && product.qbs.targetOS.contains("darwin")) { + && product.qbs.targetOS.includes("darwin")) { tags.push("aggregate_infoplist"); } return tags; } inputsFromDependencies: ["dynamiclibrary_symbols", "dynamiclibrary_import", "staticlibrary"] - outputFileTags: ["bundle.input", "application", "debuginfo_app","debuginfo_bundle", - "debuginfo_plist", "mem_map"] + outputFileTags: { + var tags = ["bundle.input", "application", "debuginfo_app", "debuginfo_bundle", + "debuginfo_plist"]; + if (shouldSignArtifacts) + tags.push("codesign.signed_artifact"); + if (generateLinkerMapFile) + tags.push("mem_map"); + return tags; + } outputArtifacts: { var app = { filePath: FileInfo.joinPaths(product.destinationDirectory, PathTools.applicationFilePath(product)), - fileTags: ["bundle.input", "application"], + fileTags: ["bundle.input", "application"].concat( + product.cpp.shouldSignArtifacts ? ["codesign.signed_artifact"] : []), bundle: { _bundleFilePath: FileInfo.joinPaths(product.destinationDirectory, PathTools.bundleExecutableFilePath(product)) @@ -578,16 +597,14 @@ CppModule { if (product.cpp.generateLinkerMapFile) { artifacts.push({ filePath: FileInfo.joinPaths(product.destinationDirectory, - product.targetName + ".map"), + product.targetName + product.cpp.linkerMapSuffix), fileTags: ["mem_map"] }); } return artifacts; } - prepare: { - return Gcc.prepareLinker.apply(Gcc, arguments); - } + prepare: Gcc.prepareLinker.apply(Gcc, arguments) } Rule { @@ -595,94 +612,53 @@ CppModule { inputs: ["cpp", "c", "objcpp", "objc", "asm_cpp"] auxiliaryInputs: ["hpp"] explicitlyDependsOn: ["c_pch", "cpp_pch", "objc_pch", "objcpp_pch"] - - outputFileTags: ["obj", "c_obj", "cpp_obj", "intermediate_obj"] - outputArtifacts: { - var tags; - if (input.fileTags.contains("cpp_intermediate_object")) - tags = ["intermediate_obj"]; - else - tags = ["obj"]; - if (inputs.c || inputs.objc) - tags.push("c_obj"); - if (inputs.cpp || inputs.objcpp) - tags.push("cpp_obj"); - return [{ - fileTags: tags, - filePath: FileInfo.joinPaths(Utilities.getHash(input.baseDir), - input.fileName + ".o") - }]; - } - - prepare: { - return Gcc.prepareCompiler.apply(Gcc, arguments); - } + outputFileTags: Cpp.compilerOutputTags(false).concat(["c_obj", "cpp_obj"]) + outputArtifacts: Cpp.compilerOutputArtifacts(input, inputs) + prepare: Gcc.prepareCompiler.apply(Gcc, arguments) } Rule { name: "assembler" inputs: ["asm"] - - Artifact { - fileTags: ["obj"] - filePath: FileInfo.joinPaths(Utilities.getHash(input.baseDir), input.fileName + ".o") - } - - prepare: { - return Gcc.prepareAssembler.apply(Gcc, arguments); - } + outputFileTags: Cpp.assemblerOutputTags(false) + outputArtifacts: Cpp.assemblerOutputArtifacts(input) + prepare: Gcc.prepareAssembler.apply(Gcc, arguments) } Rule { condition: useCPrecompiledHeader inputs: ["c_pch_src"] auxiliaryInputs: ["hpp"] - Artifact { - filePath: product.name + "_c.gch" - fileTags: ["c_pch"] - } - prepare: { - return Gcc.prepareCompiler.apply(Gcc, arguments); - } + outputFileTags: Cpp.precompiledHeaderOutputTags("c", false) + outputArtifacts: Cpp.precompiledHeaderOutputArtifacts(input, product, "c", false) + prepare: Gcc.prepareCompiler.apply(Gcc, arguments) } Rule { condition: useCxxPrecompiledHeader inputs: ["cpp_pch_src"] auxiliaryInputs: ["hpp"] - Artifact { - filePath: product.name + "_cpp.gch" - fileTags: ["cpp_pch"] - } - prepare: { - return Gcc.prepareCompiler.apply(Gcc, arguments); - } + outputFileTags: Cpp.precompiledHeaderOutputTags("cpp", false) + outputArtifacts: Cpp.precompiledHeaderOutputArtifacts(input, product, "cpp", false) + prepare: Gcc.prepareCompiler.apply(Gcc, arguments) } Rule { condition: useObjcPrecompiledHeader inputs: ["objc_pch_src"] auxiliaryInputs: ["hpp"] - Artifact { - filePath: product.name + "_objc.gch" - fileTags: ["objc_pch"] - } - prepare: { - return Gcc.prepareCompiler.apply(Gcc, arguments); - } + outputFileTags: Cpp.precompiledHeaderOutputTags("objc", false) + outputArtifacts: Cpp.precompiledHeaderOutputArtifacts(input, product, "objc", false) + prepare: Gcc.prepareCompiler.apply(Gcc, arguments) } Rule { condition: useObjcxxPrecompiledHeader inputs: ["objcpp_pch_src"] auxiliaryInputs: ["hpp"] - Artifact { - filePath: product.name + "_objcpp.gch" - fileTags: ["objcpp_pch"] - } - prepare: { - return Gcc.prepareCompiler.apply(Gcc, arguments); - } + outputFileTags: Cpp.precompiledHeaderOutputTags("objcpp", false) + outputArtifacts: Cpp.precompiledHeaderOutputArtifacts(input, product, "objcpp", false) + prepare: Gcc.prepareCompiler.apply(Gcc, arguments) } FileTagger { diff --git a/share/qbs/modules/cpp/LinuxGCC.qbs b/share/qbs/modules/cpp/LinuxGCC.qbs index 14fb0a7e9..4b594a0aa 100644 --- a/share/qbs/modules/cpp/LinuxGCC.qbs +++ b/share/qbs/modules/cpp/LinuxGCC.qbs @@ -28,11 +28,12 @@ ** ****************************************************************************/ +import qbs.Host import qbs.Process UnixGCC { - condition: qbs.targetOS.contains('linux') && - qbs.toolchain && qbs.toolchain.contains('gcc') + condition: qbs.targetOS.includes('linux') && + qbs.toolchain && qbs.toolchain.includes('gcc') priority: 1 targetVendor: "pc" @@ -41,7 +42,7 @@ UnixGCC { Probe { id: runPathsProbe - condition: !_skipAllChecks && qbs.targetPlatform === qbs.hostPlatform + condition: !_skipAllChecks && qbs.targetPlatform === Host.platform() property stringList systemRunPaths: [] configure: { var paths = []; diff --git a/share/qbs/modules/cpp/MingwBaseModule.qbs b/share/qbs/modules/cpp/MingwBaseModule.qbs index 60ad28b08..c9cfe9bd8 100644 --- a/share/qbs/modules/cpp/MingwBaseModule.qbs +++ b/share/qbs/modules/cpp/MingwBaseModule.qbs @@ -37,8 +37,6 @@ import "setuprunenv.js" as SetupRunEnv GenericGCC { condition: false - staticLibraryPrefix: "lib" - staticLibrarySuffix: ".a" dynamicLibrarySuffix: ".dll" executableSuffix: ".exe" debugInfoSuffix: ".debug" @@ -46,9 +44,10 @@ GenericGCC { windowsApiCharacterSet: "unicode" platformDefines: base.concat(WindowsUtils.characterSetDefines(windowsApiCharacterSet)) .concat("WIN32") + runtimeLibrary: "dynamic" Properties { - condition: product.multiplexByQbsProperties.contains("buildVariants") + condition: product.multiplexByQbsProperties.includes("buildVariants") && qbs.buildVariants && qbs.buildVariants.length > 1 && qbs.buildVariant !== "release" && product.type.containsAny(["staticlibrary", "dynamiclibrary"]) @@ -97,9 +96,9 @@ GenericGCC { var tf; try { tf = new TextFile(outputFilePath, TextFile.WriteOnly); - if (productType.contains("application")) + if (productType.includes("application")) tf.write("1 "); // CREATEPROCESS_MANIFEST_RESOURCE_ID - else if (productType.contains("dynamiclibrary")) + else if (productType.includes("dynamiclibrary")) tf.write("2 "); // ISOLATIONAWARE_MANIFEST_RESOURCE_ID tf.write("24 "); // RT_MANIFEST tf.writeLine(Utilities.cStringQuote(inputFilePath)); diff --git a/share/qbs/modules/cpp/UnixGCC.qbs b/share/qbs/modules/cpp/UnixGCC.qbs index e5b99cd98..6f377c5c8 100644 --- a/share/qbs/modules/cpp/UnixGCC.qbs +++ b/share/qbs/modules/cpp/UnixGCC.qbs @@ -31,14 +31,12 @@ import qbs.File GenericGCC { - condition: qbs.toolchain && qbs.toolchain.contains("gcc") - && qbs.targetOS && qbs.targetOS.contains("unix") + condition: qbs.toolchain && qbs.toolchain.includes("gcc") + && qbs.targetOS.includes("unix") priority: -50 - staticLibraryPrefix: "lib" dynamicLibraryPrefix: "lib" loadableModulePrefix: "lib" - staticLibrarySuffix: ".a" dynamicLibrarySuffix: ".so" debugInfoSuffix: ".debug" imageFormat: "elf" diff --git a/share/qbs/modules/cpp/android-gcc.qbs b/share/qbs/modules/cpp/android-gcc.qbs index 3e44f4ff3..7c45d3c6b 100644 --- a/share/qbs/modules/cpp/android-gcc.qbs +++ b/share/qbs/modules/cpp/android-gcc.qbs @@ -39,25 +39,36 @@ import 'gcc.js' as Gcc LinuxGCC { Depends { name: "Android.ndk" } - condition: qbs.targetOS.contains("android") && qbs.toolchain && qbs.toolchain.contains("llvm") + condition: qbs.targetOS.includes("android") && qbs.toolchain && qbs.toolchain.includes("llvm") priority: 2 - rpaths: [rpathOrigin] + rpaths: [] + // toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/x86_64-linux-android cxxLanguageVersion: "c++14" - property string cxxStlBaseDir: FileInfo.joinPaths(Android.ndk.ndkDir, "sources", "cxx-stl") - property string stlBaseDir: FileInfo.joinPaths(cxxStlBaseDir, "llvm-libc++") - property string stlLibsDir: { - if (stlBaseDir) - return FileInfo.joinPaths(stlBaseDir, "libs", Android.ndk.abi); - return undefined; + property string archLibsDir: { + switch (qbs.architecture) { + case "arm64": + return "aarch64"; + case "armv7a": + return "arm"; + case "x86_64": + return qbs.architecture; + case "x86": + return "i686"; + } } + property string targetDir: "android" + (["armeabi", "armeabi-v7a"].includes(Android.ndk.abi) ? "eabi" : "") + property string triple: [archLibsDir, targetSystem, targetDir].join("-") + property string libsDir: FileInfo.joinPaths(sysroot, "usr", "lib", triple); - property string sharedStlFilePath: (stlLibsDir && Android.ndk.appStl.endsWith("_shared")) - ? FileInfo.joinPaths(stlLibsDir, dynamicLibraryPrefix + Android.ndk.appStl + dynamicLibrarySuffix) + property string sharedStlFilePath: (libsDir && Android.ndk.appStl.endsWith("_shared")) + ? FileInfo.joinPaths(libsDir, dynamicLibraryPrefix + Android.ndk.appStl + dynamicLibrarySuffix) : undefined - property string staticStlFilePath: (stlLibsDir && Android.ndk.appStl.endsWith("_static")) - ? FileInfo.joinPaths(stlLibsDir, NdkUtils.stlFilePath(staticLibraryPrefix, Android.ndk, staticLibrarySuffix)) + property string staticStlFilePath: (libsDir && Android.ndk.appStl.endsWith("_static")) + ? FileInfo.joinPaths(libsDir, Android.ndk.platformVersion, + NdkUtils.stlFileName(staticLibraryPrefix, Android.ndk, + staticLibrarySuffix)) : undefined Group { @@ -67,15 +78,6 @@ LinuxGCC { fileTags: "android.stl" } - Group { - name: "gdbserver" - condition: qbs.buildVariant !== "release" && product.cpp.shouldLink - files: FileInfo.joinPaths(Android.ndk.ndkDir, "prebuilt", - "android-" + NdkUtils.abiNameToDirName(Android.ndk.abi), - "gdbserver", "gdbserver") - fileTags: "android.gdbserver" - } - toolchainInstallPath: FileInfo.joinPaths(Android.ndk.ndkDir, "toolchains", "llvm", "prebuilt", Android.ndk.hostArch, "bin") @@ -83,11 +85,10 @@ LinuxGCC { property string toolchainTriple: [targetAbi === "androideabi" ? "arm" : targetArch, targetSystem, targetAbi].join("-") + internalVersion: undefined toolchainPrefix: undefined machineType: { - if (Android.ndk.abi === "armeabi") - return "armv5te"; if (Android.ndk.abi === "armeabi-v7a") return "armv7-a"; } @@ -99,59 +100,43 @@ LinuxGCC { commonCompilerFlags: NdkUtils.commonCompilerFlags(qbs.toolchain, qbs.buildVariant, Android.ndk) - linkerFlags: NdkUtils.commonLinkerFlags(Android.ndk.abi); + linkerFlags: NdkUtils.commonLinkerFlags(Android.ndk); driverLinkerFlags: { - var flags = ["-fuse-ld=lld", "-Wl,--exclude-libs,libgcc.a", "-Wl,--exclude-libs,libatomic.a", "-nostdlib++"]; - if (Android.ndk.appStl.startsWith("c++") && Android.ndk.abi === "armeabi-v7a") - flags = flags.concat(["-Wl,--exclude-libs,libunwind.a"]); + var flags = ["-fuse-ld=lld", "-Wl,--exclude-libs,libgcc.a", "-nostdlib++"]; + // See https://android.googlesource.com/platform/ndk/+/ndk-release-r21/docs/BuildSystemMaintainers.md#Unwinding + if (Android.ndk.abi === "armeabi-v7a") { + flags = flags.concat(["-Wl,--exclude-libs,libgcc_real.a"]); + if (Android.ndk.appStl.startsWith("c++")) + flags = flags.concat(["-Wl,--exclude-libs,libunwind.a"]); + } return flags; } platformDriverFlags: ["-fdata-sections", "-ffunction-sections", "-funwind-tables", "-fstack-protector-strong", "-no-canonical-prefixes"] - libraryPaths: { - var prefix = FileInfo.joinPaths(sysroot, "usr"); - var paths = []; - if (Android.ndk.abi === "x86_64") // no lib64 for arm64-v8a - paths.push(FileInfo.joinPaths(prefix, "lib64")); - paths.push(FileInfo.joinPaths(prefix, "lib")); - paths.push(stlLibsDir); - return paths; - } - dynamicLibraries: { var libs = ["c", "m"]; if (sharedStlFilePath) - libs.push(FileInfo.joinPaths(stlLibsDir, NdkUtils.stlFilePath(dynamicLibraryPrefix, Android.ndk, dynamicLibrarySuffix))); + libs.push(FileInfo.joinPaths(libsDir, Android.ndk.platformVersion, + NdkUtils.stlFileName(dynamicLibraryPrefix, Android.ndk, + dynamicLibrarySuffix))); return libs; } staticLibraries: staticStlFilePath - systemIncludePaths: { - var includes = [FileInfo.joinPaths(sysroot, "usr", "include", toolchainTriple)]; - if (Android.ndk.abi === "armeabi-v7a") { - includes.push(FileInfo.joinPaths(Android.ndk.ndkDir, "sources", "android", - "support", "include")); - } - includes.push(FileInfo.joinPaths(stlBaseDir, "include")); - includes.push(FileInfo.joinPaths(stlBaseDir + "abi", "include")); - return includes; - } - defines: { - var list = ["ANDROID"]; - // Might be superseded by an -mandroid-version or similar Clang compiler flag in future - list.push("__ANDROID_API__=" + Android.ndk.platformVersion); - return list; - } + // When using ndk r19c, llvm doesn't add sysroot/usr/include/c++/v1 to the path + // But it works starting with ndk r20b + systemIncludePaths: (Utilities.versionCompare(Android.ndk.version, "20") < 0) ? + FileInfo.joinPaths(sysroot, "usr", "include", "c++", "v1") : [] + + defines: ["ANDROID", "__ANDROID__"] binutilsPath: FileInfo.joinPaths(Android.ndk.ndkDir, "toolchains", "llvm", "prebuilt", Android.ndk.hostArch, "bin"); binutilsPathPrefix: FileInfo.joinPaths(binutilsPath, "llvm-") - syslibroot: FileInfo.joinPaths(Android.ndk.ndkDir, "platforms", - Android.ndk.platform, "arch-" - + NdkUtils.abiNameToDirName(Android.ndk.abi)) - sysroot: FileInfo.joinPaths(Android.ndk.ndkDir, "sysroot") + sysroot: FileInfo.joinPaths(Android.ndk.ndkDir, "toolchains", "llvm", "prebuilt", + Android.ndk.hostArch, "sysroot") targetArch: { switch (qbs.architecture) { @@ -168,9 +153,10 @@ LinuxGCC { } } - targetVendor: "none" + target: [targetArch, targetSystem, targetAbi].join("-") targetSystem: "linux" - targetAbi: "android" + (["armeabi", "armeabi-v7a"].contains(Android.ndk.abi) ? "eabi" : "") + targetAbi: "android" + (["armeabi", "armeabi-v7a"].includes(Android.ndk.abi) ? "eabi" : "") + + Android.ndk.platformVersion endianness: "little" @@ -182,9 +168,9 @@ LinuxGCC { fileTags: "android.nativelibrary" } prepare: { - var stripArgs = ["--strip-unneeded", "-o", output.filePath, input.filePath]; + var stripArgs = ["--strip-all", "-o", output.filePath, input.filePath]; var stripCmd = new Command(product.cpp.stripPath, stripArgs); - stripCmd.description = "Stripping unneeded symbols from " + input.fileName; + stripCmd.description = "stripping unneeded symbols from " + input.fileName; return stripCmd; } } diff --git a/share/qbs/modules/cpp/cosmic.js b/share/qbs/modules/cpp/cosmic.js new file mode 100644 index 000000000..4979f33f7 --- /dev/null +++ b/share/qbs/modules/cpp/cosmic.js @@ -0,0 +1,443 @@ +/**************************************************************************** +** +** 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 toolchainDetails(qbs) { + var architecture = qbs.architecture; + if (architecture.startsWith("arm")) { + return { + "executableSuffix": ".cxm", + "staticLibrarySuffix": ".cxm", + "assemblerName": "cacorm", + "compilerName": "cxcorm" + }; + } else if (architecture === "stm8") { + return { + "executableSuffix": ".sm8", + "staticLibrarySuffix": ".sm8", + "assemblerName": "castm8", + "compilerName": "cxstm8" + }; + } else if (architecture === "hcs8") { + return { + "executableSuffix": ".h08", + "staticLibrarySuffix": ".h08", + "assemblerName": "ca6808", + "compilerName": "cx6808" + }; + } else if (architecture === "hcs12") { + return { + "executableSuffix": ".h12", + "staticLibrarySuffix": ".h12", + "assemblerName": "ca6812", + "compilerName": "cx6812" + }; + } else if (architecture === "m68k") { + return { + "executableSuffix": ".332", + "staticLibrarySuffix": ".332", + "assemblerName": "ca332", + "compilerName": "cx332" + }; + } +} + +function guessArchitecture(compilerFilePath) { + var baseName = FileInfo.baseName(compilerFilePath); + if (baseName === "cxcorm") + return "arm"; + else if (baseName === "cxstm8") + return "stm8"; + else if (baseName === "cx6808") + return "hcs8"; + else if (baseName === "cx6812") + return "hcs12"; + else if (baseName === "cx332") + return "m68k"; +} + +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. + return "big"; +} + +function dumpDefaultPaths(compilerFilePath, architecture) { + var rootPath = FileInfo.path(compilerFilePath); + var includePath; + var includePaths = []; + if (architecture.startsWith("arm")) { + includePath = FileInfo.joinPaths(rootPath, "hcorm"); + if (File.exists(includePath)) + includePaths.push(includePath); + } else if (architecture === "stm8") { + includePath = FileInfo.joinPaths(rootPath, "hstm8"); + if (File.exists(includePath)) + includePaths.push(includePath); + } else if (architecture === "hcs8") { + includePath = FileInfo.joinPaths(rootPath, "h6808"); + if (File.exists(includePath)) + includePaths.push(includePath); + } else if (architecture === "hcs12") { + includePath = FileInfo.joinPaths(rootPath, "h6812"); + if (File.exists(includePath)) + includePaths.push(includePath); + } else if (architecture === "m68k") { + includePath = FileInfo.joinPaths(rootPath, "h332"); + 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 compilerFlags(project, product, input, outputs, explicitlyDependsOn) { + var args = []; + + // Up to 128 include files. + args = args.concat(Cpp.collectPreincludePathsArguments(input)); + + // Defines. + args = args.concat(Cpp.collectDefinesArguments(input)); + + // Up to 128 include paths. + args = args.concat(Cpp.collectIncludePathsArguments(input)); + args = args.concat(Cpp.collectSystemIncludePathsArguments(input)); + + // 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", 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", FileInfo.path(outputs.lst[0].filePath)); + } + + // Misc flags. + args = args.concat(Cpp.collectMiscCompilerArguments(input, tag), + Cpp.collectMiscDriverArguments(product)); + + // Input. + args.push(input.filePath); + return args; +} + +function assemblerFlags(project, product, input, outputs, explicitlyDependsOn) { + var args = []; + + // Up to 128 include paths. + args = args.concat(Cpp.collectIncludePathsArguments(input)); + args = args.concat(Cpp.collectSystemIncludePathsArguments(input)); + + // Debug information flags. + if (input.cpp.debugInformation) + args.push("-xx"); + + // Misc flags. + args = args.concat(Cpp.collectMiscAssemblerArguments(input, "asm")); + + // Listing files generation flag. + if (input.cpp.generateAssemblerListingFiles) { + args.push("-l"); + args.push("+l", outputs.lst[0].filePath); + } + + // Objects output file path. + args.push("-o", outputs.obj[0].filePath); + + // Input. + args.push(input.filePath); + return args; +} + +function linkerFlags(project, product, inputs, outputs) { + var args = []; + + // Library paths. + args = args.concat(Cpp.collectLibraryPaths(product).map(function(path) { + // It is workaround to use the relative paths avoids a strange linking + // errors. Maybe it is related to the limitations on the length of the + // command arguments, or on the length of the paths. + return product.cpp.libraryPathFlag + Cpp.relativePath(product.buildDirectory, path); + })); + + // Output. + args.push("-o", outputs.application[0].filePath); + + // Map file generation flag. + if (product.cpp.generateLinkerMapFile) + args.push("-m", 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.collectLinkerScriptPathsArguments(product, inputs)); + + // Input objects. + args = args.concat(Cpp.collectLinkerObjectPaths(inputs)); + + // Library dependencies (order has matters). + args = args.concat(Cpp.collectLibraryDependencies(product).map(function(dep) { + // It is workaround to use the relative paths avoids a strange linking + // errors. Maybe it is related to the limitations on the length of the + // command arguments, or on the length of the paths. + return Cpp.relativePath(product.buildDirectory, dep.filePath); + })); + + return args; +} + +function archiverFlags(project, product, inputs, outputs) { + var args = ["-cl"]; + + // Output. + args.push(outputs.staticlibrary[0].filePath); + + // Input objects. + args = args.concat(Cpp.collectLinkerObjectPaths(inputs)); + 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"; + cmd.jobPool = "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"; + cmd.jobPool = "assembler"; + 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"; + cmd.jobPool = "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 = "creating " + output.fileName; + cmd.highlight = "linker"; + cmd.jobPool = "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..8bf0f22a3 --- /dev/null +++ b/share/qbs/modules/cpp/cosmic.qbs @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** 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 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.includes("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 + + /* Work-around for QtCreator which expects these properties to exist. */ + property string cCompilerName: compilerName + property string cxxCompilerName: compilerName + + compilerName: toolchainDetails.compilerName + compilerExtension + compilerPath: FileInfo.joinPaths(toolchainInstallPath, compilerName) + + assemblerName: toolchainDetails.assemblerName + compilerExtension + assemblerPath: FileInfo.joinPaths(toolchainInstallPath, assemblerName) + + linkerName: "clnk" + compilerExtension + linkerPath: FileInfo.joinPaths(toolchainInstallPath, linkerName) + + property string archiverName: "clib" + compilerExtension + property string archiverPath: FileInfo.joinPaths(toolchainInstallPath, archiverName) + + runtimeLibrary: "static" + + staticLibrarySuffix: toolchainDetails.staticLibrarySuffix + executableSuffix: toolchainDetails.executableSuffix + + imageFormat: "cosmic" + + enableExceptions: false + enableRtti: false + + defineFlag: "-d" + includeFlag: "-i" + systemIncludeFlag: "-i" + preincludeFlag: "-ph" + libraryDependencyFlag: "" + libraryPathFlag: "-l" + linkerScriptFlag: "" + + toolchainDetails: COSMIC.toolchainDetails(qbs) + + knownArchitectures: ["arm", "hcs12", "hcs8", "m68k", "stm8"] + + Rule { + id: assembler + inputs: ["asm"] + outputFileTags: Cpp.assemblerOutputTags(generateAssemblerListingFiles) + outputArtifacts: Cpp.assemblerOutputArtifacts(input) + 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) + prepare: COSMIC.prepareCompiler.apply(COSMIC, arguments) + } + + Rule { + id: applicationLinker + multiplex: true + inputs: ["obj", "linkerscript"] + inputsFromDependencies: ["staticlibrary"] + outputFileTags: Cpp.applicationLinkerOutputTags(generateLinkerMapFile) + outputArtifacts: Cpp.applicationLinkerOutputArtifacts(product) + prepare: COSMIC.prepareLinker.apply(COSMIC, arguments) + } + + Rule { + id: staticLibraryLinker + multiplex: true + inputs: ["obj"] + inputsFromDependencies: ["staticlibrary"] + outputFileTags: Cpp.staticLibraryLinkerOutputTags() + outputArtifacts: Cpp.staticLibraryLinkerOutputArtifacts(product) + prepare: COSMIC.prepareArchiver.apply(COSMIC, arguments) + } +} diff --git a/share/qbs/modules/cpp/cpp.js b/share/qbs/modules/cpp/cpp.js index 0e440bdb0..9f907580a 100644 --- a/share/qbs/modules/cpp/cpp.js +++ b/share/qbs/modules/cpp/cpp.js @@ -28,6 +28,12 @@ ** ****************************************************************************/ +var File = require("qbs.File"); +var FileInfo = require("qbs.FileInfo"); +var ModUtils = require("qbs.ModUtils"); +var PathTools = require("qbs.PathTools"); +var Utilities = require("qbs.Utilities"); + function languageVersion(versionArray, knownValues, lang) { if (!versionArray) return undefined; @@ -36,7 +42,7 @@ function languageVersion(versionArray, knownValues, lang) { return versions[0]; for (var i = 0; i < knownValues.length; ++i) { var candidate = knownValues[i]; - if (versions.contains(candidate)) + if (versions.includes(candidate)) return candidate; } var version = versions[0]; @@ -44,3 +50,426 @@ function languageVersion(versionArray, knownValues, lang) { + "' from list of unknown " + lang + " version strings (" + versions + ")"); return version; } + +function extractMacros(output, regexp) { + var m = {}; + output.trim().split(/\r?\n/g).map(function(line) { + if (regexp) { + var match = regexp.exec(line); + if (!match) + return; + line = match[1]; + } + var prefix = "#define "; + if (!line.startsWith(prefix)) + return; + var index = line.indexOf(" ", prefix.length); + if (index !== -1) + m[line.substr(prefix.length, index - prefix.length)] = line.substr(index + 1); + }); + return m; +} + +function relativePath(baseDirectory, filePath) { + if (FileInfo.isAbsolutePath(filePath)) + return FileInfo.relativePath(baseDirectory, filePath); + return filePath; +} + +function assemblerOutputTags(needsListingFiles) { + var tags = ["obj"]; + if (needsListingFiles) + tags.push("lst"); + return tags; +} + +function compilerOutputTags(needsListingFiles) { + var tags = ["obj", "intermediate_obj"]; + if (needsListingFiles) + tags.push("lst"); + return tags; +} + +function applicationLinkerOutputTags(needsLinkerMapFile) { + var tags = ["application"]; + if (needsLinkerMapFile) + tags.push("mem_map"); + return tags; +} + +function dynamicLibraryLinkerOutputTags() { + return ["dynamiclibrary", "dynamiclibrary_import"]; +} + +function staticLibraryLinkerOutputTags() { + return ["staticlibrary"]; +} + +function resourceCompilerOutputTags() { + return ["res"]; +} + +function precompiledHeaderOutputTags(lang, generateObjects) { + var tags = [lang + "_pch"]; + if (generateObjects) + tags.push("obj"); + return tags; +}; + +function assemblerOutputArtifacts(input) { + var artifacts = []; + artifacts.push({ + fileTags: ["obj"], + filePath: FileInfo.joinPaths(Utilities.getHash(input.baseDir), + input.fileName + input.cpp.objectSuffix) + }); + if (input.cpp.generateAssemblerListingFiles) { + artifacts.push({ + fileTags: ["lst"], + filePath: FileInfo.joinPaths(Utilities.getHash(input.baseDir), + input.fileName + input.cpp.assemblerListingSuffix) + }); + } + return artifacts; +} + +function compilerOutputArtifacts(input, inputs) { + var objTags = input.fileTags.includes("cpp_intermediate_object") + ? ["intermediate_obj"] + : ["obj"]; + if (inputs) { + if (inputs.c || inputs.objc) + objTags.push("c_obj"); + if (inputs.cpp || inputs.objcpp) + objTags.push("cpp_obj"); + } + var artifacts = [{ + fileTags: objTags, + filePath: FileInfo.joinPaths(Utilities.getHash(input.baseDir), + input.fileName + input.cpp.objectSuffix) + }]; + if (input.cpp.generateCompilerListingFiles) { + artifacts.push({ + fileTags: ["lst"], + filePath: FileInfo.joinPaths(Utilities.getHash(input.baseDir), + input.fileName + input.cpp.compilerListingSuffix) + }); + } + return artifacts; +} + +function applicationLinkerOutputArtifacts(product) { + var artifacts = [{ + fileTags: ["application"], + filePath: FileInfo.joinPaths(product.destinationDirectory, + PathTools.applicationFilePath(product)) + }]; + if (product.cpp.generateLinkerMapFile) { + artifacts.push({ + fileTags: ["mem_map"], + filePath: FileInfo.joinPaths(product.destinationDirectory, + product.targetName + product.cpp.linkerMapSuffix) + }); + } + return artifacts; +} + +function dynamicLibraryLinkerOutputArtifacts(product) { + return [{ + fileTags: ["dynamiclibrary"], + filePath: FileInfo.joinPaths(product.destinationDirectory, + PathTools.dynamicLibraryFilePath(product)) + }, { + fileTags: ["dynamiclibrary_import"], + filePath: FileInfo.joinPaths(product.destinationDirectory, + PathTools.importLibraryFilePath(product)), + alwaysUpdated: false + }]; +} + +function staticLibraryLinkerOutputArtifacts(product) { + return [{ + fileTags: ["staticlibrary"], + filePath: FileInfo.joinPaths(product.destinationDirectory, + PathTools.staticLibraryFilePath(product)) + }]; +} + +function resourceCompilerOutputArtifacts(input) { + return [{ + fileTags: ["res"], + filePath: FileInfo.joinPaths(Utilities.getHash(input.baseDir), + input.completeBaseName + input.cpp.resourceSuffix) + }]; +} + +function precompiledHeaderOutputArtifacts(input, product, lang, generateObjects) { + var artifacts = [{ + fileTags: [lang + "_pch"], + filePath: product.name + "_" + lang + product.cpp.precompiledHeaderSuffix + }]; + if (generateObjects) { + artifacts.push({ + fileTags: ["obj"], + filePath: Utilities.getHash(input.completeBaseName) + + "_" + lang + input.cpp.objectSuffix + }); + } + return artifacts; +} + +function collectLibraryDependencies(product) { + var seen = {}; + var seenObjectFiles = []; + var result = []; + + function addFilePath(filePath, wholeArchive, productName) { + result.push({ filePath: filePath, wholeArchive: wholeArchive, productName: productName }); + } + + function addArtifactFilePaths(dep, artifacts) { + if (!artifacts) + return; + var artifactFilePaths = artifacts.map(function(a) { return a.filePath; }); + var wholeArchive = dep.parameters.cpp && dep.parameters.cpp.linkWholeArchive; + var artifactsAreImportLibs = artifacts.length > 0 + && artifacts[0].fileTags.includes("dynamiclibrary_import"); + for (var i = 0; i < artifactFilePaths.length; ++i) { + addFilePath(artifactFilePaths[i], wholeArchive, + artifactsAreImportLibs ? dep.name : undefined); + } + } + + function addExternalLibs(obj) { + if (!obj.cpp) + return; + function ensureArray(a) { + return (a instanceof Array) ? a : []; + } + function sanitizedModuleListProperty(obj, moduleName, propertyName) { + return ensureArray(ModUtils.sanitizedModuleProperty(obj, moduleName, propertyName)); + } + function handleExternalLibraries(tag, libSuffix, objSuffix) { + var externalLibs = sanitizedModuleListProperty(obj, "cpp", tag) || []; + externalLibs.forEach(function(libName) { + var isObjectFile = objSuffix && libName.endsWith(objSuffix); + if (isObjectFile) { + if (seenObjectFiles.includes(libName)) + return; + seenObjectFiles.push(libName); + } + if (!libName.endsWith(libSuffix) && !isObjectFile && !libName.startsWith('@')) + libName += libSuffix; + addFilePath(libName, false); + }); + } + handleExternalLibraries("staticLibraries", + obj.moduleProperty("cpp", "staticLibrarySuffix"), + obj.moduleProperty("cpp", "objectSuffix")); + handleExternalLibraries("dynamicLibraries", + obj.moduleProperty("cpp", "dynamicLibraryImportSuffix")); + } + + function traverse(dep) { + if (seen.hasOwnProperty(dep.name)) + return; + if (dep.parameters.cpp && dep.parameters.cpp.link === false) + return; + + var staticLibraryArtifacts = dep.artifacts["staticlibrary"]; + var dynamicLibraryArtifacts = staticLibraryArtifacts + ? null : dep.artifacts["dynamiclibrary_import"]; + if (staticLibraryArtifacts) { + seen[dep.name] = true; + dep.dependencies.forEach(traverse); + addArtifactFilePaths(dep, staticLibraryArtifacts); + addExternalLibs(dep); + } else if (dynamicLibraryArtifacts) { + seen[dep.name] = true; + addArtifactFilePaths(dep, dynamicLibraryArtifacts); + } + } + + product.dependencies.forEach(traverse); + addExternalLibs(product); + return result; +} + +function collectAbsoluteLibraryDependencyPaths(product) { + var paths = collectLibraryPaths(product); + var deps = collectLibraryDependencies(product); + return deps.map(function(dep) { + var filePath = dep.filePath; + if (FileInfo.isAbsolutePath(filePath)) + return filePath; + for (var i = 0; i < paths.length; ++i) { + var fullPath = FileInfo.joinPaths(paths[i], filePath); + if (File.exists(fullPath)) + return fullPath; + } + return filePath; + }); +} + +function collectDefines(input) { + var allDefines = []; + var platformDefines = input.cpp.platformDefines; + if (platformDefines) + allDefines = allDefines.uniqueConcat(platformDefines); + var defines = input.cpp.defines; + if (defines) + allDefines = allDefines.uniqueConcat(defines); + return allDefines; +} + +function collectIncludePaths(input) { + var allIncludePaths = []; + var includePaths = input.cpp.includePaths; + if (includePaths) + allIncludePaths = allIncludePaths.uniqueConcat(includePaths); + var builtIns = input.cpp.compilerIncludePaths; + return allIncludePaths.filter(function(p) { + return !builtIns.includes(p); + }); +} + +function collectSystemIncludePaths(input) { + var allIncludePaths = []; + var systemIncludePaths = input.cpp.systemIncludePaths; + if (systemIncludePaths) + allIncludePaths = allIncludePaths.uniqueConcat(systemIncludePaths); + var distributionIncludePaths = input.cpp.distributionIncludePaths; + if (distributionIncludePaths) + allIncludePaths = allIncludePaths.uniqueConcat(distributionIncludePaths); + var builtIns = input.cpp.compilerIncludePaths; + return allIncludePaths.filter(function(p) { + return !builtIns.includes(p); + }); +} + +function collectPreincludePaths(input) { + return input.cpp.prefixHeaders; +} + +function collectLibraryPaths(product) { + var allLibraryPaths = []; + var libraryPaths = product.cpp.libraryPaths; + if (libraryPaths) + allLibraryPaths = allLibraryPaths.uniqueConcat(libraryPaths); + var distributionLibraryPaths = product.cpp.distributionLibraryPaths; + if (distributionLibraryPaths) + allLibraryPaths = allLibraryPaths.uniqueConcat(distributionLibraryPaths); + return allLibraryPaths; +} + +function collectLinkerScriptPaths(inputs) { + return inputs.linkerscript + ? inputs.linkerscript.map(function(script) { return script.filePath; }) + : []; +} + +function collectLinkerObjectPaths(inputs) { + return inputs.obj ? inputs.obj.map(function(obj) { return obj.filePath; }) : []; +} + +function collectResourceObjectPaths(inputs) { + return inputs.res ? inputs.res.map(function(res) { return res.filePath; }) : []; +} + +function collectLibraryDependenciesArguments(product) { + var deps = collectLibraryDependencies(product); + return deps.map(function(dep) { return product.cpp.libraryDependencyFlag + dep.filePath }) +} + +function collectDefinesArguments(input) { + var defines = collectDefines(input); + return defines.map(function(define) { return input.cpp.defineFlag + define }); +} + +function collectIncludePathsArguments(input) { + var paths = collectIncludePaths(input); + return paths.map(function(path) { return input.cpp.includeFlag + path }); +} + +function collectSystemIncludePathsArguments(input, flag) { + flag = (flag === undefined) ? input.cpp.systemIncludeFlag : flag; + var paths = collectSystemIncludePaths(input); + return paths.map(function(path) { return flag + path }); +} + +function collectPreincludePathsArguments(input, split) { + var paths = collectPreincludePaths(input); + if (split) { + var args = []; + for (var i = 0; i < paths.length; ++i) + args.push(input.cpp.preincludeFlag, paths[i]); + return args; + } else { + return paths.map(function(path) { return input.cpp.preincludeFlag + path }); + } +} + +function collectLibraryPathsArguments(product, flag) { + flag = (flag === undefined) ? product.cpp.libraryPathFlag : flag; + var paths = collectLibraryPaths(product); + return paths.map(function(path) { return flag + path }); +} + +function collectLinkerScriptPathsArguments(product, inputs, split, flag) { + flag = (flag === undefined) ? product.cpp.linkerScriptFlag : flag; + var paths = collectLinkerScriptPaths(inputs); + if (split) { + var args = []; + for (var i = 0; i < paths.length; ++i) + args.push(flag, paths[i]); + return args; + } else { + return paths.map(function(path) { return flag + path }); + } +} + +function collectLinkerObjectPathsArguments(product, inputs, flag) { + flag = (flag === undefined) ? "" : flag; + var paths = collectLinkerObjectPaths(product); + return paths.map(function(path) { return flag + path }); +} + +function collectMiscCompilerArguments(input, tag) { + return [].concat(ModUtils.moduleProperty(input, "platformFlags"), + ModUtils.moduleProperty(input, "flags"), + ModUtils.moduleProperty(input, "platformFlags", tag), + ModUtils.moduleProperty(input, "flags", tag)); +} + +function collectMiscAssemblerArguments(input, tag) { + return [].concat(ModUtils.moduleProperty(input, "platformFlags", tag), + ModUtils.moduleProperty(input, "flags", tag)); +} + +function collectMiscDriverArguments(config) { + return [].concat(ModUtils.moduleProperty(config, "platformDriverFlags"), + ModUtils.moduleProperty(config, "driverFlags"), + ModUtils.moduleProperty(config, "targetDriverFlags")); +} + +function collectMiscLinkerArguments(product) { + return [].concat(ModUtils.moduleProperty(product, "driverLinkerFlags")); +} + +function collectMiscEscapableLinkerArguments(product) { + return [].concat(ModUtils.moduleProperty(product, "platformLinkerFlags"), + ModUtils.moduleProperty(product, "linkerFlags")); +} + +function supportsArchitecture(architecture, knownArchitectures) { + for (var i = 0; i < knownArchitectures.length; ++i) { + if (architecture.startsWith("arm")) { + if (architecture.startsWith(knownArchitectures[i])) + return true; + } else { + if (architecture === knownArchitectures[i]) + return true; + } + } + return false; +} diff --git a/share/qbs/modules/cpp/darwin.js b/share/qbs/modules/cpp/darwin.js index 6373b57c4..0223c5ed3 100644 --- a/share/qbs/modules/cpp/darwin.js +++ b/share/qbs/modules/cpp/darwin.js @@ -28,6 +28,7 @@ ** ****************************************************************************/ +var Codesign = require("../codesign/codesign.js"); var File = require("qbs.File"); var FileInfo = require("qbs.FileInfo"); var Gcc = require("./gcc.js"); @@ -72,7 +73,7 @@ function lipoOutputArtifacts(product, inputs, fileTag, debugSuffix) { // approach for all bundle types. var defaultVariant; if (!buildVariants.some(function (x) { return x.name === "release"; }) - && product.multiplexByQbsProperties.contains("buildVariants") + && product.multiplexByQbsProperties.includes("buildVariants") && product.qbs.buildVariants && product.qbs.buildVariants.length > 1) { var defaultBuildVariant = product.qbs.defaultBuildVariant; buildVariants.map(function (variant) { @@ -99,6 +100,9 @@ function lipoOutputArtifacts(product, inputs, fileTag, debugSuffix) { else tags.push(fileTag, "primary"); + if (product.codesign.enableCodeSigning) + tags.push("codesign.signed_artifact"); + return { filePath: FileInfo.joinPaths(product.destinationDirectory, PathTools.linkerOutputFilePath(fileTag, product, @@ -133,7 +137,7 @@ function prepareLipo(project, product, inputs, outputs, input, output) { for (var p in inputs) inputs[p] = inputs[p].filter(function(inp) { return inp.product.name === product.name; }); var allInputs = [].concat.apply([], Object.keys(inputs).map(function (tag) { - return ["application", "dynamiclibrary", "staticlibrary", "loadablemodule"].contains(tag) + return ["application", "dynamiclibrary", "staticlibrary", "loadablemodule"].includes(tag) ? inputs[tag] : []; })); @@ -168,26 +172,17 @@ function prepareLipo(project, product, inputs, outputs, input, output) { commands.push(cmd); } - var debugInfo = outputs.debuginfo_app || outputs.debuginfo_dll - || outputs.debuginfo_loadablemodule; - if (debugInfo) { - var dsymPath = debugInfo[0].filePath; - if (outputs.debuginfo_bundle && outputs.debuginfo_bundle[0]) - dsymPath = outputs.debuginfo_bundle[0].filePath; - var flags = ModUtils.moduleProperty(product, "dsymutilFlags") || []; - cmd = new Command(ModUtils.moduleProperty(product, "dsymutilPath"), flags.concat([ - "-o", dsymPath - ]).concat(outputs.primary.map(function (f) { return f.filePath; }))); - cmd.description = "generating dSYM for " + product.name; - commands.push(cmd); - } + commands = commands.concat( + Gcc.separateDebugInfoCommandsDarwin(product, outputs, outputs.primary)); - cmd = new Command(ModUtils.moduleProperty(product, "stripPath"), - ["-S", outputs.primary[0].filePath]); - cmd.silent = true; - commands.push(cmd); if (outputs.dynamiclibrary_symbols) Array.prototype.push.apply(commands, Gcc.createSymbolCheckingCommands(product, outputs)); + + if (product.codesign.enableCodeSigning) { + Array.prototype.push.apply( + commands, Codesign.prepareSign(project, product, inputs, outputs, input, output)); + } + return commands; } diff --git a/share/qbs/modules/cpp/dmc.js b/share/qbs/modules/cpp/dmc.js new file mode 100644 index 000000000..ea7cd7bb5 --- /dev/null +++ b/share/qbs/modules/cpp/dmc.js @@ -0,0 +1,506 @@ +/**************************************************************************** +** +** 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"); +var Utilities = require("qbs.Utilities"); + +function targetFlags(platform, architecture, extender, consoleApp, type) { + if (platform === "dos") { + if (architecture === "x86_16") { + if (extender === "dosz") + return ["-mz"]; + else if (extender === "dosr") + return ["-mr"]; + return ["-mc"]; + } else if (architecture === "x86") { + if (extender === "dosx") + return ["-mx"]; + else if (extender === "dosp") + return ["-mp"]; + } + } else if (platform === "windows") { + var flags = []; + if (architecture === "x86_16") { + flags.push("-ml"); + if (type.includes("application") && !consoleApp) + flags.push("-WA"); + else if (type.includes("dynamiclibrary")) + flags.push("-WD"); + } else if (architecture === "x86") { + flags.push("-mn"); + if (type.includes("application")) + flags.push("-WA"); + else if (type.includes("dynamiclibrary")) + flags.push("-WD"); + } + return flags; + } + return []; +} + +function languageFlags(tag) { + if (tag === "cpp") + return ["-cpp"]; + return []; +} + +function dumpMacros(compilerPath, platform, architecture, extender, tag) { + // Note: The DMC compiler does not support the predefined/ macros dumping. So, we do it + // with the following trick, where we try to create and compile a special temporary file + // and to parse the console output with the own magic pattern: #define <key> <value>. + + var outputDirectory = new TemporaryDir(); + var outputFilePath = FileInfo.joinPaths(outputDirectory.path(), "dump-macros.c"); + var outputFile = new TextFile(outputFilePath, TextFile.WriteOnly); + outputFile.writeLine("#define VALUE_TO_STRING(x) #x"); + outputFile.writeLine("#define VALUE(x) VALUE_TO_STRING(x)"); + outputFile.writeLine("#define VAR_NAME_VALUE(var) \"#define \" #var\" \"VALUE(var)"); + // Declare all available pre-defined macros of DMC compiler. + var keys = [ + // Prepare the DOS target macros. + "_MSDOS", "MSDOS", + // Prepare the OS/2 target macros. + "__OS2__", + // Prepare the Windows target macros. + "WIN32", "_WIN32", "__NT__", + // Prepare extended the 32 and 16 bit DOS target macros. + "DOS386", "DOS16RM", + // Prepare the memory model macros. + "M_I86", "_M_I86", + "_M_I86TM", "M_I86TM", + "_M_I86SM", "M_I86SM", + "_M_I86MM", "M_I86MM", + "_M_I86CM", "M_I86CM", + "_M_I86LM", "M_I86LM", + "_M_I86VM", "M_I86VM", + // Prepare 8086 macros. + "_M_I8086", "M_I8086", + // Prepare 286 macros. + "_M_I286", "M_I286", + // Prepare 32 bit macros. + "_M_IX86", + // Prepare compiler identification macros. + "__DMC__", "__DMC_VERSION_STRING__", + // Prepare common compiler macros. + "_CHAR_UNSIGNED", "_CHAR_EQ_UCHAR", "_DEBUG_TRACE", "_DLL", + "_ENABLE_ARRAYNEW", "_BOOL_DEFINED", "_WCHAR_T_DEFINED", + "_CPPRTTI", "_CPPUNWIND", "_MD", "_PUSHPOP_SUPPORTED", + "_STDCALL_SUPPORTED", "__INTSIZE", "__DEFALIGN", "_INTEGRAL_MAX_BITS", + "_WINDOWS", "_WINDLL", "__INLINE_8087", "__I86__", "__SMALL__", + "__MEDIUM__", "__COMPACT__", "__LARGE__", "__VCM__", "__FPCE__", + "__FPCE__IEEE__", "DEBUG", + // Prepare C99 and C++98 macros. + "__STDC__", "__STDC_HOSTED__", "__STDC_VERSION__", "__STDC_IEC_559__", + "__STDC_IEC_559_COMPLEX__", "__STDC_ISO_10646__", "__cplusplus" + ]; + for (var i = 0; i < keys.length; ++i) { + var key = keys[i]; + outputFile.writeLine("#if defined (" + key + ")"); + outputFile.writeLine("#pragma message (VAR_NAME_VALUE(" + key + "))"); + outputFile.writeLine("#endif"); + } + outputFile.close(); + + var process = new Process(); + process.setWorkingDirectory(outputDirectory.path()); + var lang = languageFlags(tag); + var target = targetFlags(platform, architecture, extender, false, ["application"]); + var args = ["-c"].concat(lang, target, FileInfo.toWindowsSeparators(outputFilePath)); + process.exec(compilerPath, args, false); + File.remove(outputFilePath); + var out = process.readStdOut(); + return Cpp.extractMacros(out); +} + +function dumpDefaultPaths(compilerFilePath, tag) { + var binPath = FileInfo.path(compilerFilePath); + var rootPath = FileInfo.path(binPath); + var includePaths = []; + var cppIncludePath = FileInfo.joinPaths(rootPath, "stlport/stlport"); + if (File.exists(cppIncludePath)) + includePaths.push(cppIncludePath); + var cIncludePath = FileInfo.joinPaths(rootPath, "include"); + if (File.exists(cIncludePath)) + includePaths.push(cIncludePath); + + var libraryPaths = []; + var libraryPath = FileInfo.joinPaths(rootPath, "lib"); + if (File.exists(libraryPath)) + libraryPaths.push(libraryPath); + + return { + "includePaths": includePaths, + "libraryPaths": libraryPaths, + } +} + +function guessVersion(macros) { + var version = macros["__DMC__"]; + return { major: parseInt(version / 100), + minor: parseInt(version % 100), + patch: 0 }; +} + +function effectiveLinkerPath(product) { + if (product.cpp.linkerMode === "automatic") { + var compilerPath = product.cpp.compilerPath; + if (compilerPath) + return compilerPath; + console.log("Found no C-language objects, choosing system linker for " + product.name); + } + return product.cpp.linkerPath; +} + +function useCompilerDriverLinker(product) { + var linker = effectiveLinkerPath(product); + var compilers = product.cpp.compilerPathByLanguage; + if (compilers) + return linker === compilers["cpp"] || linker === compilers["c"]; + return linker === product.cpp.compilerPath; +} + +function depsOutputTags() { + return ["dep"]; +} + +function depsOutputArtifacts(input, product) { + return [{ + fileTags: depsOutputTags(), + filePath: FileInfo.joinPaths(product.destinationDirectory, + input.baseName + ".dep") + }]; +} + +function compilerFlags(project, product, input, outputs, explicitlyDependsOn) { + var args = ["-c"]; + + var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(outputs.obj[0].fileTags)); + args = args.concat(languageFlags(tag)); + args = args.concat(targetFlags(product.qbs.targetPlatform, product.qbs.architecture, + product.cpp.extenderName, product.consoleApplication, + product.type)); + + // Input. + args.push(FileInfo.toWindowsSeparators(input.filePath)); + // Output. + args.push("-o" + FileInfo.toWindowsSeparators(outputs.obj[0].filePath)); + // Preinclude headers. + args = args.concat(Cpp.collectPreincludePaths(input).map(function(path) { + return input.cpp.preincludeFlag + FileInfo.toWindowsSeparators(path); + })); + + // Defines. + args = args.concat(Cpp.collectDefinesArguments(input)); + + if (tag === "cpp") { + // We need to add the paths to the STL includes, because the DMC compiler does + // not handle it by default (because the STL libraries is a separate port). + var compilerIncludePaths = input.cpp.compilerIncludePaths || []; + args = args.concat(compilerIncludePaths.map(function(path) { + return input.cpp.includeFlag + FileInfo.toWindowsSeparators(path); + })); + } + + // Other includes. + args = args.concat(Cpp.collectIncludePaths(input).map(function(path) { + return input.cpp.includeFlag + FileInfo.toWindowsSeparators(path); + })); + args = args.concat(Cpp.collectSystemIncludePaths(input).map(function(path) { + return input.cpp.systemIncludeFlag + FileInfo.toWindowsSeparators(path); + })); + + // Debug information flags. + if (input.cpp.debugInformation) + args.push("-d"); + + // Optimization flags. + switch (input.cpp.optimization) { + case "small": + args.push("-o+space"); + break; + case "fast": + args.push("-o+speed"); + break; + case "none": + args.push("-o+none"); + break; + } + + // Warning level flags. + switch (input.cpp.warningLevel) { + case "none": + args.push("-w"); + break; + case "all": + args.push("-w-"); + break; + } + if (input.cpp.treatWarningsAsErrors) + args.push("-wx"); + + if (tag === "cpp") { + // Exceptions flag. + if (input.cpp.enableExceptions) + args.push("-Ae"); + + // RTTI flag. + var enableRtti = input.cpp.enableRtti; + if (input.cpp.enableRtti) + args.push("-Ar"); + } + + // Listing files generation flag. + if (input.cpp.generateCompilerListingFiles) { + // We need to use the relative path here, because the DMC compiler does not handle + // a long file path for this option. + var listingPath = Cpp.relativePath(product.buildDirectory, outputs.lst[0].filePath); + args.push("-l" + FileInfo.toWindowsSeparators(listingPath)); + } + + // Misc flags. + args = args.concat(Cpp.collectMiscCompilerArguments(input, tag), + Cpp.collectMiscDriverArguments(product)); + return args; +} + +function assemblerFlags(project, product, input, outputs, explicitlyDependsOn) { + var args = ["-c"]; + + // Input. + args.push(FileInfo.toWindowsSeparators(input.filePath)); + // Output. + args.push("-o" + FileInfo.toWindowsSeparators(outputs.obj[0].filePath)); + // Preinclude headers. + args = args.concat(Cpp.collectPreincludePaths(input).map(function(path) { + return input.cpp.preincludeFlag + FileInfo.toWindowsSeparators(path); + })); + + // Defines. + args = args.concat(Cpp.collectDefinesArguments(input)); + + // Other includes. + args = args.concat(Cpp.collectIncludePaths(input).map(function(path) { + return input.cpp.includeFlag + FileInfo.toWindowsSeparators(path); + })); + args = args.concat(Cpp.collectSystemIncludePaths(input).map(function(path) { + return input.cpp.systemIncludeFlag + FileInfo.toWindowsSeparators(path); + })); + + // Misc flags. + args = args.concat(Cpp.collectMiscAssemblerArguments(input, "asm")); + return args; +} + +function linkerFlags(project, product, inputs, outputs) { + var args = []; + + var useCompilerDriver = useCompilerDriverLinker(product); + if (useCompilerDriver) { + args = args.concat(targetFlags(product.qbs.targetPlatform, product.qbs.architecture, + product.cpp.extenderName, product.consoleApplication, + product.type)); + + // Input objects. + args = args.concat(Cpp.collectLinkerObjectPaths(inputs).map(function(path) { + return FileInfo.toWindowsSeparators(path); + })); + + // Input resources. + args = args.concat(Cpp.collectResourceObjectPaths(inputs).map(function(path) { + return FileInfo.toWindowsSeparators(path); + })); + + // Input libraries. + args = args.concat(Cpp.collectAbsoluteLibraryDependencyPaths(product).map(function(path) { + return FileInfo.toWindowsSeparators(path); + })); + + // Output. + if (product.type.includes("application")) { + args.push("-o" + FileInfo.toWindowsSeparators(outputs.application[0].filePath)); + args.push("-L/" + (product.cpp.generateLinkerMapFile ? "MAP" : "NOMAP")); + if (product.qbs.targetPlatform === "windows" && product.qbs.architecture === "x86") + args.push("-L/SUBSYSTEM:" + (product.consoleApplication ? "CONSOLE" : "WINDOWS")); + } else if (product.type.includes("dynamiclibrary")) { + args.push("-o" + FileInfo.toWindowsSeparators(outputs.dynamiclibrary[0].filePath)); + if (product.qbs.targetPlatform === "windows" && product.qbs.architecture === "x86") { + args.push("kernel32.lib"); + args.push("-L/IMPLIB:" + FileInfo.toWindowsSeparators( + outputs.dynamiclibrary_import[0].filePath)); + } + } + + if (product.cpp.debugInformation) + args.push("-L/DEBUG"); + + args.push("-L/NOLOGO", "-L/SILENT"); + } + + // Misc flags. + args = args.concat(Cpp.collectMiscEscapableLinkerArguments(product), + Cpp.collectMiscLinkerArguments(product), + Cpp.collectMiscDriverArguments(product)); + return args; +} + +function archiverFlags(project, product, inputs, outputs) { + var args = ["-c"]; + // Output. + args.push(FileInfo.toWindowsSeparators(outputs.staticlibrary[0].filePath)); + // Input objects. + args = args.concat(Cpp.collectLinkerObjectPaths(inputs).map(function(path) { + return FileInfo.toWindowsSeparators(path); + })); + return args; +} + +function rccCompilerFlags(project, product, input, outputs) { + // Input. + var args = [FileInfo.toWindowsSeparators(input.filePath)]; + // Output. + args.push("-o" + FileInfo.toWindowsSeparators(outputs.res[0].filePath)); + // Bitness. + args.push("-32"); + + // Defines + args = args.concat(Cpp.collectDefinesArguments(input)); + + // Other includes. + args = args.concat(Cpp.collectIncludePaths(input).map(function(path) { + return input.cpp.includeFlag + FileInfo.toWindowsSeparators(path); + })); + args = args.concat(Cpp.collectSystemIncludePaths(input).map(function(path) { + return input.cpp.systemIncludeFlag + FileInfo.toWindowsSeparators(path); + })); + return args; +} + +function buildLinkerMapFilePath(target, suffix) { + return FileInfo.joinPaths(FileInfo.path(target.filePath), + FileInfo.completeBaseName(target.fileName) + suffix); +} + +// It is a workaround which removes the generated linker map file if it is disabled +// by cpp.generateLinkerMapFile property. Reason is that the DMC compiler always +// generates this file, and does not have an option to disable generation for a linker +// map file. So, we can to remove a listing files only after the linking completes. +function removeLinkerMapFile(project, product, inputs, outputs, input, output) { + var target = outputs.dynamiclibrary ? outputs.dynamiclibrary[0] + : outputs.application[0]; + var cmd = new JavaScriptCommand(); + cmd.mapFilePath = buildLinkerMapFilePath(target, ".map") + cmd.silent = true; + cmd.sourceCode = function() { File.remove(mapFilePath); }; + return cmd; +} + +// It is a workaround to rename the extension of the output linker map file to the +// specified one, since the linker generates only files with the '.map' extension. +function renameLinkerMapFile(project, product, inputs, outputs, input, output) { + var target = outputs.dynamiclibrary ? outputs.dynamiclibrary[0] + : outputs.application[0]; + var cmd = new JavaScriptCommand(); + cmd.newMapFilePath = buildLinkerMapFilePath(target, product.cpp.linkerMapSuffix); + cmd.oldMapFilePath = buildLinkerMapFilePath(target, ".map"); + cmd.silent = true; + cmd.sourceCode = function() { + if (oldMapFilePath !== newMapFilePath) + File.move(oldMapFilePath, newMapFilePath); + }; + return cmd; +} + +function prepareCompiler(project, product, inputs, outputs, input, output, explicitlyDependsOn) { + var args = compilerFlags(project, product, input, outputs, explicitlyDependsOn); + var cmd = new Command(input.cpp.compilerPath, args); + cmd.workingDirectory = product.buildDirectory; + cmd.description = "compiling " + input.fileName; + cmd.highlight = "compiler"; + cmd.jobPool = "compiler"; + return [cmd]; +} + +function prepareAssembler(project, product, inputs, outputs, input, output, explicitlyDependsOn) { + var args = assemblerFlags(project, product, input, outputs, explicitlyDependsOn); + var cmd = new Command(input.cpp.assemblerPath, args); + cmd.workingDirectory = product.buildDirectory; + cmd.description = "compiling " + input.fileName; + cmd.highlight = "compiler"; + cmd.jobPool = "assembler"; + return [cmd]; +} + +function prepareLinker(project, product, inputs, outputs, input, output) { + var cmds = []; + var primaryOutput = outputs.dynamiclibrary ? outputs.dynamiclibrary[0] + : outputs.application[0]; + var args = linkerFlags(project, product, inputs, outputs); + var linkerPath = effectiveLinkerPath(product); + var cmd = new Command(linkerPath, args); + cmd.workingDirectory = product.buildDirectory; + cmd.description = "linking " + primaryOutput.fileName; + cmd.highlight = "linker"; + cmd.jobPool = "linker"; + cmds.push(cmd); + + if (product.cpp.generateLinkerMapFile) + cmds.push(renameLinkerMapFile(project, product, inputs, outputs, input, output)); + else + cmds.push(removeLinkerMapFile(project, product, inputs, outputs, input, output)); + + return cmds; +} + +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 = "creating " + output.fileName; + cmd.highlight = "linker"; + cmd.jobPool = "linker"; + return [cmd]; +} + +function prepareRccCompiler(project, product, inputs, outputs, input, output) { + var args = rccCompilerFlags(project, product, input, outputs); + var cmd = new Command(input.cpp.rccCompilerPath, args); + cmd.workingDirectory = product.buildDirectory; + cmd.description = "compiling " + input.fileName; + cmd.highlight = "compiler"; + cmd.jobPool = "compiler"; + return [cmd]; +} diff --git a/share/qbs/modules/cpp/dmc.qbs b/share/qbs/modules/cpp/dmc.qbs new file mode 100644 index 000000000..ac89550e8 --- /dev/null +++ b/share/qbs/modules/cpp/dmc.qbs @@ -0,0 +1,194 @@ +/**************************************************************************** +** +** 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 qbs.FileInfo +import qbs.Host +import qbs.PathTools +import qbs.Probes +import qbs.Utilities +import "dmc.js" as DMC +import "cpp.js" as Cpp + +CppModule { + condition: Host.os().includes("windows") && qbs.toolchain && qbs.toolchain.includes("dmc") + + Probes.BinaryProbe { + id: compilerPathProbe + condition: !toolchainInstallPath && !_skipAllChecks + names: ["dmc"] + } + + Probes.DmcProbe { + id: dmcProbe + condition: !_skipAllChecks + compilerFilePath: compilerPath + enableDefinesByLanguage: enableCompilerDefinesByLanguage + _targetPlatform: qbs.targetPlatform + _targetArchitecture: qbs.architecture + _targetExtender: extenderName + } + + qbs.architecture: dmcProbe.found ? dmcProbe.architecture : original + qbs.targetPlatform: dmcProbe.found ? dmcProbe.targetPlatform : original + + compilerVersionMajor: dmcProbe.versionMajor + compilerVersionMinor: dmcProbe.versionMinor + compilerVersionPatch: dmcProbe.versionPatch + endianness: "little" + + compilerDefinesByLanguage: dmcProbe.compilerDefinesByLanguage + compilerIncludePaths: dmcProbe.includePaths + + toolchainInstallPath: compilerPathProbe.found ? compilerPathProbe.path : undefined + + /* Work-around for QtCreator which expects these properties to exist. */ + property string cCompilerName: compilerName + property string cxxCompilerName: compilerName + + compilerName: "dmc.exe" + compilerPath: FileInfo.joinPaths(toolchainInstallPath, compilerName) + + assemblerName: "dmc.exe" + assemblerPath: FileInfo.joinPaths(toolchainInstallPath, assemblerName) + + linkerName: "link.exe" + linkerPath: FileInfo.joinPaths(toolchainInstallPath, linkerName) + + property string archiverName: "lib.exe" + property string archiverPath: FileInfo.joinPaths(toolchainInstallPath, archiverName) + property string rccCompilerName: "rcc.exe" + property string rccCompilerPath: FileInfo.joinPaths(toolchainInstallPath, rccCompilerName) + + property string extenderName + PropertyOptions { + name: "extenderName" + allowedValues: [undefined, "dosz", "dosr", "dosx", "dosp"] + description: "Specifies the DOS memory extender to compile with:\n" + + " - \"dosz\" is the ZPM 16 bit DOS Extender\n" + + " - \"dosr\" is the Rational 16 bit DOS Extender\n" + + " - \"dosx\" is the DOSX 32 bit DOS Extender\n" + + " - \"dosp\" is the Pharlap 32 bit DOS Extender\n" + ; + } + + runtimeLibrary: "dynamic" + + staticLibrarySuffix: ".lib" + dynamicLibrarySuffix: ".dll" + executableSuffix: ".exe" + objectSuffix: ".obj" + + imageFormat: { + if (qbs.targetPlatform === "dos") + return "mz"; + else if (qbs.targetPlatform === "windows") + return "pe"; + } + + defineFlag: "-D" + includeFlag: "-I" + systemIncludeFlag: "-I" + preincludeFlag: "-HI" + libraryPathFlag: "-L/packcode" + + knownArchitectures: ["x86", "x86_16"] + + Rule { + id: assembler + inputs: ["asm"] + outputFileTags: DMC.depsOutputTags().concat( + Cpp.assemblerOutputTags(generateAssemblerListingFiles)) + outputArtifacts: DMC.depsOutputArtifacts(input, product).concat( + Cpp.assemblerOutputArtifacts(input)) + prepare: DMC.prepareAssembler.apply(DMC, arguments) + } + + FileTagger { + patterns: ["*.s", "*.asm"] + fileTags: ["asm"] + } + + Rule { + id: compiler + inputs: ["cpp", "c"] + auxiliaryInputs: ["hpp"] + outputFileTags: DMC.depsOutputTags().concat( + Cpp.compilerOutputTags(generateCompilerListingFiles)) + outputArtifacts: DMC.depsOutputArtifacts(input, product).concat( + Cpp.compilerOutputArtifacts(input)) + prepare: DMC.prepareCompiler.apply(DMC, arguments) + } + + + Rule { + id: rccCompiler + inputs: ["rc"] + auxiliaryInputs: ["hpp"] + outputFileTags: Cpp.resourceCompilerOutputTags() + outputArtifacts: Cpp.resourceCompilerOutputArtifacts(input) + prepare: DMC.prepareRccCompiler.apply(DMC, arguments) + } + + FileTagger { + patterns: ["*.rc"] + fileTags: ["rc"] + } + + Rule { + id: applicationLinker + multiplex: true + inputs: ["obj", "res", "linkerscript"] + inputsFromDependencies: ["staticlibrary", "dynamiclibrary_import"] + outputFileTags: Cpp.applicationLinkerOutputTags(generateLinkerMapFile) + outputArtifacts: Cpp.applicationLinkerOutputArtifacts(product) + prepare: DMC.prepareLinker.apply(DMC, arguments) + } + + Rule { + id: dynamicLibraryLinker + multiplex: true + inputs: ["obj", "res"] + inputsFromDependencies: ["staticlibrary", "dynamiclibrary_import"] + outputFileTags: Cpp.dynamicLibraryLinkerOutputTags() + outputArtifacts: Cpp.dynamicLibraryLinkerOutputArtifacts(product) + prepare: DMC.prepareLinker.apply(DMC, arguments) + } + + Rule { + id: staticLibraryLinker + multiplex: true + inputs: ["obj"] + inputsFromDependencies: ["staticlibrary", "dynamiclibrary_import"] + outputFileTags: Cpp.staticLibraryLinkerOutputTags() + outputArtifacts: Cpp.staticLibraryLinkerOutputArtifacts(product) + prepare: DMC.prepareArchiver.apply(DMC, arguments) + } +} diff --git a/share/qbs/modules/cpp/freebsd-gcc.qbs b/share/qbs/modules/cpp/freebsd-gcc.qbs index b5a4b89f3..e04ed7f2b 100644 --- a/share/qbs/modules/cpp/freebsd-gcc.qbs +++ b/share/qbs/modules/cpp/freebsd-gcc.qbs @@ -29,13 +29,14 @@ ****************************************************************************/ import "freebsd.js" as FreeBSD +import qbs.Host UnixGCC { - condition: qbs.targetOS && qbs.targetOS.contains("freebsd") && - qbs.toolchain && qbs.toolchain.contains("gcc") + condition: qbs.targetOS.includes("freebsd") && + qbs.toolchain && qbs.toolchain.includes("gcc") priority: 1 - targetSystem: "freebsd" + (qbs.hostOS.contains("freebsd") ? FreeBSD.hostKernelRelease() : "") + targetSystem: "freebsd" + (Host.os().includes("freebsd") ? FreeBSD.hostKernelRelease() : "") distributionIncludePaths: ["/usr/local/include"] distributionLibraryPaths: ["/usr/local/lib"] diff --git a/share/qbs/modules/cpp/gcc.js b/share/qbs/modules/cpp/gcc.js index 574fe769b..90f8fc933 100644 --- a/share/qbs/modules/cpp/gcc.js +++ b/share/qbs/modules/cpp/gcc.js @@ -28,9 +28,11 @@ ** ****************************************************************************/ +var Codesign = require("../codesign/codesign.js"); var Cpp = require("cpp.js"); var File = require("qbs.File"); var FileInfo = require("qbs.FileInfo"); +var Host = require("qbs.Host"); var DarwinTools = require("qbs.DarwinTools"); var ModUtils = require("qbs.ModUtils"); var PathTools = require("qbs.PathTools"); @@ -76,6 +78,7 @@ function useCompilerDriverLinker(product, inputs) { function collectLibraryDependencies(product, isDarwin) { var publicDeps = {}; + var privateDeps = {}; var objects = []; var objectByFilePath = {}; var tagForLinkingAgainstSharedLib = product.cpp.imageFormat === "pe" @@ -132,7 +135,7 @@ function collectLibraryDependencies(product, isDarwin) { if (!obj.cpp) return; function ensureArray(a) { - return Array.isArray(a) ? a : []; + return (a instanceof Array) ? a : []; } function sanitizedModuleListProperty(obj, moduleName, propertyName) { return ensureArray(ModUtils.sanitizedModuleProperty(obj, moduleName, propertyName)); @@ -168,6 +171,8 @@ function collectLibraryDependencies(product, isDarwin) { && typeof dep.artifacts[tagForLinkingAgainstSharedLib] !== "undefined"; if (!isStaticLibrary && !isDynamicLibrary) return; + if (isBelowIndirectDynamicLib && privateDeps[dep.name]) + return; var nextIsBelowIndirectDynamicLib = isBelowIndirectDynamicLib || isDynamicLibrary; dep.dependencies.forEach(function(depdep) { @@ -191,6 +196,7 @@ function collectLibraryDependencies(product, isDarwin) { publicDeps[dep.name] = true; } else { addArtifactFilePaths(dep, tagForLinkingAgainstSharedLib, addPrivateFilePath); + privateDeps[dep.name] = true; } } } @@ -202,7 +208,6 @@ function collectLibraryDependencies(product, isDarwin) { product.dependencies.forEach(traverseDirectDependency); addExternalLibs(product); - var seenRPathLinkDirs = {}; var result = { libraries: [], rpath_link: [] }; objects.forEach( function (obj) { @@ -213,10 +218,7 @@ function collectLibraryDependencies(product, isDarwin) { framework: obj.framework }); } else { var dirPath = FileInfo.path(obj.filePath); - if (!seenRPathLinkDirs.hasOwnProperty(dirPath)) { - seenRPathLinkDirs[dirPath] = true; - result.rpath_link.push(dirPath); - } + result.rpath_link.push(dirPath); } }); return result; @@ -228,7 +230,7 @@ function escapeLinkerFlags(product, inputs, linkerFlags) { if (useCompilerDriverLinker(product, inputs)) { var sep = ","; - var useXlinker = linkerFlags.some(function (f) { return f.contains(sep); }); + var useXlinker = linkerFlags.some(function (f) { return f.includes(sep); }); if (useXlinker) { // One or more linker arguments contain the separator character itself // Use -Xlinker to handle these @@ -245,7 +247,7 @@ function escapeLinkerFlags(product, inputs, linkerFlags) { return xlinkerFlags; } - if (product.cpp.enableSuspiciousLinkerFlagWarnings && linkerFlags.contains("-Xlinker")) { + if (product.cpp.enableSuspiciousLinkerFlagWarnings && linkerFlags.includes("-Xlinker")) { console.warn("Encountered -Xlinker linker flag escape sequence. This may cause the " + "target to fail to link. Please do not escape these flags manually; " + "qbs does that for you."); @@ -260,9 +262,7 @@ function escapeLinkerFlags(product, inputs, linkerFlags) { } function linkerFlags(project, product, inputs, outputs, primaryOutput, linkerPath) { - var libraryPaths = product.cpp.libraryPaths; - var distributionLibraryPaths = product.cpp.distributionLibraryPaths; - var isDarwin = product.qbs.targetOS.contains("darwin"); + var isDarwin = product.qbs.targetOS.includes("darwin"); var libraryDependencies = collectLibraryDependencies(product, isDarwin); var frameworks = product.cpp.frameworks; var weakFrameworks = product.cpp.weakFrameworks; @@ -275,7 +275,7 @@ function linkerFlags(project, product, inputs, outputs, primaryOutput, linkerPat var escapableLinkerFlags = []; - if (primaryOutput.fileTags.contains("dynamiclibrary")) { + if (primaryOutput.fileTags.includes("dynamiclibrary")) { if (isDarwin) { args.push((function () { var tags = ["c", "cpp", "objc", "objcpp", "asm_cpp"]; @@ -300,7 +300,7 @@ function linkerFlags(project, product, inputs, outputs, primaryOutput, linkerPat } } - if (primaryOutput.fileTags.contains("loadablemodule")) + if (primaryOutput.fileTags.includes("loadablemodule")) args.push(isDarwin ? "-bundle" : "-shared"); if (primaryOutput.fileTags.containsAny(["dynamiclibrary", "loadablemodule"])) { @@ -323,7 +323,7 @@ function linkerFlags(project, product, inputs, outputs, primaryOutput, linkerPat var sysroot = product.cpp.syslibroot; if (sysroot) { - if (product.qbs.toolchain.contains("qcc")) + if (product.qbs.toolchain.includes("qcc")) escapableLinkerFlags.push("--sysroot=" + sysroot); else if (isDarwin) escapableLinkerFlags.push("-syslibroot", sysroot); @@ -340,7 +340,7 @@ function linkerFlags(project, product, inputs, outputs, primaryOutput, linkerPat function fixupRPath(rpath) { // iOS, tvOS, watchOS, and others, are fine - if (!product.qbs.targetOS.contains("macos")) + if (!product.qbs.targetOS.includes("macos")) return rpath; // ...as are newer versions of macOS @@ -355,11 +355,12 @@ function linkerFlags(project, product, inputs, outputs, primaryOutput, linkerPat return rpath; } - if (!product.qbs.targetOS.contains("windows")) { - function isNotSystemRunPath(p) { - return !FileInfo.isAbsolutePath(p) || (!systemRunPaths.contains(p) - && !canonicalSystemRunPaths.contains(File.canonicalFilePath(p))); - }; + function isNotSystemRunPath(p) { + return !FileInfo.isAbsolutePath(p) || (!systemRunPaths.includes(p) + && !canonicalSystemRunPaths.includes(File.canonicalFilePath(p))); + }; + + if (!product.qbs.targetOS.includes("windows")) { for (i in rpaths) { if (isNotSystemRunPath(rpaths[i])) escapableLinkerFlags.push("-rpath", fixupRPath(rpaths[i])); @@ -369,13 +370,16 @@ function linkerFlags(project, product, inputs, outputs, primaryOutput, linkerPat if (product.cpp.entryPoint) escapableLinkerFlags.push("-e", product.cpp.entryPoint); - if (product.qbs.toolchain.contains("mingw")) { + if (product.qbs.toolchain.includes("mingw")) { if (product.consoleApplication !== undefined) escapableLinkerFlags.push("-subsystem", product.consoleApplication ? "console" : "windows"); var minimumWindowsVersion = product.cpp.minimumWindowsVersion; if (minimumWindowsVersion) { + // workaround for QBS-1724, mingw seems to be broken + if (Utilities.versionCompare(minimumWindowsVersion, "6.2") > 0) + minimumWindowsVersion = "6.2"; var subsystemVersion = WindowsUtils.getWindowsVersionInFormat(minimumWindowsVersion, 'subsystem'); if (subsystemVersion) { var major = subsystemVersion.split('.')[0]; @@ -397,23 +401,16 @@ function linkerFlags(project, product, inputs, outputs, primaryOutput, linkerPat var stdlib = isLinkingCppObjects ? product.cpp.cxxStandardLibrary : undefined; - if (stdlib && product.qbs.toolchain.contains("clang")) + if (stdlib && product.qbs.toolchain.includes("clang")) args.push("-stdlib=" + stdlib); // Flags for library search paths - var allLibraryPaths = []; - if (libraryPaths) - allLibraryPaths = allLibraryPaths.uniqueConcat(libraryPaths); - if (distributionLibraryPaths) - allLibraryPaths = allLibraryPaths.uniqueConcat(distributionLibraryPaths); - if (systemRunPaths.length > 0) - allLibraryPaths = allLibraryPaths.filter(isNotSystemRunPath); - args = args.concat(allLibraryPaths.map(function(path) { return '-L' + path })); - - var linkerScripts = inputs.linkerscript - ? inputs.linkerscript.map(function(a) { return a.filePath; }) : []; - Array.prototype.push.apply(escapableLinkerFlags, [].uniqueConcat(linkerScripts) - .map(function(path) { return '-T' + path })); + var allLibraryPaths = Cpp.collectLibraryPaths(product); + var builtIns = product.cpp.compilerLibraryPaths + allLibraryPaths = allLibraryPaths.filter(function(p) { return !builtIns.includes(p); }); + args = args.concat(allLibraryPaths.map(function(path) { return product.cpp.libraryPathFlag + path })); + + escapableLinkerFlags = escapableLinkerFlags.concat(Cpp.collectLinkerScriptPathsArguments(product, inputs)); var versionScripts = inputs.versionscript ? inputs.versionscript.map(function(a) { return a.filePath; }) : []; @@ -425,16 +422,15 @@ function linkerFlags(project, product, inputs, outputs, primaryOutput, linkerPat var useCompilerDriver = useCompilerDriverLinker(product, inputs); args = args.concat(configFlags(product, useCompilerDriver)); - Array.prototype.push.apply(escapableLinkerFlags, product.cpp.platformLinkerFlags); - Array.prototype.push.apply(escapableLinkerFlags, product.cpp.linkerFlags); + escapableLinkerFlags = escapableLinkerFlags.concat(Cpp.collectMiscEscapableLinkerArguments(product)); // Note: due to the QCC response files hack in prepareLinker(), at least one object file or // library file must follow the output file path so that QCC has something to process before // sending the rest of the arguments through the response file. args.push("-o", primaryOutput.filePath); - if (inputs.obj) - args = args.concat(inputs.obj.map(function (obj) { return obj.filePath })); + args = args.concat(Cpp.collectLinkerObjectPaths(inputs)); + args = args.concat(Cpp.collectResourceObjectPaths(inputs)); for (i in frameworks) { frameworkExecutablePath = PathTools.frameworkExecutablePath(frameworks[i]); @@ -479,9 +475,12 @@ function linkerFlags(project, product, inputs, outputs, primaryOutput, linkerPat var symbolLinkMode = dep.symbolLinkMode; if (isDarwin && symbolLinkMode) { - if (!["lazy", "reexport", "upward", "weak"].contains(symbolLinkMode)) + if (!["lazy", "reexport", "upward", "weak"].includes(symbolLinkMode)) throw new Error("unknown value '" + symbolLinkMode + "' for cpp.symbolLinkMode"); + } + var supportsLazyMode = Utilities.versionCompare(product.cpp.compilerVersion, "15.0.0") < 0; + if (isDarwin && symbolLinkMode && (symbolLinkMode !== "lazy" || supportsLazyMode)) { if (FileInfo.isAbsolutePath(lib) || lib.startsWith('@')) escapableLinkerFlags.push("-" + symbolLinkMode + "_library", lib); else if (dep.framework) @@ -535,9 +534,10 @@ function linkerFlags(project, product, inputs, outputs, primaryOutput, linkerPat var escapedLinkerFlags = escapeLinkerFlags(product, inputs, escapableLinkerFlags); Array.prototype.push.apply(escapedLinkerFlags, args); - var driverLinkerFlags = useCompilerDriver ? product.cpp.driverLinkerFlags : undefined; - if (driverLinkerFlags) - Array.prototype.push.apply(escapedLinkerFlags, driverLinkerFlags); + if (useCompilerDriver) + escapedLinkerFlags = escapedLinkerFlags.concat(Cpp.collectMiscLinkerArguments(product)); + if (product.qbs.toolchain.includes("mingw") && product.cpp.runtimeLibrary === "static") + escapedLinkerFlags = ['-static-libgcc', '-static-libstdc++'].concat(escapedLinkerFlags); return escapedLinkerFlags; } @@ -545,15 +545,12 @@ function linkerFlags(project, product, inputs, outputs, primaryOutput, linkerPat function configFlags(config, isDriver) { var args = []; - if (isDriver !== false) { - args = args.concat(config.cpp.platformDriverFlags); - args = args.concat(config.cpp.driverFlags); - args = args.concat(config.cpp.targetDriverFlags); - } + if (isDriver !== false) + args = args.concat(Cpp.collectMiscDriverArguments(config)); var frameworkPaths = config.cpp.frameworkPaths; if (frameworkPaths) - args = args.concat(frameworkPaths.map(function(path) { return '-F' + path })); + args = args.uniqueConcat(frameworkPaths.map(function(path) { return '-F' + path })); var allSystemFrameworkPaths = []; @@ -586,7 +583,7 @@ function languageTagFromFileExtension(toolchain, fileName) { "s" : "asm", "S" : "asm_cpp" }; - if (!toolchain.contains("clang")) + if (!toolchain.includes("clang")) m["sx"] = "asm_cpp"; // clang does NOT recognize .sx return m[fileName.substring(i + 1)]; } @@ -594,7 +591,7 @@ function languageTagFromFileExtension(toolchain, fileName) { // Older versions of the QNX SDK have C and C++ compilers whose filenames differ only by case, // which won't work in case insensitive environments like Win32+NTFS, HFS+ and APFS function isLegacyQnxSdk(config) { - return config.qbs.toolchain.contains("qcc") && config.qnx && !config.qnx.qnx7; + return config.qbs.toolchain.includes("qcc") && config.qnx && !config.qnx.qnx7; } function effectiveCompilerInfo(toolchain, input, output) { @@ -602,7 +599,7 @@ function effectiveCompilerInfo(toolchain, input, output) { var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(output.fileTags)); // Whether we're compiling a precompiled header or normal source file - var pchOutput = output.fileTags.contains(tag + "_pch"); + var pchOutput = output.fileTags.includes(tag + "_pch"); var compilerPathByLanguage = input.cpp.compilerPathByLanguage; if (compilerPathByLanguage) @@ -610,7 +607,7 @@ function effectiveCompilerInfo(toolchain, input, output) { if (!compilerPath || tag !== languageTagFromFileExtension(toolchain, input.fileName) || isLegacyQnxSdk(input)) { - if (input.qbs.toolchain.contains("qcc")) + if (input.qbs.toolchain.includes("qcc")) language = qnxLangArgs(input, tag); else language = ["-x", languageName(tag) + (pchOutput ? '-header' : '')]; @@ -704,6 +701,22 @@ function standardFallbackValueOrDefault(toolchain, compilerVersion, languageVers {"name": "gcc", "version": "4.7"} ] }, + "c17": { + "fallback": "c11", + "toolchains": [ + {"name": "xcode", "version": "10.2"}, + {"name": "clang", "version": "7.0"}, + {"name": "gcc", "version": "8.1"} + ] + }, + "c2x": { + "fallback": "c17", + "toolchains": [ + {"name": "xcode", "version": "11.4"}, + {"name": "clang", "version": "9.0"}, + {"name": "gcc", "version": "9.0"} + ] + }, "c++14": { "fallback": "c++1y", "toolchains": [ @@ -723,9 +736,17 @@ function standardFallbackValueOrDefault(toolchain, compilerVersion, languageVers "c++20": { "fallback": "c++2a", "toolchains": [ - {"name": "xcode"}, // not yet implemented - {"name": "clang"}, // not yet implemented - {"name": "gcc"} // not yet implemented + {"name": "xcode", "version": "12.5"}, + {"name": "clang", "version": "11.0"}, + {"name": "gcc", "version": "10.1"} + ] + }, + "c++23": { + "fallback": "c++2b", + "toolchains": [ + {"name": "xcode"}, + {"name": "clang"}, + {"name": "gcc"} ] } }; @@ -734,7 +755,7 @@ function standardFallbackValueOrDefault(toolchain, compilerVersion, languageVers if (m) { for (var idx = 0; idx < m.toolchains.length; ++idx) { var tc = m.toolchains[idx]; - if (toolchain.contains(tc.name)) { + if (toolchain.includes(tc.name)) { // If we found our toolchain and it doesn't yet support the language standard // we're requesting, or we're using an older version that only supports the // preliminary flag, use that. @@ -754,16 +775,9 @@ function standardFallbackValueOrDefault(toolchain, compilerVersion, languageVers function compilerFlags(project, product, input, output, explicitlyDependsOn) { var i; - var includePaths = input.cpp.includePaths; - var systemIncludePaths = input.cpp.systemIncludePaths; - var distributionIncludePaths = input.cpp.distributionIncludePaths; - - var platformDefines = input.cpp.platformDefines; - var defines = input.cpp.defines; - // Determine which C-language we're compiling var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(output.fileTags)); - if (!["c", "cpp", "objc", "objcpp", "asm_cpp"].contains(tag)) + if (!["c", "cpp", "objc", "objcpp", "asm_cpp"].includes(tag)) throw ("unsupported source language: " + tag); var compilerInfo = effectiveCompilerInfo(product.qbs.toolchain, @@ -796,12 +810,12 @@ function compilerFlags(project, product, input, output, explicitlyDependsOn) { args = args.concat(configFlags(input)); - if (!input.qbs.toolchain.contains("qcc")) + if (!input.qbs.toolchain.includes("qcc")) args.push('-pipe'); if (input.cpp.enableReproducibleBuilds) { var toolchain = product.qbs.toolchain; - if (!toolchain.contains("clang")) { + if (!toolchain.includes("clang")) { var hashString = FileInfo.relativePath(project.sourceDirectory, input.filePath); var hash = Utilities.getHash(hashString); args.push("-frandom-seed=0x" + hash.substring(0, 8)); @@ -809,8 +823,8 @@ function compilerFlags(project, product, input, output, explicitlyDependsOn) { var major = product.cpp.compilerVersionMajor; var minor = product.cpp.compilerVersionMinor; - if ((toolchain.contains("clang") && (major > 3 || (major === 3 && minor >= 5))) || - (toolchain.contains("gcc") && (major > 4 || (major === 4 && minor >= 9)))) { + if ((toolchain.includes("clang") && (major > 3 || (major === 3 && minor >= 5))) || + (toolchain.includes("gcc") && (major > 4 || (major === 4 && minor >= 9)))) { args.push("-Wdate-time"); } } @@ -838,7 +852,7 @@ function compilerFlags(project, product, input, output, explicitlyDependsOn) { } var visibility = input.cpp.visibility; - if (!product.qbs.toolchain.contains("mingw")) { + if (!product.qbs.toolchain.includes("mingw")) { if (visibility === 'hidden' || visibility === 'minimal') args.push('-fvisibility=hidden'); if ((visibility === 'hiddenInlines' || visibility === 'minimal') && tag === 'cpp') @@ -851,62 +865,40 @@ function compilerFlags(project, product, input, output, explicitlyDependsOn) { // Only push language arguments if we have to. Array.prototype.push.apply(args, compilerInfo.language); - args = args.concat(ModUtils.moduleProperty(input, 'platformFlags'), - ModUtils.moduleProperty(input, 'flags'), - ModUtils.moduleProperty(input, 'platformFlags', tag), - ModUtils.moduleProperty(input, 'flags', tag)); + args = args.concat(Cpp.collectMiscCompilerArguments(input, tag)); var pchTag = compilerInfo.tag + "_pch"; - var pchOutput = output.fileTags.contains(pchTag); + var pchOutput = output.fileTags.includes(pchTag); var pchInputs = explicitlyDependsOn[pchTag]; if (!pchOutput && pchInputs && pchInputs.length === 1 && ModUtils.moduleProperty(input, 'usePrecompiledHeader', tag)) { var pchInput = pchInputs[0]; var pchFilePath = FileInfo.joinPaths(FileInfo.path(pchInput.filePath), pchInput.completeBaseName); - args.push('-include', pchFilePath); + args.push(input.cpp.preincludeFlag, pchFilePath); } - var prefixHeaders = input.cpp.prefixHeaders; - for (i in prefixHeaders) { - args.push('-include'); - args.push(prefixHeaders[i]); - } + args = args.concat(Cpp.collectPreincludePathsArguments(input)); var positionIndependentCode = input.cpp.positionIndependentCode; - if (positionIndependentCode && !product.qbs.targetOS.contains("windows")) + if (positionIndependentCode && !product.qbs.targetOS.includes("windows")) args.push('-fPIC'); var cppFlags = input.cpp.cppFlags; for (i in cppFlags) args.push('-Wp,' + cppFlags[i]) - var allDefines = []; - if (platformDefines) - allDefines = allDefines.uniqueConcat(platformDefines); - if (defines) - allDefines = allDefines.uniqueConcat(defines); - args = args.concat(allDefines.map(function(define) { return '-D' + define })); - if (includePaths) { - args = args.concat([].uniqueConcat(includePaths).map(function(path) { - return input.cpp.includeFlag + path; - })); - } - - var allSystemIncludePaths = []; - if (systemIncludePaths) - allSystemIncludePaths = allSystemIncludePaths.uniqueConcat(systemIncludePaths); - if (distributionIncludePaths) - allSystemIncludePaths = allSystemIncludePaths.uniqueConcat(distributionIncludePaths); - allSystemIncludePaths.forEach(function(v) { args.push(input.cpp.systemIncludeFlag, v); }); + args = args.concat(Cpp.collectDefinesArguments(input)); + args = args.concat(Cpp.collectIncludePathsArguments(input)); + args = args.concat(Cpp.collectSystemIncludePathsArguments(input)); var minimumWindowsVersion = input.cpp.minimumWindowsVersion; - if (minimumWindowsVersion && product.qbs.targetOS.contains("windows")) { + if (minimumWindowsVersion && product.qbs.targetOS.includes("windows")) { var hexVersion = WindowsUtils.getWindowsVersionInFormat(minimumWindowsVersion, 'hex'); if (hexVersion) { var versionDefs = [ 'WINVER', '_WIN32_WINNT', '_WIN32_WINDOWS' ]; for (i in versionDefs) - args.push('-D' + versionDefs[i] + '=' + hexVersion); + args.push(input.cpp.defineFlag + versionDefs[i] + '=' + hexVersion); } } @@ -914,11 +906,11 @@ function compilerFlags(project, product, input, output, explicitlyDependsOn) { switch (tag) { case "c": case "objc": - var knownValues = ["c11", "c99", "c90", "c89"]; + var knownValues = ["c2x", "c17", "c11", "c99", "c90", "c89"]; return Cpp.languageVersion(input.cpp.cLanguageVersion, knownValues, "C"); case "cpp": case "objcpp": - knownValues = ["c++20", "c++2a", "c++17", "c++1z", + knownValues = ["c++23", "c++2b", "c++20", "c++2a", "c++17", "c++1z", "c++14", "c++1y", "c++11", "c++0x", "c++03", "c++98"]; return Cpp.languageVersion(input.cpp.cxxLanguageVersion, knownValues, "C++"); @@ -935,7 +927,7 @@ function compilerFlags(project, product, input, output, explicitlyDependsOn) { if (tag === "cpp" || tag === "objcpp") { var cxxStandardLibrary = product.cpp.cxxStandardLibrary; - if (cxxStandardLibrary && product.qbs.toolchain.contains("clang")) { + if (cxxStandardLibrary && product.qbs.toolchain.includes("clang")) { args.push("-stdlib=" + cxxStandardLibrary); } } @@ -950,7 +942,7 @@ function additionalCompilerAndLinkerFlags(product) { var args = [] var requireAppExtensionSafeApi = product.cpp.requireAppExtensionSafeApi; - if (requireAppExtensionSafeApi !== undefined && product.qbs.targetOS.contains("darwin")) { + if (requireAppExtensionSafeApi !== undefined && product.qbs.targetOS.includes("darwin")) { args.push(requireAppExtensionSafeApi ? "-fapplication-extension" : "-fno-application-extension"); } @@ -976,10 +968,6 @@ function languageName(fileTag) { function prepareAssembler(project, product, inputs, outputs, input, output) { var assemblerPath = product.cpp.assemblerPath; - var includePaths = input.cpp.includePaths; - var systemIncludePaths = input.cpp.systemIncludePaths; - var distributionIncludePaths = input.cpp.distributionIncludePaths; - var args = product.cpp.targetAssemblerFlags; if (input.cpp.debugInformation) @@ -989,18 +977,9 @@ function prepareAssembler(project, product, inputs, outputs, input, output) { if (warnings === 'none') args.push('-W'); - var tag = "asm"; - args = args.concat(ModUtils.moduleProperty(input, 'platformFlags', tag), - ModUtils.moduleProperty(input, 'flags', tag)); - - var allIncludePaths = []; - if (includePaths) - allIncludePaths = allIncludePaths.uniqueConcat(includePaths); - if (systemIncludePaths) - allIncludePaths = allIncludePaths.uniqueConcat(systemIncludePaths); - if (distributionIncludePaths) - allIncludePaths = allIncludePaths.uniqueConcat(distributionIncludePaths); - args = args.concat(allIncludePaths.map(function(path) { return input.cpp.includeFlag + path })); + args = args.concat(Cpp.collectMiscAssemblerArguments(input, "asm")); + args = args.concat(Cpp.collectIncludePathsArguments(input)); + args = args.concat(Cpp.collectSystemIncludePathsArguments(input, input.cpp.includeFlag)); args.push("-o", output.filePath); args.push(input.filePath); @@ -1014,7 +993,7 @@ function prepareAssembler(project, product, inputs, outputs, input, output) { function compilerEnvVars(config, compilerInfo) { - if (config.qbs.toolchain.contains("qcc")) + if (config.qbs.toolchain.includes("qcc")) return ["QCC_CONF_PATH"]; var list = ["CPATH", "TMPDIR"]; @@ -1026,15 +1005,15 @@ function compilerEnvVars(config, compilerInfo) list.push("OBJC_INCLUDE_PATH"); else if (compilerInfo.tag === "objccpp") list.push("OBJCPLUS_INCLUDE_PATH"); - if (config.qbs.targetOS.contains("macos")) + if (config.qbs.targetOS.includes("macos")) list.push("MACOSX_DEPLOYMENT_TARGET"); - else if (config.qbs.targetOS.contains("ios")) + else if (config.qbs.targetOS.includes("ios")) list.push("IPHONEOS_DEPLOYMENT_TARGET"); - else if (config.qbs.targetOS.contains("tvos")) + else if (config.qbs.targetOS.includes("tvos")) list.push("TVOS_DEPLOYMENT_TARGET"); - else if (config.qbs.targetOS.contains("watchos")) + else if (config.qbs.targetOS.includes("watchos")) list.push("WATCHOS_DEPLOYMENT_TARGET"); - if (config.qbs.toolchain.contains("clang")) { + if (config.qbs.toolchain.includes("clang")) { list.push("TEMP", "TMP"); } else { list.push("LANG", "LC_CTYPE", "LC_MESSAGES", "LC_ALL", "GCC_COMPARE_DEBUG", @@ -1045,7 +1024,7 @@ function compilerEnvVars(config, compilerInfo) function linkerEnvVars(config, inputs) { - if (config.qbs.toolchain.contains("clang") || config.qbs.toolchain.contains("qcc")) + if (config.qbs.toolchain.includes("clang") || config.qbs.toolchain.includes("qcc")) return []; var list = ["GNUTARGET", "LDEMULATION", "COLLECT_NO_DEMANGLE"]; if (useCompilerDriverLinker(config, inputs)) @@ -1055,7 +1034,7 @@ function linkerEnvVars(config, inputs) function setResponseFileThreshold(command, product) { - if (product.qbs.targetOS.contains("windows") && product.qbs.hostOS.contains("windows")) + if (product.qbs.targetOS.includes("windows") && Host.os().includes("windows")) command.responseFileThreshold = 10000; } @@ -1063,7 +1042,7 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli var compilerInfo = effectiveCompilerInfo(product.qbs.toolchain, input, output); var compilerPath = compilerInfo.path; - var pchOutput = output.fileTags.contains(compilerInfo.tag + "_pch"); + var pchOutput = output.fileTags.includes(compilerInfo.tag + "_pch"); var args = compilerFlags(project, product, input, output, explicitlyDependsOn); var wrapperArgsLength = 0; @@ -1153,7 +1132,7 @@ function getSymbolInfo(product, inputFile) // construct the list of defined symbols by subtracting. var undefinedGlobalSymbols = collectStdoutLines(command, args.concat(["-u", inputFile])); result.definedGlobalSymbols = result.allGlobalSymbols.filter(function(line) { - return !undefinedGlobalSymbols.contains(line); }); + return !undefinedGlobalSymbols.includes(line); }); result.success = true; } catch (e) { console.debug("Failed to collect symbols for shared library: nm command '" @@ -1253,8 +1232,13 @@ function createSymbolCheckingCommands(product, outputs) { oldSymbols = oldNmResult.allGlobalSymbols; newSymbols = newNmResult.allGlobalSymbols; } else { - oldSymbols = oldNmResult.definedGlobalSymbols; - newSymbols = newNmResult.definedGlobalSymbols; + var weakFilter = function(line) { + var symbolType = line.split(/\s+/)[1]; + return symbolType != "v" && symbolType != "V" + && symbolType != "w" && symbolType != "W"; + }; + oldSymbols = oldNmResult.definedGlobalSymbols.filter(weakFilter); + newSymbols = newNmResult.definedGlobalSymbols.filter(weakFilter); } if (oldSymbols.length !== newSymbols.length) { console.debug("List of relevant symbols differs for '" + libFilePath + "'."); @@ -1281,6 +1265,59 @@ function createSymbolCheckingCommands(product, outputs) { return commands; } +function separateDebugInfoCommands(product, outputs, primaryOutput) { + var commands = []; + + var debugInfo = outputs.debuginfo_app || outputs.debuginfo_dll + || outputs.debuginfo_loadablemodule; + + if (debugInfo) { + var objcopy = product.cpp.objcopyPath; + + var cmd = new Command(objcopy, ["--only-keep-debug", primaryOutput.filePath, + debugInfo[0].filePath]); + cmd.silent = true; + commands.push(cmd); + + cmd = new Command(objcopy, ["--strip-debug", primaryOutput.filePath]); + cmd.silent = true; + commands.push(cmd); + + cmd = new Command(objcopy, ["--add-gnu-debuglink=" + debugInfo[0].filePath, + primaryOutput.filePath]); + cmd.silent = true; + commands.push(cmd); + } + + return commands; +} + +function separateDebugInfoCommandsDarwin(product, outputs, primaryOutputs) { + var commands = []; + + var debugInfo = outputs.debuginfo_app || outputs.debuginfo_dll + || outputs.debuginfo_loadablemodule; + if (debugInfo) { + var dsymPath = debugInfo[0].filePath; + if (outputs.debuginfo_bundle && outputs.debuginfo_bundle[0]) + dsymPath = outputs.debuginfo_bundle[0].filePath; + + var flags = product.cpp.dsymutilFlags || []; + var files = primaryOutputs.map(function (f) { return f.filePath; }); + var cmd = new Command(product.cpp.dsymutilPath, + flags.concat(["-o", dsymPath].concat(files))); + cmd.description = "generating dSYM for " + product.name; + commands.push(cmd); + + // strip debug info + cmd = new Command(product.cpp.stripPath, ["-S"].concat(files)); + cmd.silent = true; + commands.push(cmd); + } + + return commands; +} + function prepareLinker(project, product, inputs, outputs, input, output) { var i, primaryOutput, cmd, commands = []; @@ -1307,7 +1344,7 @@ function prepareLinker(project, product, inputs, outputs, input, output) { var responseFileArgumentIndex = wrapperArgsLength; // qcc doesn't properly handle response files, so we have to do it manually - var useQnxResponseFileHack = product.qbs.toolchain.contains("qcc") + var useQnxResponseFileHack = product.qbs.toolchain.includes("qcc") && useCompilerDriverLinker(product, inputs); if (useQnxResponseFileHack) { // qcc needs to see at least one object/library file to think it has something to do, @@ -1327,43 +1364,13 @@ function prepareLinker(project, product, inputs, outputs, input, output) { setResponseFileThreshold(cmd, product); commands.push(cmd); - var debugInfo = outputs.debuginfo_app || outputs.debuginfo_dll - || outputs.debuginfo_loadablemodule; - if (debugInfo) { - if (product.qbs.targetOS.contains("darwin")) { - if (!product.aggregate) { - var dsymPath = debugInfo[0].filePath; - if (outputs.debuginfo_bundle && outputs.debuginfo_bundle[0]) - dsymPath = outputs.debuginfo_bundle[0].filePath; - var flags = product.cpp.dsymutilFlags || []; - cmd = new Command(product.cpp.dsymutilPath, flags.concat([ - "-o", dsymPath, primaryOutput.filePath - ])); - cmd.description = "generating dSYM for " + product.name; - commands.push(cmd); - - cmd = new Command(product.cpp.stripPath, - ["-S", primaryOutput.filePath]); - cmd.silent = true; - commands.push(cmd); - } - } else { - var objcopy = product.cpp.objcopyPath; - - cmd = new Command(objcopy, ["--only-keep-debug", primaryOutput.filePath, - debugInfo[0].filePath]); - cmd.silent = true; - commands.push(cmd); - - cmd = new Command(objcopy, ["--strip-debug", primaryOutput.filePath]); - cmd.silent = true; - commands.push(cmd); - - cmd = new Command(objcopy, ["--add-gnu-debuglink=" + debugInfo[0].filePath, - primaryOutput.filePath]); - cmd.silent = true; - commands.push(cmd); + if (product.qbs.targetOS.includes("darwin")) { + if (!product.aggregate) { + commands = commands.concat(separateDebugInfoCommandsDarwin( + product, outputs, [primaryOutput])); } + } else { + commands = commands.concat(separateDebugInfoCommands(product, outputs, primaryOutput)); } if (outputs.dynamiclibrary) { @@ -1383,27 +1390,9 @@ function prepareLinker(project, product, inputs, outputs, input, output) { } } - if (product.xcode && product.bundle) { - var actualSigningIdentity = product.xcode.actualSigningIdentity; - var codesignDisplayName = product.xcode.actualSigningIdentityDisplayName; - if (actualSigningIdentity && !product.bundle.isBundle) { - args = product.xcode.codesignFlags || []; - args.push("--force"); - args.push("--sign", actualSigningIdentity); - args = args.concat(DarwinTools._codeSignTimestampFlags(product)); - - for (var j in inputs.xcent) { - args.push("--entitlements", inputs.xcent[j].filePath); - break; // there should only be one - } - args.push(primaryOutput.filePath); - cmd = new Command(product.xcode.codesignPath, args); - cmd.description = "codesign " - + primaryOutput.fileName - + " using " + codesignDisplayName - + " (" + actualSigningIdentity + ")"; - commands.push(cmd); - } + if (product.cpp.shouldSignArtifacts) { + Array.prototype.push.apply( + commands, Codesign.prepareSign(project, product, inputs, outputs, input, output)); } return commands; @@ -1462,12 +1451,7 @@ function dumpMacros(env, compilerFilePath, args, nullDevice, tag) { p.exec(compilerFilePath, (args || []).concat(["-Wp,-dM", "-E", "-x", languageName(tag || "c") , nullDevice]), true); - var map = {}; - p.readStdOut().trim().split(/\r?\n/g).map(function (line) { - var parts = line.split(" ", 3); - map[parts[1]] = parts[2]; - }); - return map; + return Cpp.extractMacros(p.readStdOut()); } finally { p.close(); } @@ -1514,7 +1498,7 @@ function dumpDefaultPaths(env, compilerFilePath, args, nullDevice, pathListSepar if (libraryPaths.length === 0) libraryPaths.push(sysroot + "/lib", sysroot + "/usr/lib"); - if (frameworkPaths.length === 0 && targetOS.contains("darwin")) + if (frameworkPaths.length === 0 && targetOS.includes("darwin")) frameworkPaths.push(sysroot + "/System/Library/Frameworks"); return { @@ -1527,6 +1511,28 @@ function dumpDefaultPaths(env, compilerFilePath, args, nullDevice, pathListSepar } } +function targetLinkerFlags(targetArch, targetOS) { + var linkerFlags = { + "windows": { + "i386": "i386pe", + "x86_64": "i386pep", + }, + "freebsd": { + "i386": "elf_i386_fbsd", + "x86_64": "elf_x86_64_fbsd", + }, + "other": { + "i386": "elf_i386", + "x86_64": "elf_x86_64", + } + }; + if (targetOS.includes("windows")) + return linkerFlags["windows"][targetArch]; + else if (targetOS.includes("freebsd")) + return linkerFlags["freebsd"][targetArch]; + return linkerFlags["other"][targetArch]; +} + function targetFlags(tool, hasTargetOption, target, targetArch, machineType, targetOS) { var args = []; if (hasTargetOption) { @@ -1539,8 +1545,8 @@ function targetFlags(tool, hasTargetOption, target, targetArch, machineType, tar "x86_64": ["-m64"], }, "linker": { - "i386": ["-m", targetOS.contains("windows") ? "i386pe" : "elf_i386"], - "x86_64": ["-m", targetOS.contains("windows") ? "i386pep" : "elf_x86_64"], + "i386": ["-m", targetLinkerFlags("i386", targetOS)], + "x86_64": ["-m", targetLinkerFlags("x86_64", targetOS)], }, "assembler": { "i386": ["--32"], diff --git a/share/qbs/modules/cpp/iar.js b/share/qbs/modules/cpp/iar.js index d51668095..ed58ac262 100644 --- a/share/qbs/modules/cpp/iar.js +++ b/share/qbs/modules/cpp/iar.js @@ -37,155 +37,342 @@ var Process = require("qbs.Process"); var TemporaryDir = require("qbs.TemporaryDir"); var TextFile = require("qbs.TextFile"); -function compilerName(qbs) { - switch (qbs.architecture) { - case "arm": - return "iccarm"; - case "mcs51": - return "icc8051"; - case "avr": - return "iccavr"; - case "stm8": - return "iccstm8"; - case "msp430": - return "icc430"; - case "rl78": - return "iccrl78"; - } - throw "Unable to deduce compiler name for unsupported architecture: '" - + qbs.architecture + "'"; +function supportXLinker(architecture) { + return architecture === "78k" + || architecture === "avr" + || architecture === "avr32" + || architecture === "cr16" + || architecture === "hcs12" + || architecture === "hcs8" + || architecture === "m16c" + || architecture === "m32c" + || architecture === "m68k" + || architecture === "mcs51" + || architecture === "msp430" + || architecture === "r32c" + || architecture === "v850"; } -function assemblerName(qbs) { - switch (qbs.architecture) { - case "arm": - return "iasmarm"; - case "rl78": - return "iasmrl78"; - case "mcs51": - return "a8051"; - case "avr": - return "aavr"; - case "stm8": - return "iasmstm8"; - case "msp430": - return "a430"; - } - throw "Unable to deduce assembler name for unsupported architecture: '" - + qbs.architecture + "'"; +function supportILinker(architecture) { + return architecture.startsWith("arm") + || architecture === "rh850" + || architecture === "riscv" + || architecture === "rl78" + || architecture === "rx" + || architecture === "sh" + || architecture === "stm8"; } -function linkerName(qbs) { - switch (qbs.architecture) { - case "arm": - return "ilinkarm"; - case "stm8": - return "ilinkstm8"; - case "rl78": - return "ilinkrl78"; - case "mcs51": - case "avr": - case "msp430": - return "xlink"; - } - throw "Unable to deduce linker name for unsupported architecture: '" - + qbs.architecture + "'"; +function supportXArchiver(architecture) { + return architecture === "78k" + || architecture === "avr" + || architecture === "avr32" + || architecture === "cr16" + || architecture === "hcs12" + || architecture === "hcs8" + || architecture === "m16c" + || architecture === "m32c" + || architecture === "m68k" + || architecture === "mcs51" + || architecture === "msp430" + || architecture === "r32c" + || architecture === "v850"; } -function archiverName(qbs) { - switch (qbs.architecture) { - case "arm": - case "stm8": - case "rl78": - return "iarchive"; - case "mcs51": - case "avr": - case "msp430": - return "xlib"; - } - throw "Unable to deduce archiver name for unsupported architecture: '" - + qbs.architecture + "'"; +function supportIArchiver(architecture) { + return architecture.startsWith("arm") + || architecture === "rh850" + || architecture === "riscv" + || architecture === "rl78" + || architecture === "rx" + || architecture === "sh" + || architecture === "stm8"; } -function staticLibrarySuffix(qbs) { - switch (qbs.architecture) { - case "arm": - case "stm8": - case "rl78": - return ".a"; - case "mcs51": - return ".r51"; - case "avr": - return ".r90"; - case "msp430": - return ".r43"; - } - throw "Unable to deduce static library suffix for unsupported architecture: '" - + qbs.architecture + "'"; +function supportXAssembler(architecture) { + return architecture.startsWith("arm") + || architecture === "78k" + || architecture === "avr" + || architecture === "hcs12" + || architecture === "m16c" + || architecture === "mcs51" + || architecture === "msp430" + || architecture === "m32c" + || architecture === "v850"; } -function executableSuffix(qbs) { - switch (qbs.architecture) { - case "arm": - case "stm8": - case "rl78": - return ".out"; - case "mcs51": - return qbs.debugInformation ? ".d51" : ".a51"; - case "avr": - return qbs.debugInformation ? ".d90" : ".a90"; - case "msp430": - return qbs.debugInformation ? ".d43" : ".a43"; - } - throw "Unable to deduce executable suffix for unsupported architecture: '" - + qbs.architecture + "'"; +function supportIAssembler(architecture) { + return architecture === "avr32" + || architecture === "cr16" + || architecture === "hcs8" + || architecture === "r32c" + || architecture === "rh850" + || architecture === "riscv" + || architecture === "rl78" + || architecture === "rx" + || architecture === "sh" + || architecture === "stm8" + || architecture === "m68k"; } -function objectSuffix(qbs) { - switch (qbs.architecture) { - case "arm": - case "stm8": - case "rl78": - return ".o"; - case "mcs51": - return ".r51"; +function supportEndianness(architecture) { + return architecture.startsWith("arm") + || architecture === "rx"; +} + +function supportCppExceptions(architecture) { + return architecture.startsWith("arm") + || architecture === "rh850" + || architecture === "riscv" + || architecture === "rl78" + || architecture === "rx"; +} + +function supportCppRtti(architecture) { + return architecture.startsWith("arm") + || architecture === "rh850" + || architecture === "riscv" + || architecture === "rl78" + || architecture === "rx"; +} + +function supportCppWarningAboutCStyleCast(architecture) { + return architecture.startsWith("arm") + || architecture === "avr" + || architecture === "avr32" + || architecture === "cr16" + || architecture === "mcs51" + || architecture === "msp430" + || architecture === "rh850" + || architecture === "rl78" + || architecture === "rx" + || architecture === "stm8" + || architecture === "v850"; +} + +function supportDeprecatedFeatureWarnings(architecture) { + return architecture.startsWith("arm") + || architecture === "avr" + || architecture === "cr16" + || architecture === "mcs51" + || architecture === "msp430" + || architecture === "rh850" + || architecture === "rl78" + || architecture === "rx" + || architecture === "stm8" + || architecture === "v850"; +} + +function supportCLanguageVersion(architecture) { + return architecture !== "78k"; +} + +function supportCppLanguage(compilerFilePath) { + var baseName = FileInfo.baseName(compilerFilePath); + return baseName !== "iccs08"; +} + +// It is a 'magic' IAR-specific target architecture code. +function architectureCode(architecture) { + switch (architecture) { + case "78k": + return "26"; case "avr": - return ".r90"; + return "90"; + case "avr32": + return "82"; + case "mcs51": + return "51"; case "msp430": - return ".r43"; + return "43"; + case "v850": + return "85"; + case "m68k": + return "68"; + case "m32c": + return "48"; + case "r32c": + return "53"; + case "m16c": + return "34"; + case "cr16": + return "45"; + case "hcs12": + return "12"; + case "hcs8": + return "78"; + case "rh850": case "riscv": case "rl78": case "rx": case "sh": case "stm8": + return ""; + default: + if (architecture.startsWith("arm")) + return ""; + break; } - throw "Unable to deduce object file suffix for unsupported architecture: '" - + qbs.architecture + "'"; } -function imageFormat(qbs) { - switch (qbs.architecture) { - case "arm": - case "stm8": - case "rl78": - return "elf"; - case "mcs51": - case "avr": - case "msp430": - return "ubrof"; +function toolchainDetails(qbs) { + var architecture = qbs.architecture; + var code = architectureCode(architecture); + var details = {}; + + if (supportXLinker(architecture)) { + details.libraryPathFlag = "-I"; + details.linkerScriptFlag = "-f"; + details.linkerName = "xlink"; + details.linkerSilentFlag = "-S"; + details.linkerMapFileFlag = "-l"; + details.linkerEntryPointFlag = "-s"; + } else if (supportILinker(architecture)) { + details.libraryPathFlag = "-L"; + details.linkerScriptFlag = "--config"; + details.linkerSilentFlag = "--silent"; + details.linkerMapFileFlag = "--map"; + details.linkerEntryPointFlag = "--entry"; + details.linkerName = architecture.startsWith("arm") + ? "ilinkarm" : ("ilink" + architecture); + } + + if (supportXArchiver(architecture)) + details.archiverName = "xar"; + else if (supportIArchiver(architecture)) + details.archiverName = "iarchive"; + + var hasCode = (code !== ""); + details.staticLibrarySuffix = hasCode ? (".r" + code) : ".a"; + details.executableSuffix = hasCode + ? ((qbs.debugInformation) ? (".d" + code) : (".a" + code)) : ".out"; + details.objectSuffix = hasCode ? (".r" + code) : ".o"; + details.imageFormat = hasCode ? "ubrof" : "elf"; + + if (architecture.startsWith("arm")) { + details.compilerName = "iccarm"; + details.assemblerName = "iasmarm"; + } else if (architecture === "78k") { + details.compilerName = "icc78k"; + details.assemblerName = "a78k"; + } else if (architecture === "avr") { + details.compilerName = "iccavr"; + details.assemblerName = "aavr"; + } else if (architecture === "avr32") { + details.compilerName = "iccavr32"; + details.assemblerName = "aavr32"; + } else if (architecture === "cr16") { + details.compilerName = "icccr16c"; + details.assemblerName = "acr16c"; + } else if (architecture === "hcs12") { + details.compilerName = "icchcs12"; + details.assemblerName = "ahcs12"; + } else if (architecture === "hcs8") { + details.compilerName = "iccs08"; + details.assemblerName = "as08"; + } else if (architecture === "m16c") { + details.compilerName = "iccm16c"; + details.assemblerName = "am16c"; + } else if (architecture === "m32c") { + details.compilerName = "iccm32c"; + details.assemblerName = "am32c"; + } else if (architecture === "m68k") { + details.compilerName = "icccf"; + details.assemblerName = "acf"; + } else if (architecture === "mcs51") { + details.compilerName = "icc8051"; + details.assemblerName = "a8051"; + } else if (architecture === "msp430") { + details.compilerName = "icc430"; + details.assemblerName = "a430"; + } else if (architecture === "r32c") { + details.compilerName = "iccr32c"; + details.assemblerName = "ar32c"; + } else if (architecture === "rh850") { + details.compilerName = "iccrh850"; + details.assemblerName = "iasmrh850"; + } else if (architecture === "riscv") { + details.compilerName = "iccriscv"; + details.assemblerName = "iasmriscv"; + } else if (architecture === "rl78") { + details.compilerName = "iccrl78"; + details.assemblerName = "iasmrl78"; + } else if (architecture === "rx") { + details.compilerName = "iccrx"; + details.assemblerName = "iasmrx"; + } else if (architecture === "sh") { + details.compilerName = "iccsh"; + details.assemblerName = "iasmsh"; + } else if (architecture === "stm8") { + details.compilerName = "iccstm8"; + details.assemblerName = "iasmstm8"; + } else if (architecture === "v850") { + details.compilerName = "iccv850"; + details.assemblerName = "av850"; } - throw "Unable to deduce image format for unsupported architecture: '" - + qbs.architecture + "'"; + + return details; +} + +function guessArmArchitecture(core) { + var arch = "arm"; + if (core === "__ARM4M__") + arch += "v4m"; + else if (core === "__ARM4TM__") + arch += "v4tm"; + else if (core === "__ARM5E__") + arch += "v5e"; + else if (core === "__ARM5__") + arch += "v5"; + else if (core === "__ARM6M__") + arch += "v6m"; + else if (core === "__ARM6SM__") + arch += "v6sm"; + else if (core === "__ARM6__") + arch += "v6"; + else if (core === "__ARM7M__") + arch += "v7m"; + else if (core === "__ARM7R__") + arch += "v7r"; + return arch; } function guessArchitecture(macros) { - if (macros["__ICCARM__"] === "1") - return "arm"; + if (macros["__ICC430__"] === "1") + return "msp430"; + else if (macros["__ICC78K__"] === "1") + return "78k"; else if (macros["__ICC8051__"] === "1") return "mcs51"; + else if (macros["__ICCARM__"] === "1") + return guessArmArchitecture(macros["__CORE__"]); + else if (macros["__ICCAVR32__"] === "1") + return "avr32"; else if (macros["__ICCAVR__"] === "1") return "avr"; - else if (macros["__ICCSTM8__"] === "1") - return "stm8"; - else if (macros["__ICC430__"] === "1") - return "msp430"; + else if (macros["__ICCCF__"] === "1") + return "m68k"; + else if (macros["__ICCCR16C__"] === "1") + return "cr16"; + else if (macros["__ICCHCS12__"] === "1") + return "hcs12"; + else if (macros["__ICCM16C__"] === "1") + return "m16c"; + else if (macros["__ICCM32C__"] === "1") + return "m32c"; + else if (macros["__ICCR32C__"] === "1") + return "r32c"; + else if (macros["__ICCRH850__"] === "1") + return "rh850"; + else if (macros["__ICCRISCV__"] === "1") + return "riscv"; else if (macros["__ICCRL78__"] === "1") return "rl78"; + else if (macros["__ICCRX__"] === "1") + return "rx"; + else if (macros["__ICCS08__"] === "1") + return "hcs8"; + else if (macros["__ICCSH__"] === "1") + return "sh"; + else if (macros["__ICCSTM8__"] === "1") + return "stm8"; + else if (macros["__ICCV850__"] === "1") + return "v850"; } function guessEndianness(macros) { @@ -194,24 +381,34 @@ function guessEndianness(macros) { return "big" } -function guessVersion(macros, architecture) -{ +function guessVersion(macros, architecture) { var version = parseInt(macros["__VER__"], 10); - switch (architecture) { - case "arm": + if (architecture.startsWith("arm")) { return { major: parseInt(version / 1000000), minor: parseInt(version / 1000) % 1000, - patch: parseInt(version) % 1000, - found: true } - case "mcs51": - case "avr": - case "stm8": - case "msp430": - case "rl78": + patch: parseInt(version) % 1000 } + } else if (architecture === "78k" + || architecture === "avr" + || architecture === "avr32" + || architecture === "cr16" + || architecture === "hcs12" + || architecture === "hcs8" + || architecture === "m16c" + || architecture === "m32c" + || architecture === "m68k" + || architecture === "mcs51" + || architecture === "msp430" + || architecture === "r32c" + || architecture === "rh850" + || architecture === "riscv" + || architecture === "rl78" + || architecture === "rx" + || architecture === "sh" + || architecture === "stm8" + || architecture === "v850") { return { major: parseInt(version / 100), minor: parseInt(version % 100), - patch: 0, - found: true } + patch: 0 } } } @@ -219,16 +416,27 @@ function cppLanguageOption(compilerFilePath) { var baseName = FileInfo.baseName(compilerFilePath); switch (baseName) { case "iccarm": - case "rl78": + case "iccrh850": + case "iccriscv": + case "iccrl78": + case "iccrx": return "--c++"; + case "icc430": + case "icc78k": case "icc8051": case "iccavr": + case "iccavr32": + case "icccf": + case "icccr16c": + case "icchcs12": + case "iccm16c": + case "iccm32c": + case "iccr32c": + case "iccsh": case "iccstm8": - case "icc430": + case "iccv850": return "--ec++"; } - throw "Unable to deduce C++ language option for unsupported compiler: '" - + FileInfo.toNativeSeparators(compilerFilePath) + "'"; } function dumpMacros(compilerFilePath, tag) { @@ -238,169 +446,65 @@ function dumpMacros(compilerFilePath, tag) { var outFilePath = FileInfo.fromNativeSeparators(tempDir.path() + "/iar-macros.predef"); var args = [ inFilePath, "--predef_macros", outFilePath ]; - if (tag && tag === "cpp") + if (tag === "cpp" && supportCppLanguage(compilerFilePath)) args.push(cppLanguageOption(compilerFilePath)); var p = new Process(); + p.setWorkingDirectory(tempDir.path()); p.exec(compilerFilePath, args, true); var outFile = new TextFile(outFilePath, TextFile.ReadOnly); - var map = {}; - outFile.readAll().trim().split(/\r?\n/g).map(function (line) { - var parts = line.split(" ", 3); - map[parts[1]] = parts[2]; - }); - return map; + return Cpp.extractMacros(outFile.readAll()); } -function dumpDefaultPaths(compilerFilePath, tag) { +function dumpCompilerIncludePaths(compilerFilePath, tag) { + // We can dump the compiler include paths using the undocumented `--IDE3` flag, + // e.g. which also is used in the IAR extension for the VSCode. In this case the + // compiler procuces the console output in the following format: + // `$$TOOL_BEGIN $$VERSION "3" $$INC_BEGIN $$FILEPATH "<path\\to\\directory>" $$TOOL_END` + var tempDir = new TemporaryDir(); var inFilePath = FileInfo.fromNativeSeparators(tempDir.path() + "/empty-source.c"); var inFile = new TextFile(inFilePath, TextFile.WriteOnly); - var args = [ inFilePath, "--preinclude", "." ]; - if (tag === "cpp") + var args = ["--IDE3", inFilePath]; + if (tag === "cpp" && supportCppLanguage(compilerFilePath)) args.push(cppLanguageOption(compilerFilePath)); + var includePaths = []; var p = new Process(); - // This process should return an error, don't throw - // an error in this case. + p.setWorkingDirectory(tempDir.path()); + // It is possible that the process can return an error code in case the + // compiler does not support the `--IDE3` flag. So, don't throw an error in this case. p.exec(compilerFilePath, args, false); - var output = p.readStdErr(); - - var includePaths = []; - var pass = 0; - for (var pos = 0; pos < output.length; ++pos) { - var searchIndex = output.indexOf("searched:", pos); - if (searchIndex === -1) - break; - var startQuoteIndex = output.indexOf('"', searchIndex + 1); - if (startQuoteIndex === -1) - break; - var endQuoteIndex = output.indexOf('"', startQuoteIndex + 1); - if (endQuoteIndex === -1) - break; - pos = endQuoteIndex + 1; - - // Ignore the first path as it is not a compiler include path. - ++pass; - if (pass === 1) - continue; - - var parts = output.substring(startQuoteIndex + 1, endQuoteIndex).split("\n"); - var includePath = ""; - for (var i in parts) - includePath += parts[i].trim(); - - includePaths.push(includePath); - } - - return { - "includePaths": includePaths - }; -} - -function collectLibraryDependencies(product) { - var seen = {}; - var result = []; - - function addFilePath(filePath) { - result.push({ filePath: filePath }); - } - - function addArtifactFilePaths(dep, artifacts) { - if (!artifacts) - return; - var artifactFilePaths = artifacts.map(function(a) { return a.filePath; }); - artifactFilePaths.forEach(addFilePath); - } - - function addExternalStaticLibs(obj) { - if (!obj.cpp) - return; - function ensureArray(a) { - return Array.isArray(a) ? a : []; - } - function sanitizedModuleListProperty(obj, moduleName, propertyName) { - return ensureArray(ModUtils.sanitizedModuleProperty(obj, moduleName, propertyName)); - } - var externalLibs = [].concat( - sanitizedModuleListProperty(obj, "cpp", "staticLibraries")); - var staticLibrarySuffix = obj.moduleProperty("cpp", "staticLibrarySuffix"); - externalLibs.forEach(function(staticLibraryName) { - if (!staticLibraryName.endsWith(staticLibrarySuffix)) - staticLibraryName += staticLibrarySuffix; - addFilePath(staticLibraryName); - }); - } - - function traverse(dep) { - if (seen.hasOwnProperty(dep.name)) - return; - seen[dep.name] = true; - - if (dep.parameters.cpp && dep.parameters.cpp.link === false) - return; - - var staticLibraryArtifacts = dep.artifacts["staticlibrary"]; - if (staticLibraryArtifacts) { - dep.dependencies.forEach(traverse); - addArtifactFilePaths(dep, staticLibraryArtifacts); - addExternalStaticLibs(dep); + p.readStdOut().trim().split(/\r?\n/g).map(function(line) { + var m = line.match(/\$\$INC_BEGIN\s\$\$FILEPATH\s\"([^"]*)/); + if (m) { + var includePath = m[1].replace(/\\\\/g, '/'); + if (includePath && File.exists(includePath)) + includePaths.push(includePath); } - } - - product.dependencies.forEach(traverse); - addExternalStaticLibs(product); - return result; -} - -function compilerOutputArtifacts(input, useListing) { - var artifacts = []; - artifacts.push({ - fileTags: ["obj"], - filePath: Utilities.getHash(input.baseDir) + "/" - + input.fileName + input.cpp.objectSuffix }); - if (useListing) { - artifacts.push({ - fileTags: ["lst"], - filePath: Utilities.getHash(input.baseDir) + "/" - + input.fileName + ".lst" - }); + + if (includePaths.length === 0) { + // This can happen if the compiler does not support the `--IDE3` flag, + // e.g. IAR for S08 architecture. In this case we use fallback to the + // detection of the `inc` directory. + var includePath = FileInfo.joinPaths(FileInfo.path(compilerFilePath), "../inc/"); + if (File.exists(includePath)) + includePaths.push(includePath); } - return artifacts; -} -function applicationLinkerOutputArtifacts(product) { - var app = { - fileTags: ["application"], - filePath: FileInfo.joinPaths( - product.destinationDirectory, - PathTools.applicationFilePath(product)) - }; - var mem_map = { - fileTags: ["mem_map"], - filePath: FileInfo.joinPaths( - product.destinationDirectory, - product.targetName + ".map") - }; - return [app, mem_map] + return includePaths; } -function staticLibraryLinkerOutputArtifacts(product) { - var staticLib = { - fileTags: ["staticlibrary"], - filePath: FileInfo.joinPaths( - product.destinationDirectory, - PathTools.staticLibraryFilePath(product)) +function dumpDefaultPaths(compilerFilePath, tag) { + var includePaths = dumpCompilerIncludePaths(compilerFilePath, tag); + return { + "includePaths": includePaths }; - return [staticLib] } function compilerFlags(project, product, input, outputs, explicitlyDependsOn) { - // Determine which C-language we're compiling. - var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(outputs.obj[0].fileTags)); - var args = []; // Input. @@ -409,28 +513,15 @@ function compilerFlags(project, product, input, outputs, explicitlyDependsOn) { // Output. args.push("-o", outputs.obj[0].filePath); + // Preinclude headers. + args = args.concat(Cpp.collectPreincludePathsArguments(input, true)); + // Defines. - var allDefines = []; - var platformDefines = input.cpp.platformDefines; - if (platformDefines) - allDefines = allDefines.uniqueConcat(platformDefines); - var defines = input.cpp.defines; - if (defines) - allDefines = allDefines.uniqueConcat(defines); - args = args.concat(allDefines.map(function(define) { return "-D" + define })); + args = args.concat(Cpp.collectDefinesArguments(input)); // Includes. - var allIncludePaths = []; - var includePaths = input.cpp.includePaths; - if (includePaths) - allIncludePaths = allIncludePaths.uniqueConcat(includePaths); - var systemIncludePaths = input.cpp.systemIncludePaths; - if (systemIncludePaths) - allIncludePaths = allIncludePaths.uniqueConcat(systemIncludePaths); - var compilerIncludePaths = input.cpp.compilerIncludePaths; - if (compilerIncludePaths) - allIncludePaths = allIncludePaths.uniqueConcat(compilerIncludePaths); - args = args.concat(allIncludePaths.map(function(include) { return "-I" + include })); + args = args.concat(Cpp.collectIncludePathsArguments(input)); + args = args.concat(Cpp.collectSystemIncludePathsArguments(input)); // Silent output generation flag. args.push("--silent"); @@ -442,35 +533,40 @@ function compilerFlags(project, product, input, outputs, explicitlyDependsOn) { // Optimization flags. switch (input.cpp.optimization) { case "small": - args.push("-Ohs"); + args.push("-Ohz"); break; case "fast": - args.push("-Ohz"); + args.push("-Ohs"); break; case "none": args.push("-On"); break; } + 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": args.push("--no_warnings"); break; case "all": - args.push("--deprecated_feature_warnings=" - +"+attribute_syntax," - +"+preprocessor_extensions," - +"+segment_pragmas"); - if (tag === "cpp") - args.push("--warn_about_c_style_casts"); + if (supportDeprecatedFeatureWarnings(architecture)) { + args.push("--deprecated_feature_warnings=" + +"+attribute_syntax," + +"+preprocessor_extensions," + +"+segment_pragmas"); + } + if (tag === "cpp" && supportCppWarningAboutCStyleCast(architecture)) + args.push("--warn_about_c_style_casts"); break; } if (input.cpp.treatWarningsAsErrors) args.push("--warnings_are_errors"); // C language version flags. - if (tag === "c") { + if (tag === "c" && supportCLanguageVersion(architecture)) { var knownValues = ["c89"]; var cLanguageVersion = Cpp.languageVersion( input.cpp.cLanguageVersion, knownValues, "C"); @@ -479,58 +575,46 @@ function compilerFlags(project, product, input, outputs, explicitlyDependsOn) { args.push("--c89"); break; default: - // Default C language version is C11/C99 that + // Default C language version is C18/C11/C99 that // depends on the IAR version. break; } } - // Architecture specific flags. - switch (input.qbs.architecture) { - case "arm": - case "rl78": - // Byte order flags. - var endianness = input.cpp.endianness; - if (endianness && input.qbs.architecture === "arm") - args.push("--endian=" + endianness); - if (tag === "cpp") { - // Enable C++ language flags. - args.push("--c++"); - // Exceptions flags. - if (!input.cpp.enableExceptions) - args.push("--no_exceptions"); - // RTTI flags. - if (!input.cpp.enableRtti) - args.push("--no_rtti"); - } - break; - case "stm8": - case "mcs51": - case "avr": - case "msp430": - // Enable C++ language flags. - if (tag === "cpp") - args.push("--ec++"); - break; + // C++ language version flags. + var compilerFilePath = input.cpp.compilerPath; + if (tag === "cpp" && supportCppLanguage(compilerFilePath)) { + // C++ language flag. + var cppOption = cppLanguageOption(compilerFilePath); + args.push(cppOption); + + // Exceptions flag. + var enableExceptions = input.cpp.enableExceptions; + if (!enableExceptions && supportCppExceptions(architecture)) + args.push("--no_exceptions"); + + // RTTI flag. + var enableRtti = input.cpp.enableRtti; + if (!enableRtti && supportCppRtti(architecture)) + args.push("--no_rtti"); } + // Byte order flags. + var endianness = input.cpp.endianness; + if (endianness && supportEndianness(architecture)) + args.push("--endian=" + endianness); + // Listing files generation flag. - if (product.cpp.generateCompilerListingFiles) + if (input.cpp.generateCompilerListingFiles) args.push("-l", outputs.lst[0].filePath); // Misc flags. - args = args.concat(ModUtils.moduleProperty(input, "platformFlags"), - ModUtils.moduleProperty(input, "flags"), - ModUtils.moduleProperty(input, "platformFlags", tag), - ModUtils.moduleProperty(input, "flags", tag), - ModUtils.moduleProperty(input, "driverFlags", tag)); + args = args.concat(Cpp.collectMiscCompilerArguments(input, tag), + Cpp.collectMiscDriverArguments(product)); return args; } function assemblerFlags(project, product, input, outputs, explicitlyDependsOn) { - // Determine which C-language we"re compiling - var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(outputs.obj[0].fileTags)); - var args = []; // Input. @@ -539,24 +623,23 @@ function assemblerFlags(project, product, input, outputs, explicitlyDependsOn) { // Output. args.push("-o", outputs.obj[0].filePath); + var architecture = input.qbs.architecture; + + // The `--preinclude` flag is only supported for a certain + // set of assemblers, not for all. + if (supportIAssembler(architecture)) + args = args.concat(Cpp.collectPreincludePathsArguments(input)); + // Includes. - var allIncludePaths = []; - var systemIncludePaths = input.cpp.systemIncludePaths; - if (systemIncludePaths) - allIncludePaths = allIncludePaths.uniqueConcat(systemIncludePaths); - var compilerIncludePaths = input.cpp.compilerIncludePaths; - if (compilerIncludePaths) - allIncludePaths = allIncludePaths.uniqueConcat(compilerIncludePaths); - args = args.concat(allIncludePaths.map(function(include) { return "-I" + include })); + args = args.concat(Cpp.collectIncludePathsArguments(input)); + args = args.concat(Cpp.collectSystemIncludePathsArguments(input)); // Debug information flags. if (input.cpp.debugInformation) args.push("-r"); // Architecture specific flags. - switch (input.qbs.architecture) { - case "stm8": - case "rl78": + if (supportIAssembler(architecture)) { // Silent output generation flag. args.push("--silent"); // Warning level flags. @@ -564,104 +647,79 @@ function assemblerFlags(project, product, input, outputs, explicitlyDependsOn) { args.push("--no_warnings"); if (input.cpp.treatWarningsAsErrors) args.push("--warnings_are_errors"); - break; - default: + } else if (supportXAssembler(architecture)){ // Silent output generation flag. args.push("-S"); // Warning level flags. args.push("-w" + (input.cpp.warningLevel === "none" ? "-" : "+")); - break; } + // Byte order flags. + var endianness = input.cpp.endianness; + if (endianness && supportEndianness(architecture)) + args.push("--endian", endianness); + // Listing files generation flag. - if (product.cpp.generateAssemblerListingFiles) + if (input.cpp.generateAssemblerListingFiles) args.push("-l", outputs.lst[0].filePath); // Misc flags. - args = args.concat(ModUtils.moduleProperty(input, "platformFlags", tag), - ModUtils.moduleProperty(input, "flags", tag), - ModUtils.moduleProperty(input, "driverFlags", tag)); + args = args.concat(Cpp.collectMiscAssemblerArguments(input, "asm")); return args; } -function linkerFlags(project, product, input, outputs) { +function linkerFlags(project, product, inputs, outputs) { var args = []; // Inputs. - if (inputs.obj) - args = args.concat(inputs.obj.map(function(obj) { return obj.filePath })); + args = args.concat(Cpp.collectLinkerObjectPaths(inputs)); // Output. args.push("-o", outputs.application[0].filePath); // Library paths. - var allLibraryPaths = []; - var libraryPaths = product.cpp.libraryPaths; - if (libraryPaths) - allLibraryPaths = allLibraryPaths.uniqueConcat(libraryPaths); - var distributionLibraryPaths = product.cpp.distributionLibraryPaths; - if (distributionLibraryPaths) - allLibraryPaths = allLibraryPaths.uniqueConcat(distributionLibraryPaths); - args = args.concat(allLibraryPaths.map(function(path) { return '-L' + path })); + args = args.concat(Cpp.collectLibraryPathsArguments(product)); // Library dependencies. - var libraryDependencies = collectLibraryDependencies(product); - if (libraryDependencies) - args = args.concat(libraryDependencies.map(function(dep) { return dep.filePath })); + args = args.concat(Cpp.collectLibraryDependenciesArguments(product)); // Linker scripts. - var linkerScripts = inputs.linkerscript - ? inputs.linkerscript.map(function(a) { return a.filePath; }) : []; + args = args.concat(Cpp.collectLinkerScriptPathsArguments(product, inputs, true)); - // Architecture specific flags. - switch (product.qbs.architecture) { - case "arm": - case "stm8": - case "rl78": - // Silent output generation flag. - args.push("--silent"); - // Map file generation flag. - if (product.cpp.generateLinkerMapFile) - args.push("--map", outputs.mem_map[0].filePath); - // Entry point flag. - if (product.cpp.entryPoint) - args.push("--entry", product.cpp.entryPoint); - // Linker scripts flags. - linkerScripts.forEach(function(script) { args.push("--config", script); }); - break; - case "mcs51": - case "avr": - case "msp430": - // Silent output generation flag. - args.push("-S"); - // Debug information flag. + // Silent output generation flag. + args.push(product.cpp.linkerSilentFlag); + + // Map file generation flag. + if (product.cpp.generateLinkerMapFile) + args.push(product.cpp.linkerMapFileFlag, outputs.mem_map[0].filePath); + + // Entry point flag. + if (product.cpp.entryPoint) + args.push(product.cpp.linkerEntryPointFlag, product.cpp.entryPoint); + + // Debug information flag. + if (supportXLinker(product.qbs.architecture)) { if (product.cpp.debugInformation) args.push("-rt"); - // Map file generation flag. - if (product.cpp.generateLinkerMapFile) - args.push("-l", outputs.mem_map[0].filePath); - // Entry point flag. - if (product.cpp.entryPoint) - args.push("-s", product.cpp.entryPoint); - // Linker scripts flags. - linkerScripts.forEach(function(script) { args.push("-f", script); }); - break; } // Misc flags. - args = args.concat(ModUtils.moduleProperty(product, "driverLinkerFlags")); + args = args.concat(Cpp.collectMiscEscapableLinkerArguments(product), + Cpp.collectMiscLinkerArguments(product), + Cpp.collectMiscDriverArguments(product)); return args; } -function archiverFlags(project, product, input, outputs) { +function archiverFlags(project, product, inputs, outputs) { var args = []; // Inputs. - if (inputs.obj) - args = args.concat(inputs.obj.map(function(obj) { return obj.filePath })); + args = args.concat(Cpp.collectLinkerObjectPaths(inputs)); // Output. - args.push("--create"); + var architecture = product.qbs.architecture; + if (supportIArchiver(architecture)) + args.push("--create"); args.push("-o", outputs.staticlibrary[0].filePath); return args; @@ -673,6 +731,7 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli var cmd = new Command(compilerPath, args); cmd.description = "compiling " + input.fileName; cmd.highlight = "compiler"; + cmd.jobPool = "compiler"; return [cmd]; } @@ -682,25 +741,28 @@ function prepareAssembler(project, product, inputs, outputs, input, output, expl var cmd = new Command(assemblerPath, args); cmd.description = "assembling " + input.fileName; cmd.highlight = "compiler"; + cmd.jobPool = "assembler"; return [cmd]; } function prepareLinker(project, product, inputs, outputs, input, output) { var primaryOutput = outputs.application[0]; - var args = linkerFlags(project, product, input, outputs); + var args = linkerFlags(project, product, inputs, outputs); var linkerPath = product.cpp.linkerPath; var cmd = new Command(linkerPath, args); cmd.description = "linking " + primaryOutput.fileName; cmd.highlight = "linker"; + cmd.jobPool = "linker"; return [cmd]; } function prepareArchiver(project, product, inputs, outputs, input, output) { - var args = archiverFlags(project, product, input, outputs); + var args = archiverFlags(project, product, inputs, outputs); var archiverPath = product.cpp.archiverPath; var cmd = new Command(archiverPath, args); - cmd.description = "linking " + output.fileName; + cmd.description = "creating " + output.fileName; cmd.highlight = "linker"; + cmd.jobPool = "linker"; cmd.stdoutFilterFunction = function(output) { return ""; }; diff --git a/share/qbs/modules/cpp/iar.qbs b/share/qbs/modules/cpp/iar.qbs index bc226389b..558635ef5 100644 --- a/share/qbs/modules/cpp/iar.qbs +++ b/share/qbs/modules/cpp/iar.qbs @@ -28,17 +28,16 @@ ** ****************************************************************************/ -import qbs 1.0 import qbs.File import qbs.FileInfo -import qbs.ModUtils import qbs.PathTools import qbs.Probes import qbs.Utilities +import "cpp.js" as Cpp import "iar.js" as IAR CppModule { - condition: qbs.toolchain && qbs.toolchain.contains("iar") + condition: qbs.toolchain && qbs.toolchain.includes("iar") Probes.BinaryProbe { id: compilerPathProbe @@ -54,6 +53,7 @@ CppModule { } qbs.architecture: iarProbe.found ? iarProbe.architecture : original + qbs.targetPlatform: "none" compilerVersionMajor: iarProbe.versionMajor compilerVersionMinor: iarProbe.versionMinor @@ -63,45 +63,58 @@ CppModule { compilerDefinesByLanguage: iarProbe.compilerDefinesByLanguage compilerIncludePaths: iarProbe.includePaths - property string toolchainInstallPath: compilerPathProbe.found - ? compilerPathProbe.path : undefined - - property string compilerExtension: qbs.hostOS.contains("windows") ? ".exe" : "" + toolchainInstallPath: compilerPathProbe.found ? compilerPathProbe.path : undefined /* Work-around for QtCreator which expects these properties to exist. */ property string cCompilerName: compilerName property string cxxCompilerName: compilerName - compilerName: IAR.compilerName(qbs) + compilerExtension + compilerName: toolchainDetails.compilerName + compilerExtension compilerPath: FileInfo.joinPaths(toolchainInstallPath, compilerName) - assemblerName: IAR.assemblerName(qbs) + compilerExtension + assemblerName: toolchainDetails.assemblerName + compilerExtension assemblerPath: FileInfo.joinPaths(toolchainInstallPath, assemblerName) - linkerName: IAR.linkerName(qbs) + compilerExtension + linkerName: toolchainDetails.linkerName + compilerExtension linkerPath: FileInfo.joinPaths(toolchainInstallPath, linkerName) - property string archiverName: IAR.archiverName(qbs) + compilerExtension + property string archiverName: toolchainDetails.archiverName + compilerExtension property string archiverPath: FileInfo.joinPaths(toolchainInstallPath, archiverName) runtimeLibrary: "static" - staticLibrarySuffix: IAR.staticLibrarySuffix(qbs) - executableSuffix: IAR.executableSuffix(qbs) - - property string objectSuffix: IAR.objectSuffix(qbs) + staticLibrarySuffix: toolchainDetails.staticLibrarySuffix + executableSuffix: toolchainDetails.executableSuffix + objectSuffix: toolchainDetails.objectSuffix - imageFormat: IAR.imageFormat(qbs) + imageFormat: toolchainDetails.imageFormat enableExceptions: false enableRtti: false + defineFlag: "-D" + includeFlag: "-I" + systemIncludeFlag: "-I" + preincludeFlag: "--preinclude" + libraryDependencyFlag: "" + libraryPathFlag: toolchainDetails.libraryPathFlag + linkerScriptFlag: toolchainDetails.linkerScriptFlag + + property string linkerSilentFlag: toolchainDetails.linkerSilentFlag + property string linkerMapFileFlag: toolchainDetails.linkerMapFileFlag + property string linkerEntryPointFlag: toolchainDetails.linkerEntryPointFlag + + toolchainDetails: IAR.toolchainDetails(qbs) + + knownArchitectures: ["78k", "arm", "avr", "avr32", "cr16", + "hcs12", "hcs8", "m16c", "m32c", "m68k", "mcs51", "msp430", + "r32c", "rh850", "riscv", "rl78", "rx", "sh", "stm8", "v850"] + Rule { id: assembler inputs: ["asm"] - outputFileTags: ["obj", "lst"] - outputArtifacts: IAR.compilerOutputArtifacts( - input, input.cpp.generateAssemblerListingFiles) + outputFileTags: Cpp.assemblerOutputTags(generateAssemblerListingFiles) + outputArtifacts: Cpp.assemblerOutputArtifacts(input) prepare: IAR.prepareAssembler.apply(IAR, arguments) } @@ -114,9 +127,8 @@ CppModule { id: compiler inputs: ["cpp", "c"] auxiliaryInputs: ["hpp"] - outputFileTags: ["obj", "lst"] - outputArtifacts: IAR.compilerOutputArtifacts( - input, input.cpp.generateCompilerListingFiles) + outputFileTags: Cpp.compilerOutputTags(generateCompilerListingFiles) + outputArtifacts: Cpp.compilerOutputArtifacts(input) prepare: IAR.prepareCompiler.apply(IAR, arguments) } @@ -125,8 +137,8 @@ CppModule { multiplex: true inputs: ["obj", "linkerscript"] inputsFromDependencies: ["staticlibrary"] - outputFileTags: ["application", "mem_map"] - outputArtifacts: IAR.applicationLinkerOutputArtifacts(product) + outputFileTags: Cpp.applicationLinkerOutputTags(generateLinkerMapFile) + outputArtifacts: Cpp.applicationLinkerOutputArtifacts(product) prepare: IAR.prepareLinker.apply(IAR, arguments) } @@ -135,8 +147,8 @@ CppModule { multiplex: true inputs: ["obj"] inputsFromDependencies: ["staticlibrary"] - outputFileTags: ["staticlibrary"] - outputArtifacts: IAR.staticLibraryLinkerOutputArtifacts(product) + outputFileTags: Cpp.staticLibraryLinkerOutputTags() + outputArtifacts: Cpp.staticLibraryLinkerOutputArtifacts(product) prepare: IAR.prepareArchiver.apply(IAR, arguments) } } diff --git a/share/qbs/modules/cpp/ios-gcc.qbs b/share/qbs/modules/cpp/ios-gcc.qbs index 0ff99679f..968b873da 100644 --- a/share/qbs/modules/cpp/ios-gcc.qbs +++ b/share/qbs/modules/cpp/ios-gcc.qbs @@ -31,21 +31,30 @@ import qbs.DarwinTools import qbs.File import qbs.FileInfo +import qbs.Host import qbs.ModUtils import qbs.Utilities DarwinGCC { priority: 1 - condition: qbs.targetOS.contains('ios') && - qbs.toolchain && qbs.toolchain.contains('gcc') + condition: qbs.targetOS.includes('ios') && + qbs.toolchain && qbs.toolchain.includes('gcc') + minimumIosVersion: { + if (Host.architecture() == "armv7a") + return "6.0"; + // XCode 12 requres version (at least 8.0) to be present in the -target triplet + // when compiling for ios-simulator + if (xcode.present && Utilities.versionCompare(xcode.version, "12.0") >= 0) + return "8.0"; + } targetSystem: "ios" + (minimumIosVersion || "") minimumDarwinVersion: minimumIosVersion - minimumDarwinVersionCompilerFlag: qbs.targetOS.contains("ios-simulator") + minimumDarwinVersionCompilerFlag: qbs.targetOS.includes("ios-simulator") ? "-mios-simulator-version-min" : "-miphoneos-version-min" - minimumDarwinVersionLinkerFlag: qbs.targetOS.contains("ios-simulator") + minimumDarwinVersionLinkerFlag: qbs.targetOS.includes("ios-simulator") ? "-ios_simulator_version_min" : "-iphoneos_version_min" @@ -60,13 +69,13 @@ DarwinGCC { readonly property stringList simulatorObjcFlags: { // default in Xcode and also required for building 32-bit Simulator binaries with ARC // since the default ABI version is 0 for 32-bit targets - return qbs.targetOS.contains("ios-simulator") + return qbs.targetOS.includes("ios-simulator") ? ["-fobjc-abi-version=2", "-fobjc-legacy-dispatch"] : []; } Rule { - condition: !product.qbs.targetOS.contains("ios-simulator") + condition: !product.qbs.targetOS.includes("ios-simulator") inputsFromDependencies: ["bundle.content"] Artifact { diff --git a/share/qbs/modules/cpp/keil.js b/share/qbs/modules/cpp/keil.js index befcc5a33..8f3297aa2 100644 --- a/share/qbs/modules/cpp/keil.js +++ b/share/qbs/modules/cpp/keil.js @@ -39,382 +39,456 @@ var TemporaryDir = require("qbs.TemporaryDir"); var TextFile = require("qbs.TextFile"); var Utilities = require("qbs.Utilities"); -function compilerName(qbs) { - switch (qbs.architecture) { - case "mcs51": - return "c51"; - case "arm": - return "armcc"; - } - throw "Unable to deduce compiler name for unsupported architecture: '" - + qbs.architecture + "'"; +function isMcs51Architecture(architecture) { + return architecture === "mcs51"; } -function assemblerName(qbs) { - switch (qbs.architecture) { - case "mcs51": - return "a51"; - case "arm": - return "armasm"; - } - throw "Unable to deduce assembler name for unsupported architecture: '" - + qbs.architecture + "'"; +function isMcs251Architecture(architecture) { + return architecture === "mcs251"; } -function linkerName(qbs) { - switch (qbs.architecture) { - case "mcs51": - return "bl51"; - case "arm": - return "armlink"; - } - throw "Unable to deduce linker name for unsupported architecture: '" - + qbs.architecture + "'"; +function isMcsArchitecture(architecture) { + return isMcs51Architecture(architecture) + || isMcs251Architecture(architecture); } -function archiverName(qbs) { - switch (qbs.architecture) { - case "mcs51": - return "lib51"; - case "arm": - return "armar"; - } - throw "Unable to deduce archiver name for unsupported architecture: '" - + qbs.architecture + "'"; +function isC166Architecture(architecture) { + return architecture === "c166"; } -function staticLibrarySuffix(qbs) { - switch (qbs.architecture) { - case "mcs51": - case "arm": - return ".lib"; - } - throw "Unable to deduce static library suffix for unsupported architecture: '" - + qbs.architecture + "'"; +function isArmArchitecture(architecture) { + return architecture.startsWith("arm"); } -function executableSuffix(qbs) { - switch (qbs.architecture) { - case "mcs51": - return ".abs"; - case "arm": - return ".axf"; - } - throw "Unable to deduce executable suffix for unsupported architecture: '" - + qbs.architecture + "'"; +function isMcsCompiler(compilerPath) { + var base = FileInfo.baseName(compilerPath).toLowerCase(); + return base === "c51" || base === "cx51" || base === "c251"; } -function objectSuffix(qbs) { - switch (qbs.architecture) { - case "mcs51": - return ".obj"; - case "arm": - return ".o"; - } - throw "Unable to deduce object file suffix for unsupported architecture: '" - + qbs.architecture + "'"; -} - -function imageFormat(qbs) { - switch (qbs.architecture) { - case "mcs51": - // Keil OMF51 or OMF2 Object Module Format (which is an - // extension of the original Intel OMF51). - return "omf"; - case "arm": - return "elf"; +function isC166Compiler(compilerPath) { + return FileInfo.baseName(compilerPath).toLowerCase() === "c166"; +} + +function isArmCCCompiler(compilerPath) { + return FileInfo.baseName(compilerPath).toLowerCase() === "armcc"; +} + +function isArmClangCompiler(compilerPath) { + return FileInfo.baseName(compilerPath).toLowerCase() === "armclang"; +} + +function preincludeFlag(compilerPath) { + if (isArmCCCompiler(compilerPath)) + return "--preinclude"; + else if (isArmClangCompiler(compilerPath)) + return "-include"; +} + +function toolchainDetails(qbs) { + var architecture = qbs.architecture; + if (isMcs51Architecture(architecture)) { + return { + "imageFormat": "omf", + "linkerMapSuffix":".m51", + "objectSuffix": ".obj", + "executableSuffix": ".abs", + "compilerName": "c51", + "assemblerName": "a51", + "linkerName": "bl51", + "archiverName": "lib51" + }; + } else if (isMcs251Architecture(architecture)) { + return { + "imageFormat": "omf", + "linkerMapSuffix":".m51", + "objectSuffix": ".obj", + "executableSuffix": ".abs", + "compilerName": "c251", + "assemblerName": "a251", + "linkerName": "l251", + "archiverName": "lib251" + }; + } else if (isC166Architecture(architecture)) { + return { + "imageFormat": "omf", + "linkerMapSuffix":".m66", + "objectSuffix": ".obj", + "executableSuffix": ".abs", + "compilerName": "c166", + "assemblerName": "a166", + "linkerName": "l166", + "archiverName": "lib166" + }; + } else if (isArmArchitecture(architecture)) { + return { + "imageFormat": "elf", + "linkerMapSuffix":".map", + "objectSuffix": ".o", + "executableSuffix": ".axf", + "disassemblerName": "fromelf", + "compilerName": "armcc", + "assemblerName": "armasm", + "linkerName": "armlink", + "archiverName": "armar" + }; } - throw "Unable to deduce image format for unsupported architecture: '" - + qbs.architecture + "'"; +} + +function guessArmCCArchitecture(targetArchArm, targetArchThumb) { + var arch = "arm"; + if (targetArchArm === "4" && targetArchThumb === "0") + arch += "v4"; + else if (targetArchArm === "4" && targetArchThumb === "1") + arch += "v4t"; + else if (targetArchArm === "5" && targetArchThumb === "2") + arch += "v5t"; + else if (targetArchArm === "6" && targetArchThumb === "3") + arch += "v6"; + else if (targetArchArm === "6" && targetArchThumb === "4") + arch += "v6t2"; + else if (targetArchArm === "0" && targetArchThumb === "3") + arch += "v6m"; + else if (targetArchArm === "7" && targetArchThumb === "4") + arch += "v7r"; + else if (targetArchArm === "0" && targetArchThumb === "4") + arch += "v7m"; + return arch; +} + +function guessArmClangArchitecture(targetArchArm, targetArchProfile) { + targetArchProfile = targetArchProfile.replace(/'/g, ""); + var arch = "arm"; + if (targetArchArm !== "" && targetArchProfile !== "") + arch += "v" + targetArchArm + targetArchProfile.toLowerCase(); + return arch; } function guessArchitecture(macros) { if (macros["__C51__"]) return "mcs51"; - else if (macros["__CC_ARM"] === 1) - return "arm"; + else if (macros["__C251__"]) + return "mcs251"; + else if (macros["__C166__"]) + return "c166"; + else if (macros["__CC_ARM"] === "1") + return guessArmCCArchitecture(macros["__TARGET_ARCH_ARM"], macros["__TARGET_ARCH_THUMB"]); + else if (macros["__clang__"] === "1" && macros["__arm__"] === "1") + return guessArmClangArchitecture(macros["__ARM_ARCH"], macros["__ARM_ARCH_PROFILE"]); } function guessEndianness(macros) { - if (macros["__C51__"]) { + if (macros["__C51__"] || macros["__C251__"]) { // 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 + // * http://www.keil.com/support/man/docs/c251/c251_ap_2bytescalar.htm + // * http://www.keil.com/support/man/docs/c251/c251_ap_4bytescalar.htm return "big"; - } else if (macros["__ARMCC_VERSION"]) { + } else if (macros["__C166__"]) { + // The C166 processors are 16-bit. So, the data with an integer type + // represented by more than one byte is stored as little endian in the + // Keil toolchain. See for more info: + // * http://www.keil.com/support/man/docs/c166/c166_ap_ints.htm + // * http://www.keil.com/support/man/docs/c166/c166_ap_longs.htm + return "little"; + } else if (macros["__CC_ARM"]) { return macros["__BIG_ENDIAN"] ? "big" : "little"; + } else if (macros["__clang__"] && macros["__arm__"]) { + switch (macros["__BYTE_ORDER__"]) { + case "__ORDER_BIG_ENDIAN__": + return "big"; + case "__ORDER_LITTLE_ENDIAN__": + return "little"; + } } } function guessVersion(macros) { - if (macros["__C51__"]) { - var mcsVersion = macros["__C51__"]; + if (macros["__C51__"] || macros["__C251__"]) { + var mcsVersion = macros["__C51__"] || macros["__C251__"]; return { major: parseInt(mcsVersion / 100), minor: parseInt(mcsVersion % 100), - patch: 0, - found: true } - } else if (macros["__CC_ARM"]) { + patch: 0 } + } else if (macros["__C166__"]) { + var xcVersion = macros["__C166__"]; + return { major: parseInt(xcVersion / 100), + minor: parseInt(xcVersion % 100), + patch: 0 } + } else if (macros["__CC_ARM"] || macros["__clang__"]) { var armVersion = macros["__ARMCC_VERSION"]; return { major: parseInt(armVersion / 1000000), minor: parseInt(armVersion / 10000) % 100, - patch: parseInt(armVersion) % 10000, - found: true } + patch: parseInt(armVersion) % 10000 } } } -// 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, tag) { - // C51 compiler support only C language. +function dumpMcsCompilerMacros(compilerFilePath, tag) { + // C51 or C251 compiler support only C language. if (tag === "cpp") return {}; - function createDumpMacrosFile() { - var td = new TemporaryDir(); - var fn = FileInfo.fromNativeSeparators(td.path() + "/dump-macros.c"); - var tf = new TextFile(fn, TextFile.WriteOnly); - tf.writeLine("#define VALUE_TO_STRING(x) #x"); - tf.writeLine("#define VALUE(x) VALUE_TO_STRING(x)"); - tf.writeLine("#define VAR_NAME_VALUE(var) \"\"\"|\"#var\"|\"VALUE(var)"); - tf.writeLine("#ifdef __C51__"); - tf.writeLine("#pragma message(VAR_NAME_VALUE(__C51__))"); - tf.writeLine("#endif"); - tf.close(); - return fn; + // Note: The C51 or C251 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)"); + + // Predefined keys for C51 and C251 compilers, see details: + // * https://www.keil.com/support/man/docs/c51/c51_pp_predefmacroconst.htm + // * https://www.keil.com/support/man/docs/c251/c251_pp_predefmacroconst.htm + var keys = ["__C51__", "__CX51__", "__C251__", "__MODEL__", + "__STDC__", "__FLOAT64__", "__MODSRC__"]; + + // For C51 compiler. + outputFile.writeLine("#if defined(__C51__) || defined(__CX51__)"); + outputFile.writeLine("# define VAR_NAME_VALUE(var) \"(\"\"\"\"|\"#var\"|\"VALUE(var)\"|\"\"\"\")\""); + 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.writeLine("#endif"); + + // For C251 compiler. + outputFile.writeLine("#if defined(__C251__)"); + outputFile.writeLine("# define VAR_NAME_VALUE(var) \"\"|#var|VALUE(var)|\"\""); + for (var i in keys) { + var key = keys[i]; + outputFile.writeLine("# if defined (" + key + ")"); + outputFile.writeLine("# warning (VAR_NAME_VALUE(" + key + "))"); + outputFile.writeLine("# endif"); } + outputFile.writeLine("#endif"); - var fn = createDumpMacrosFile(); - var p = new Process(); - p.exec(compilerFilePath, [ fn ], false); + outputFile.close(); + + var process = new Process(); + process.exec(compilerFilePath, [outputFilePath], false); + File.remove(outputFilePath); var map = {}; - p.readStdOut().trim().split(/\r?\n/g).map(function(line) { - var parts = line.split("\"|\"", 3); - map[parts[1]] = parts[2]; + process.readStdOut().trim().split(/\r?\n/g).map(function(line) { + var parts = line.split("\"|\"", 4); + if (parts.length === 4) + map[parts[1]] = parts[2]; }); return map; } -function dumpArmCompilerMacros(compilerFilePath, tag, nullDevice) { - var args = [ "-E", "--list-macros", nullDevice ]; +function dumpC166CompilerMacros(compilerFilePath, tag) { + // C166 compiler support only C language. if (tag === "cpp") - args.push("--cpp"); + return {}; - var p = new Process(); - p.exec(compilerFilePath, args, 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; -} + // Note: The C166 compiler does not support the predefined + // macros dumping. Also, it does not support the '#pragma' and + // '#message|warning|error' directives properly (it is impossible + // to print to console the value of macro). + // 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, e.g: + // + // *** WARNING C320 IN LINE 41 OF c51.c: __C166__ + // *** WARNING C2 IN LINE 42 OF c51.c: '757': unknown #pragma/control, line ignored + // + // where the '__C166__' is a key, and the '757' is a value. + + var outputDirectory = new TemporaryDir(); + var outputFilePath = FileInfo.fromNativeSeparators(outputDirectory.path() + "/dump-macros.c"); + var outputFile = new TextFile(outputFilePath, TextFile.WriteOnly); + + // Predefined keys for C166 compiler, see details: + // * https://www.keil.com/support/man/docs/c166/c166_pp_predefmacroconst.htm + var keys = ["__C166__", "__DUS__", "__MAC__", "__MOD167__", + "__MODEL__", "__MODV2__", "__SAVEMAC__", "__STDC__"]; + + // For C166 compiler. + outputFile.writeLine("#if defined(__C166__)"); + for (var i in keys) { + var key = keys[i]; + outputFile.writeLine("# if defined (" + key + ")"); + outputFile.writeLine("# warning " + key); + outputFile.writeLine("# pragma " + key); + outputFile.writeLine("# endif"); + } + outputFile.writeLine("#endif"); -function dumpMacros(compilerFilePath, tag, nullDevice) { - var map1 = dumpC51CompilerMacros(compilerFilePath, tag, nullDevice); - var map2 = dumpArmCompilerMacros(compilerFilePath, tag, nullDevice); - var map = {}; - for (var key1 in map1) - map[key1] = map1[key1]; - for (var key2 in map2) - map[key2] = map2[key2]; - return map; -} + outputFile.close(); -function dumpDefaultPaths(compilerFilePath, architecture) { - var incDir = (architecture === "arm") ? "include" : "inc"; - var includePath = compilerFilePath.replace(/bin[\\\/](.*)$/i, incDir); - return { - "includePaths": [includePath] + function extractKey(line, knownKeys) { + for (var i in keys) { + var key = knownKeys[i]; + var regexp = new RegExp("^\\*\\*\\* WARNING C320 IN LINE .+: (" + key + ")$"); + var match = regexp.exec(line); + if (match) + return key; + } }; -} - -function adjustPathsToWindowsSeparators(sourcePaths) { - var resulingPaths = []; - sourcePaths.forEach(function(path) { - resulingPaths.push(FileInfo.toWindowsSeparators(path)); - }); - return resulingPaths; -} -function getMaxExitCode(architecture) { - if (architecture === "mcs51") - return 1; - else if (architecture === "arm") - return 0; -} + function extractValue(line) { + var regexp = new RegExp("^\\*\\*\\* WARNING C2 IN LINE .+'(.+)':.+$"); + var match = regexp.exec(line); + if (match) + return match[1]; + }; -function collectLibraryDependencies(product) { - var seen = {}; - var result = []; + var process = new Process(); + process.exec(compilerFilePath, [outputFilePath], false); + File.remove(outputFilePath); + var lines = process.readStdOut().trim().split(/\r?\n/g); + var map = {}; + for (var i = 0; i < lines.length; ++i) { + // First line should contains the macro key. + var keyLine = lines[i]; + var key = extractKey(keyLine, keys); + if (!key) + continue; + + i += 1; + if (i >= lines.length) + break; - function addFilePath(filePath) { - result.push({ filePath: filePath }); + // Second line should contains the macro value. + var valueLine = lines[i]; + var value = extractValue(valueLine); + if (!value) + continue; + map[key] = value; } + return map; +} - function addArtifactFilePaths(dep, artifacts) { - if (!artifacts) - return; - var artifactFilePaths = artifacts.map(function(a) { return a.filePath; }); - artifactFilePaths.forEach(addFilePath); - } +function dumpArmCCCompilerMacros(compilerFilePath, tag, nullDevice) { + var args = [ "-E", "--list-macros", "--cpu=cortex-m0", nullDevice ]; + if (tag === "cpp") + args.push("--cpp"); - function addExternalStaticLibs(obj) { - if (!obj.cpp) - return; - function ensureArray(a) { - return Array.isArray(a) ? a : []; - } - function sanitizedModuleListProperty(obj, moduleName, propertyName) { - return ensureArray(ModUtils.sanitizedModuleProperty(obj, moduleName, propertyName)); - } - var externalLibs = [].concat( - sanitizedModuleListProperty(obj, "cpp", "staticLibraries")); - var staticLibrarySuffix = obj.moduleProperty("cpp", "staticLibrarySuffix"); - externalLibs.forEach(function(staticLibraryName) { - if (!staticLibraryName.endsWith(staticLibrarySuffix)) - staticLibraryName += staticLibrarySuffix; - addFilePath(staticLibraryName); - }); - } + var p = new Process(); + p.exec(compilerFilePath, args, false); + return Cpp.extractMacros(p.readStdOut()); +} - function traverse(dep) { - if (seen.hasOwnProperty(dep.name)) - return; - seen[dep.name] = true; +function dumpArmClangCompilerMacros(compilerFilePath, tag, nullDevice) { + var args = [ "--target=arm-arm-none-eabi", "-mcpu=cortex-m0", "-dM", "-E", + "-x", ((tag === "cpp") ? "c++" : "c"), nullDevice ]; + var p = new Process(); + p.exec(compilerFilePath, args, false); + return Cpp.extractMacros(p.readStdOut()); +} - if (dep.parameters.cpp && dep.parameters.cpp.link === false) - return; +function dumpMacros(compilerFilePath, tag, nullDevice) { + if (isMcsCompiler(compilerFilePath)) + return dumpMcsCompilerMacros(compilerFilePath, tag, nullDevice); + else if (isC166Compiler(compilerFilePath)) + return dumpC166CompilerMacros(compilerFilePath, tag, nullDevice); + else if (isArmCCCompiler(compilerFilePath)) + return dumpArmCCCompilerMacros(compilerFilePath, tag, nullDevice); + else if (isArmClangCompiler(compilerFilePath)) + return dumpArmClangCompilerMacros(compilerFilePath, tag, nullDevice); + return {}; +} - var staticLibraryArtifacts = dep.artifacts["staticlibrary"]; - if (staticLibraryArtifacts) { - dep.dependencies.forEach(traverse); - addArtifactFilePaths(dep, staticLibraryArtifacts); - addExternalStaticLibs(dep); - } - } +function dumpMcsCompilerIncludePaths(compilerFilePath) { + var includePath = compilerFilePath.replace(/bin[\\\/](.*)$/i, "inc"); + return [includePath]; +} - product.dependencies.forEach(traverse); - addExternalStaticLibs(product); - return result; -} - -function filterStdOutput(cmd) { - cmd.stdoutFilterFunction = function(output) { - var sourceLines = output.split("\n"); - var filteredLines = []; - for (var i in sourceLines) { - if (sourceLines[i].startsWith("***") - || sourceLines[i].startsWith(">>") - || sourceLines[i].startsWith(" ") - || sourceLines[i].startsWith("Program Size:") - || sourceLines[i].startsWith("A51 FATAL") - || sourceLines[i].startsWith("C51 FATAL") - || sourceLines[i].startsWith("ASSEMBLER INVOKED BY") - || sourceLines[i].startsWith("LOC OBJ LINE SOURCE") - ) { - filteredLines.push(sourceLines[i]); - } else { - var regexp = /^([0-9A-F]{4})/; - if (regexp.exec(sourceLines[i])) - filteredLines.push(sourceLines[i]); - } - } - return filteredLines.join("\n"); - }; +function dumpC166CompilerIncludePaths(compilerFilePath) { + // Same as for MCS compiler. + return dumpMcsCompilerIncludePaths(compilerFilePath); } -function compilerOutputArtifacts(input, useListing) { - var artifacts = []; - artifacts.push({ - fileTags: ["obj"], - filePath: Utilities.getHash(input.baseDir) + "/" - + input.fileName + input.cpp.objectSuffix - }); - if (useListing) { - artifacts.push({ - fileTags: ["lst"], - filePath: Utilities.getHash(input.baseDir) + "/" - + (input.cpp.architecture === "mcs51" - ? input.fileName : input.baseName) - + ".lst" - }); - } - return artifacts; +function dumpArmCCCompilerIncludePaths(compilerFilePath) { + var includePath = compilerFilePath.replace(/bin[\\\/](.*)$/i, "include"); + return [includePath]; } -function applicationLinkerOutputArtifacts(product) { - var app = { - fileTags: ["application"], - filePath: FileInfo.joinPaths( - product.destinationDirectory, - PathTools.applicationFilePath(product)) - }; - var mem_map = { - fileTags: ["mem_map"], - filePath: FileInfo.joinPaths( - product.destinationDirectory, - product.targetName - + (product.cpp.architecture === "mcs51" ? ".m51" : ".map")) - }; - return [app, mem_map]; +function dumpArmClangCompilerIncludePaths(compilerFilePath, nullDevice) { + var args = [ "--target=arm-arm-none-eabi", "-mcpu=cortex-m0", "-v", "-E", + "-x", "c++", nullDevice ]; + var p = new Process(); + p.exec(compilerFilePath, args, false); + var lines = p.readStdErr().trim().split(/\r?\n/g).map(function (line) { return line.trim(); }); + var addIncludes = false; + var includePaths = []; + for (var i = 0; i < lines.length; ++i) { + var line = lines[i]; + if (line === "#include <...> search starts here:") + addIncludes = true; + else if (line === "End of search list.") + addIncludes = false; + else if (addIncludes) + includePaths.push(line); + } + return includePaths; } -function staticLibraryLinkerOutputArtifacts(product) { - var staticLib = { - fileTags: ["staticlibrary"], - filePath: FileInfo.joinPaths( - product.destinationDirectory, - PathTools.staticLibraryFilePath(product)) - }; - return [staticLib] +function dumpDefaultPaths(compilerFilePath, nullDevice) { + var includePaths = []; + if (isMcsCompiler(compilerFilePath)) + includePaths = dumpMcsCompilerIncludePaths(compilerFilePath); + else if (isC166Compiler(compilerFilePath)) + includePaths = dumpC166CompilerIncludePaths(compilerFilePath); + else if (isArmCCCompiler(compilerFilePath)) + includePaths = dumpArmCCCompilerIncludePaths(compilerFilePath); + else if (isArmClangCompiler(compilerFilePath)) + includePaths = dumpArmClangCompilerIncludePaths(compilerFilePath, nullDevice); + return { "includePaths": includePaths }; } +function filterMcsOutput(output) { + var filteredLines = []; + output.split(/\r\n|\r|\n/).forEach(function(line) { + var regexp = /^\s*\*{3}\s|^\s{29}|^\s{4}\S|^\s{2}[0-9A-F]{4}|^\s{21,25}\d+|^[0-9A-F]{4}\s\d+/; + if (regexp.exec(line)) + filteredLines.push(line); + }); + return filteredLines.join('\n'); +}; + +function filterC166Output(output) { + var filteredLines = []; + output.split(/\r\n|\r|\n/).forEach(function(line) { + var regexp = /^\s*\*{3}\s|^\s{29}|^\s{27,28}\d+|^\s{21}\d+|^\s{4}\S|^\s{2}[0-9A-F]{4}|^[0-9A-F]{4}\s\d+/; + if (regexp.exec(line)) + filteredLines.push(line); + }); + return filteredLines.join('\n'); +}; + function compilerFlags(project, product, input, outputs, explicitlyDependsOn) { // Determine which C-language we're compiling. var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(outputs.obj[0].fileTags)); var args = []; - var allDefines = []; - var platformDefines = input.cpp.platformDefines; - if (platformDefines) - allDefines = allDefines.uniqueConcat(platformDefines); - var defines = input.cpp.defines; - if (defines) - allDefines = allDefines.uniqueConcat(defines); - - var allIncludePaths = []; - var includePaths = input.cpp.includePaths; - if (includePaths) - allIncludePaths = allIncludePaths.uniqueConcat(includePaths); - var systemIncludePaths = input.cpp.systemIncludePaths; - if (systemIncludePaths) - allIncludePaths = allIncludePaths.uniqueConcat(systemIncludePaths); - var compilerIncludePaths = input.cpp.compilerIncludePaths; - if (compilerIncludePaths) - allIncludePaths = allIncludePaths.uniqueConcat(compilerIncludePaths); - var architecture = input.qbs.architecture; - if (architecture === "mcs51") { + if (isMcsArchitecture(architecture) || isC166Architecture(architecture)) { // Input. - args.push(FileInfo.toWindowsSeparators(input.filePath)); + args.push(input.filePath); // Output. - args.push("OBJECT (" + FileInfo.toWindowsSeparators(outputs.obj[0].filePath) + ")"); + args.push("OBJECT(" + FileInfo.toWindowsSeparators(outputs.obj[0].filePath) + ")"); // Defines. - if (allDefines.length > 0) - args = args.concat("DEFINE (" + allDefines.join(",") + ")"); + var defines = Cpp.collectDefines(input); + if (defines.length > 0) + args = args.concat("DEFINE (" + defines.join(",") + ")"); // Includes. - if (allIncludePaths.length > 0) { - var adjusted = adjustPathsToWindowsSeparators(allIncludePaths); - args = args.concat("INCDIR (" + adjusted.join(";") + ")"); - } + var allIncludePaths = [].concat(Cpp.collectIncludePaths(input), + Cpp.collectSystemIncludePaths(input)); + if (allIncludePaths.length > 0) + args = args.concat("INCDIR(" + allIncludePaths.map(function(path) { + return FileInfo.toWindowsSeparators(path); }).join(";") + ")"); // Debug information flags. if (input.cpp.debugInformation) @@ -440,153 +514,191 @@ function compilerFlags(project, product, input, outputs, explicitlyDependsOn) { break; case "all": args.push("WARNINGLEVEL (2)"); - args.push("FARWARNING"); + if (isMcs51Architecture(architecture)) + args.push("FARWARNING"); break; } // Listing files generation flag. - if (!product.cpp.generateCompilerListingFiles) + if (!input.cpp.generateCompilerListingFiles) args.push("NOPRINT"); else args.push("PRINT(" + FileInfo.toWindowsSeparators(outputs.lst[0].filePath) + ")"); - } else if (architecture === "arm") { + } else if (isArmArchitecture(architecture)) { // Input. args.push("-c", input.filePath); // Output. args.push("-o", outputs.obj[0].filePath); + // Preinclude headers. + args = args.concat(Cpp.collectPreincludePathsArguments(input, true)); + // Defines. - args = args.concat(allDefines.map(function(define) { return '-D' + define })); + args = args.concat(Cpp.collectDefinesArguments(input)); // Includes. - args = args.concat(allIncludePaths.map(function(include) { return '-I' + include })); + args = args.concat(Cpp.collectIncludePathsArguments(input)); + args = args.concat(Cpp.collectSystemIncludePathsArguments(input)); + + var compilerPath = input.cpp.compilerPath; + if (isArmCCCompiler(compilerPath)) { + // Debug information flags. + if (input.cpp.debugInformation) { + args.push("--debug"); + args.push("-g"); + } - // Debug information flags. - if (input.cpp.debugInformation) { - args.push("--debug"); - args.push("-g"); - } + // Optimization level flags. + switch (input.cpp.optimization) { + case "small": + args.push("-Ospace") + break; + case "fast": + args.push("-Otime") + break; + case "none": + args.push("-O0") + break; + } - // Optimization level flags. - switch (input.cpp.optimization) { - case "small": - args.push("-Ospace") - break; - case "fast": - args.push("-Otime") - break; - case "none": - args.push("-O0") - break; - } + // Warning level flags. + switch (input.cpp.warningLevel) { + case "none": + args.push("-W"); + break; + default: + // By default all warnings are enabled. + break; + } - // Warning level flags. - switch (input.cpp.warningLevel) { - case "none": - args.push("-W"); - break; - default: - // By default all warnings are enabled. - break; - } + if (tag === "c") { + // C language version flags. + var knownCLanguageValues = ["c99", "c90"]; + var cLanguageVersion = Cpp.languageVersion( + input.cpp.cLanguageVersion, knownCLanguageValues, "C"); + switch (cLanguageVersion) { + case "c99": + args.push("--c99"); + break; + case "c90": + args.push("--c90"); + break; + } + } else if (tag === "cpp") { + // C++ language version flags. + var knownCppLanguageValues = ["c++11", "c++03"]; + var cppLanguageVersion = Cpp.languageVersion( + input.cpp.cxxLanguageVersion, knownCppLanguageValues, "C++"); + switch (cppLanguageVersion) { + case "c++11": + args.push("--cpp11"); + break; + default: + // Default C++ language is C++03. + args.push("--cpp"); + break; + } + + // Exceptions flags. + var enableExceptions = input.cpp.enableExceptions; + if (enableExceptions !== undefined) + args.push(enableExceptions ? "--exceptions" : "--no_exceptions"); + + // RTTI flags. + var enableRtti = input.cpp.enableRtti; + if (enableRtti !== undefined) + args.push(enableRtti ? "--rtti" : "--no_rtti"); + } - if (tag === "c") { - // C language version flags. - var knownCLanguageValues = ["c99", "c90"]; - var cLanguageVersion = Cpp.languageVersion( - input.cpp.cLanguageVersion, knownCLanguageValues, "C"); - switch (cLanguageVersion) { - case "c99": - args.push("--c99"); + // Listing files generation flag. + if (input.cpp.generateCompilerListingFiles) { + args.push("--list"); + args.push("--list_dir", FileInfo.path(outputs.lst[0].filePath)); + } + } else if (isArmClangCompiler(compilerPath)) { + // Debug information flags. + if (input.cpp.debugInformation) + args.push("-g"); + + // Optimization level flags. + switch (input.cpp.optimization) { + case "small": + args.push("-Oz") + break; + case "fast": + args.push("-Ofast") break; - case "c90": - args.push("--c90"); + case "none": + args.push("-O0") break; } - } else if (tag === "cpp") { - // C++ language version flags. - var knownCppLanguageValues = ["c++11", "c++03"]; - var cppLanguageVersion = Cpp.languageVersion( - input.cpp.cxxLanguageVersion, knownCppLanguageValues, "C++"); - switch (cppLanguageVersion) { - case "c++11": - args.push("--cpp11"); + + // Warning level flags. + switch (input.cpp.warningLevel) { + case "all": + args.push("-Wall"); break; default: - // Default C++ language is C++03. - args.push("--cpp"); break; } - // Exceptions flags. - var enableExceptions = input.cpp.enableExceptions; - if (enableExceptions !== undefined) - args.push(enableExceptions ? "--exceptions" : "--no_exceptions"); - - // RTTI flags. - var enableRtti = input.cpp.enableRtti; - if (enableRtti !== undefined) - args.push(enableRtti ? "--rtti" : "--no_rtti"); - } + if (input.cpp.treatWarningsAsErrors) + args.push("-Werror"); + + if (tag === "c") { + // C language version flags. + var knownCLanguageValues = ["c11", "c99", "c90", "c89"]; + var cLanguageVersion = Cpp.languageVersion( + input.cpp.cLanguageVersion, knownCLanguageValues, "C"); + if (cLanguageVersion) + args.push("-std=" + cLanguageVersion); + } else if (tag === "cpp") { + // C++ language version flags. + var knownCppLanguageValues = ["c++17", "c++14", "c++11", "c++03", "c++98"]; + var cppLanguageVersion = Cpp.languageVersion( + input.cpp.cxxLanguageVersion, knownCppLanguageValues, "C++"); + if (cppLanguageVersion) + args.push("-std=" + cppLanguageVersion); + + // Exceptions flags. + var enableExceptions = input.cpp.enableExceptions; + if (enableExceptions !== undefined) + args.push(enableExceptions ? "-fexceptions" : "-fno-exceptions"); + + // RTTI flags. + var enableRtti = input.cpp.enableRtti; + if (enableRtti !== undefined) + args.push(enableRtti ? "-frtti" : "-fno-rtti"); + } - // Listing files generation flag. - if (product.cpp.generateCompilerListingFiles) { - args.push("--list"); - args.push("--list_dir", FileInfo.path(outputs.lst[0].filePath)); + // Listing files generation does not supported! + // There are only a workaround: http://www.keil.com/support/docs/4152.htm } } // Misc flags. - args = args.concat(ModUtils.moduleProperty(input, "platformFlags"), - ModUtils.moduleProperty(input, "flags"), - ModUtils.moduleProperty(input, "platformFlags", tag), - ModUtils.moduleProperty(input, "flags", tag), - ModUtils.moduleProperty(input, "driverFlags", tag)); + args = args.concat(Cpp.collectMiscCompilerArguments(input, tag), + Cpp.collectMiscDriverArguments(input)); return args; } function assemblerFlags(project, product, input, outputs, explicitlyDependsOn) { - // Determine which C-language we're compiling - var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(outputs.obj[0].fileTags)); var args = []; - - var allDefines = []; - var platformDefines = input.cpp.platformDefines; - if (platformDefines) - allDefines = allDefines.uniqueConcat(platformDefines); - var defines = input.cpp.defines; - if (defines) - allDefines = allDefines.uniqueConcat(defines); - - var allIncludePaths = []; - var includePaths = input.cpp.includePaths; - if (includePaths) - allIncludePaths = allIncludePaths.uniqueConcat(includePaths); - var systemIncludePaths = input.cpp.systemIncludePaths; - if (systemIncludePaths) - allIncludePaths = allIncludePaths.uniqueConcat(systemIncludePaths); - var compilerIncludePaths = input.cpp.compilerIncludePaths; - if (compilerIncludePaths) - allIncludePaths = allIncludePaths.uniqueConcat(compilerIncludePaths); - var architecture = input.qbs.architecture; - if (architecture === "mcs51") { + if (isMcsArchitecture(architecture) || isC166Architecture(architecture)) { // Input. - args.push(FileInfo.toWindowsSeparators(input.filePath)); + args.push(input.filePath); // Output. - args.push("OBJECT (" + FileInfo.toWindowsSeparators(outputs.obj[0].filePath) + ")"); - - // Defines. - if (allDefines.length > 0) - args = args.concat("DEFINE (" + allDefines.join(",") + ")"); + args.push("OBJECT(" + FileInfo.toWindowsSeparators(outputs.obj[0].filePath) + ")"); // Includes. - if (allIncludePaths.length > 0) { - var adjusted = adjustPathsToWindowsSeparators(allIncludePaths); - args = args.concat("INCDIR (" + adjusted.join(";") + ")"); - } + var allIncludePaths = [].concat(Cpp.collectIncludePaths(input), + Cpp.collectSystemIncludePaths(input)); + if (allIncludePaths.length > 0) + args = args.concat("INCDIR(" + allIncludePaths.map(function(path) { + return FileInfo.toWindowsSeparators(path); }).join(";") + ")"); // Debug information flags. if (input.cpp.debugInformation) @@ -596,31 +708,20 @@ function assemblerFlags(project, product, input, outputs, explicitlyDependsOn) { args.push("EP"); // Listing files generation flag. - if (!product.cpp.generateAssemblerListingFiles) + if (!input.cpp.generateAssemblerListingFiles) args.push("NOPRINT"); else args.push("PRINT(" + FileInfo.toWindowsSeparators(outputs.lst[0].filePath) + ")"); - } else if (architecture === "arm") { + } else if (isArmArchitecture(architecture)) { // Input. args.push(input.filePath); // Output. args.push("-o", outputs.obj[0].filePath); - // Defines. - 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]); - }); - // Includes. - args = args.concat(allIncludePaths.map(function(include) { return '-I' + include })); + args = args.concat(Cpp.collectIncludePathsArguments(input)); + args = args.concat(Cpp.collectSystemIncludePathsArguments(input)); // Debug information flags. if (input.cpp.debugInformation) { @@ -638,72 +739,86 @@ function assemblerFlags(project, product, input, outputs, explicitlyDependsOn) { args.push((endianness === "little") ? "--littleend" : "--bigend"); // Listing files generation flag. - if (product.cpp.generateAssemblerListingFiles) + if (input.cpp.generateAssemblerListingFiles) args.push("--list", outputs.lst[0].filePath); } // Misc flags. - args = args.concat(ModUtils.moduleProperty(input, "platformFlags", tag), - ModUtils.moduleProperty(input, "flags", tag), - ModUtils.moduleProperty(input, "driverFlags", tag)); + args = args.concat(Cpp.collectMiscAssemblerArguments(input, "asm")); + return args; +} + +function disassemblerFlags(project, product, input, outputs, explicitlyDependsOn) { + var args = ["--disassemble", "--interleave=source"]; + args.push(outputs.obj[0].filePath); + args.push("--output=" + outputs.lst[0].filePath); return args; } -function linkerFlags(project, product, input, outputs) { +function linkerFlags(project, product, inputs, outputs) { var args = []; + var libraryPaths = Cpp.collectLibraryPaths(product); + var architecture = product.qbs.architecture; - if (architecture === "mcs51") { - // Note: The C51 linker does not distinguish an object files and + if (isMcsArchitecture(architecture) || isC166Architecture(architecture)) { + // Semi-intelligent handling the library paths. + // We need to add the full path prefix to the library file if this + // file is not absolute or not relative. Reason is that the C51, C251, + // and C166 linkers does not support the library paths. + function collectLibraryObjectPaths(product) { + var libraryObjects = Cpp.collectLibraryDependencies(product); + return libraryObjects.map(function(dep) { + var filePath = dep.filePath; + if (FileInfo.isAbsolutePath(filePath)) + return filePath; + for (var i = 0; i < libraryPaths.length; ++i) { + var fullPath = FileInfo.joinPaths(libraryPaths[i], filePath); + if (File.exists(fullPath)) + return fullPath; + } + return filePath; + }); + } + + // Note: The C51, C251, or C166 linker does not distinguish an object files and // a libraries, it interpret all this stuff as an input objects, // so, we need to pass it together in one string. - - var allObjectPaths = []; - function addObjectPath(obj) { - allObjectPaths.push(obj.filePath); + function collectAllObjectPathsArguments(product, inputs) { + return [].concat(Cpp.collectLinkerObjectPaths(inputs), + collectLibraryObjectPaths(product)); } - // Inputs. - if (inputs.obj) - inputs.obj.map(function(obj) { addObjectPath(obj) }); - - // Library dependencies. - var libraryObjects = collectLibraryDependencies(product); - libraryObjects.forEach(function(dep) { addObjectPath(dep); }) - // Add all input objects as arguments (application and library object files). - var adjusted = adjustPathsToWindowsSeparators(allObjectPaths); - args = args.concat(adjusted.join(",")); + var allObjectPaths = collectAllObjectPathsArguments(product, inputs); + if (allObjectPaths.length > 0) + args = args.concat(allObjectPaths.map(function(path) { + return FileInfo.toWindowsSeparators(path); }).join(",")); // Output. - // Note: 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.push("TO", FileInfo.toWindowsSeparators(outputs.application[0].filePath)); // Map file generation flag. if (!product.cpp.generateLinkerMapFile) - args.push("NOMAP"); - } else if (architecture === "arm") { + args.push("NOPRINT"); + else + args.push("PRINT(" + FileInfo.toWindowsSeparators(outputs.mem_map[0].filePath) + ")"); + } else if (isArmArchitecture(architecture)) { // Inputs. - if (inputs.obj) - args = args.concat(inputs.obj.map(function(obj) { return obj.filePath })); + args = args.concat(Cpp.collectLinkerObjectPaths(inputs)); // Output. args.push("--output", outputs.application[0].filePath); // Library paths. - var libraryPaths = product.cpp.libraryPaths; - if (libraryPaths) - args.push("--userlibpath=" + libraryPaths.join(",")); + if (libraryPaths.length > 0) + args.push(product.cpp.libraryPathFlag + libraryPaths.join(",")); // Library dependencies. - var libraryDependencies = collectLibraryDependencies(product); - args = args.concat(libraryDependencies.map(function(dep) { return dep.filePath; })); + args = args.concat(Cpp.collectLibraryDependenciesArguments(product)); - // Debug information flag. - var debugInformation = product.cpp.debugInformation; - if (debugInformation !== undefined) - args.push(debugInformation ? "--debug" : "--no_debug"); + // Linker scripts. + args = args.concat(Cpp.collectLinkerScriptPathsArguments(product, inputs, true)); // Map file generation flag. if (product.cpp.generateLinkerMapFile) @@ -713,43 +828,38 @@ function linkerFlags(project, product, input, outputs) { if (product.cpp.entryPoint) args.push("--entry", product.cpp.entryPoint); - // Linker scripts flags. - var linkerScripts = inputs.linkerscript - ? inputs.linkerscript.map(function(a) { return a.filePath; }) : []; - linkerScripts.forEach(function(script) { args.push("--scatter", script); }); + // Debug information flag. + var debugInformation = product.cpp.debugInformation; + if (debugInformation !== undefined) + args.push(debugInformation ? "--debug" : "--no_debug"); } // Misc flags. - args = args.concat(ModUtils.moduleProperty(product, "driverLinkerFlags")); + args = args.concat(Cpp.collectMiscEscapableLinkerArguments(product), + Cpp.collectMiscLinkerArguments(product), + Cpp.collectMiscDriverArguments(product)); return args; } -function archiverFlags(project, product, input, outputs) { +function archiverFlags(project, product, inputs, outputs) { var args = []; + // Inputs. + var objectPaths = Cpp.collectLinkerObjectPaths(inputs); + var architecture = product.qbs.architecture; - if (architecture === "mcs51") { + if (isMcsArchitecture(architecture) || isC166Architecture(architecture)) { // Library creation command. args.push("TRANSFER"); - var allObjectPaths = []; - function addObjectPath(obj) { - allObjectPaths.push(obj.filePath); - } - // Inputs. - if (inputs.obj) - inputs.obj.map(function(obj) { addObjectPath(obj) }); - - // Add all input objects as arguments. - var adjusted = adjustPathsToWindowsSeparators(allObjectPaths); - args = args.concat(adjusted.join(",")); + if (objectPaths.length > 0) + args = args.concat(objectPaths.map(function(path) { + return FileInfo.toWindowsSeparators(path); }).join(",")); // Output. - // Note: 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("TO", FileInfo.toWindowsSeparators(outputs.staticlibrary[0].filePath)); + } else if (isArmArchitecture(architecture)) { // Note: The ARM archiver command line expect the output file // first, and then a set of input objects. @@ -757,8 +867,7 @@ function archiverFlags(project, product, input, outputs) { args.push("--create", outputs.staticlibrary[0].filePath); // Inputs. - if (inputs.obj) - args = args.concat(inputs.obj.map(function(obj) { return obj.filePath })); + args = args.concat(objectPaths); // Debug information flag. if (product.cpp.debugInformation) @@ -768,48 +877,116 @@ function archiverFlags(project, product, input, outputs) { return args; } +// The ARMCLANG compiler does not support generation +// for the listing files: +// * https://www.keil.com/support/docs/4152.htm +// So, we generate the listing files from the object files +// using the disassembler. +function generateClangCompilerListing(project, product, inputs, outputs, input, output) { + if (isArmClangCompiler(input.cpp.compilerPath) && input.cpp.generateCompilerListingFiles) { + var args = disassemblerFlags(project, product, input, outputs, explicitlyDependsOn); + var disassemblerPath = input.cpp.disassemblerPath; + var cmd = new Command(disassemblerPath, args); + cmd.silent = true; + return cmd; + } +} + +// The ARMCC compiler generates the listing files only in a short form, +// e.g. to 'module.lst' instead of 'module.{c|cpp}.lst', that complicates +// the auto-tests. Therefore we need to rename generated listing files +// with correct unified names. +function generateArmccCompilerListing(project, product, inputs, outputs, input, output) { + if (isArmCCCompiler(input.cpp.compilerPath) && input.cpp.generateCompilerListingFiles) { + var listingPath = FileInfo.path(outputs.lst[0].filePath); + var cmd = new JavaScriptCommand(); + cmd.oldListing = FileInfo.joinPaths(listingPath, input.baseName + ".lst"); + cmd.newListing = FileInfo.joinPaths( + listingPath, input.fileName + input.cpp.compilerListingSuffix); + cmd.silent = true; + cmd.sourceCode = function() { File.move(oldListing, newListing); }; + return cmd; + } +} + function prepareCompiler(project, product, inputs, outputs, input, output, explicitlyDependsOn) { + var cmds = []; var args = compilerFlags(project, product, input, outputs, 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 = getMaxExitCode(architecture); - filterStdOutput(cmd); - return [cmd]; -} + cmd.jobPool = "compiler"; + if (isMcsArchitecture(architecture)) { + cmd.maxExitCode = 1; + cmd.stdoutFilterFunction = filterMcsOutput; + } else if (isC166Architecture(architecture)) { + cmd.maxExitCode = 1; + cmd.stdoutFilterFunction = filterC166Output; + } + cmds.push(cmd); + + cmd = generateClangCompilerListing(project, product, inputs, outputs, input, output); + if (cmd) + cmds.push(cmd); + cmd = generateArmccCompilerListing(project, product, inputs, outputs, input, output); + if (cmd) + cmds.push(cmd); + + return cmds; +} function prepareAssembler(project, product, inputs, outputs, input, output, explicitlyDependsOn) { var args = assemblerFlags(project, product, input, outputs, explicitlyDependsOn); var assemblerPath = input.cpp.assemblerPath; + var architecture = input.cpp.architecture; var cmd = new Command(assemblerPath, args); cmd.description = "assembling " + input.fileName; cmd.highlight = "compiler"; - filterStdOutput(cmd); + cmd.jobPool = "assembler"; + if (isMcsArchitecture(architecture)) { + cmd.maxExitCode = 1; + cmd.stdoutFilterFunction = filterMcsOutput; + } else if (isC166Architecture(architecture)) { + cmd.maxExitCode = 1; + cmd.stdoutFilterFunction = filterC166Output; + } return [cmd]; } function prepareLinker(project, product, inputs, outputs, input, output) { var primaryOutput = outputs.application[0]; - var args = linkerFlags(project, product, input, outputs); + var args = linkerFlags(project, product, inputs, outputs); var linkerPath = product.cpp.linkerPath; var architecture = product.cpp.architecture; var cmd = new Command(linkerPath, args); cmd.description = "linking " + primaryOutput.fileName; cmd.highlight = "linker"; - cmd.maxExitCode = getMaxExitCode(architecture); - filterStdOutput(cmd); + cmd.jobPool = "linker"; + if (isMcsArchitecture(architecture)) { + cmd.maxExitCode = 1; + cmd.stdoutFilterFunction = filterMcsOutput; + } else if (isC166Architecture(architecture)) { + cmd.maxExitCode = 1; + cmd.stdoutFilterFunction = filterC166Output; + } return [cmd]; } function prepareArchiver(project, product, inputs, outputs, input, output) { - var args = archiverFlags(project, product, input, outputs); + var args = archiverFlags(project, product, inputs, outputs); var archiverPath = product.cpp.archiverPath; + var architecture = product.cpp.architecture; var cmd = new Command(archiverPath, args); - cmd.description = "linking " + output.fileName; + cmd.description = "creating " + output.fileName; cmd.highlight = "linker"; - filterStdOutput(cmd); + cmd.jobPool = "linker"; + if (isMcsArchitecture(architecture)) { + cmd.stdoutFilterFunction = filterMcsOutput; + } else if (isC166Architecture(architecture)) { + cmd.stdoutFilterFunction = filterC166Output; + } return [cmd]; } diff --git a/share/qbs/modules/cpp/keil.qbs b/share/qbs/modules/cpp/keil.qbs index 67ea5e675..5922ad4e2 100644 --- a/share/qbs/modules/cpp/keil.qbs +++ b/share/qbs/modules/cpp/keil.qbs @@ -28,15 +28,15 @@ ** ****************************************************************************/ -import qbs 1.0 import qbs.File import qbs.FileInfo -import qbs.ModUtils +import qbs.Host import qbs.Probes +import "cpp.js" as Cpp import "keil.js" as KEIL CppModule { - condition: qbs.hostOS.contains("windows") && qbs.toolchain && qbs.toolchain.contains("keil") + condition: Host.os().includes("windows") && qbs.toolchain && qbs.toolchain.includes("keil") Probes.BinaryProbe { id: compilerPathProbe @@ -52,6 +52,7 @@ CppModule { } qbs.architecture: keilProbe.found ? keilProbe.architecture : original + qbs.targetPlatform: "none" compilerVersionMajor: keilProbe.versionMajor compilerVersionMinor: keilProbe.versionMinor @@ -61,45 +62,56 @@ CppModule { compilerDefinesByLanguage: keilProbe.compilerDefinesByLanguage compilerIncludePaths: keilProbe.includePaths - property string toolchainInstallPath: compilerPathProbe.found - ? compilerPathProbe.path : undefined - - property string compilerExtension: qbs.hostOS.contains("windows") ? ".exe" : "" + toolchainInstallPath: compilerPathProbe.found ? compilerPathProbe.path : undefined /* Work-around for QtCreator which expects these properties to exist. */ property string cCompilerName: compilerName property string cxxCompilerName: compilerName - compilerName: KEIL.compilerName(qbs) + compilerExtension + compilerName: toolchainDetails.compilerName + compilerExtension compilerPath: FileInfo.joinPaths(toolchainInstallPath, compilerName) - assemblerName: KEIL.assemblerName(qbs) + compilerExtension + assemblerName: toolchainDetails.assemblerName + compilerExtension assemblerPath: FileInfo.joinPaths(toolchainInstallPath, assemblerName) - linkerName: KEIL.linkerName(qbs) + compilerExtension + linkerName: toolchainDetails.linkerName + compilerExtension linkerPath: FileInfo.joinPaths(toolchainInstallPath, linkerName) - property string archiverName: KEIL.archiverName(qbs) + compilerExtension + property string archiverName: toolchainDetails.archiverName + compilerExtension property string archiverPath: FileInfo.joinPaths(toolchainInstallPath, archiverName) - runtimeLibrary: "static" + property string disassemblerName: toolchainDetails.disassemblerName + compilerExtension + property string disassemblerPath: FileInfo.joinPaths(toolchainInstallPath, disassemblerName) - staticLibrarySuffix: KEIL.staticLibrarySuffix(qbs) - executableSuffix: KEIL.executableSuffix(qbs) + runtimeLibrary: "static" - property string objectSuffix: KEIL.objectSuffix(qbs) + staticLibrarySuffix: ".lib" + executableSuffix: toolchainDetails.executableSuffix + objectSuffix: toolchainDetails.objectSuffix + linkerMapSuffix: toolchainDetails.linkerMapSuffix - imageFormat: KEIL.imageFormat(qbs) + imageFormat: toolchainDetails.imageFormat enableExceptions: false enableRtti: false + defineFlag: "-D" + includeFlag: "-I" + systemIncludeFlag: "-I" + preincludeFlag: KEIL.preincludeFlag(compilerPath) + libraryDependencyFlag: "" + libraryPathFlag: "--userlibpath=" + linkerScriptFlag: "--scatter" + + toolchainDetails: KEIL.toolchainDetails(qbs) + + knownArchitectures: ["arm", "c166", "mcs251", "mcs51"] + Rule { id: assembler inputs: ["asm"] - outputFileTags: ["obj", "lst"] - outputArtifacts: KEIL.compilerOutputArtifacts( - input, input.cpp.generateAssemblerListingFiles) + outputFileTags: Cpp.assemblerOutputTags(generateAssemblerListingFiles) + outputArtifacts: Cpp.assemblerOutputArtifacts(input) prepare: KEIL.prepareAssembler.apply(KEIL, arguments) } @@ -112,9 +124,8 @@ CppModule { id: compiler inputs: ["cpp", "c"] auxiliaryInputs: ["hpp"] - outputFileTags: ["obj", "lst"] - outputArtifacts: KEIL.compilerOutputArtifacts( - input, input.cpp.generateCompilerListingFiles) + outputFileTags: Cpp.compilerOutputTags(generateCompilerListingFiles) + outputArtifacts: Cpp.compilerOutputArtifacts(input) prepare: KEIL.prepareCompiler.apply(KEIL, arguments) } @@ -122,8 +133,9 @@ CppModule { id: applicationLinker multiplex: true inputs: ["obj", "linkerscript"] - outputFileTags: ["application", "mem_map"] - outputArtifacts: KEIL.applicationLinkerOutputArtifacts(product) + inputsFromDependencies: ["staticlibrary"] + outputFileTags: Cpp.applicationLinkerOutputTags(generateLinkerMapFile) + outputArtifacts: Cpp.applicationLinkerOutputArtifacts(product) prepare: KEIL.prepareLinker.apply(KEIL, arguments) } @@ -132,8 +144,8 @@ CppModule { multiplex: true inputs: ["obj"] inputsFromDependencies: ["staticlibrary"] - outputFileTags: ["staticlibrary"] - outputArtifacts: KEIL.staticLibraryLinkerOutputArtifacts(product) + outputFileTags: Cpp.staticLibraryLinkerOutputTags() + outputArtifacts: Cpp.staticLibraryLinkerOutputArtifacts(product) prepare: KEIL.prepareArchiver.apply(KEIL, arguments) } } diff --git a/share/qbs/modules/cpp/macos-gcc.qbs b/share/qbs/modules/cpp/macos-gcc.qbs index 612b46ae8..c667bbc29 100644 --- a/share/qbs/modules/cpp/macos-gcc.qbs +++ b/share/qbs/modules/cpp/macos-gcc.qbs @@ -33,8 +33,8 @@ import qbs.Utilities DarwinGCC { priority: 1 - condition: qbs.targetOS.contains('macos') && - qbs.toolchain && qbs.toolchain.contains('gcc') + condition: qbs.targetOS.includes('macos') && + qbs.toolchain && qbs.toolchain.includes('gcc') targetSystem: "macosx" + (minimumMacosVersion || "") diff --git a/share/qbs/modules/cpp/msvc.js b/share/qbs/modules/cpp/msvc.js index b67ab811f..1700c21b3 100644 --- a/share/qbs/modules/cpp/msvc.js +++ b/share/qbs/modules/cpp/msvc.js @@ -28,6 +28,7 @@ ** ****************************************************************************/ +var Codesign = require("../codesign/codesign.js"); var Cpp = require("cpp.js"); var File = require("qbs.File"); var FileInfo = require("qbs.FileInfo"); @@ -35,6 +36,22 @@ var ModUtils = require("qbs.ModUtils"); var Utilities = require("qbs.Utilities"); var WindowsUtils = require("qbs.WindowsUtils"); +function effectiveLinkerPath(product, inputs) { + if (product.cpp.linkerMode === "automatic") { + var compiler = product.cpp.compilerPath; + if (compiler) { + if (inputs.obj || inputs.staticlibrary) { + console.log("Found C/C++ objects, using compiler as a linker for " + product.name); + return compiler; + } + } + + console.log("Found no C-language objects, choosing system linker for " + product.name); + } + + return product.cpp.linkerPath; +} + function handleCpuFeatures(input, flags) { if (!input.qbs.architecture) return; @@ -62,20 +79,54 @@ function hasCxx17Option(input) { // Probably this is not the earliest version to support the flag, but we have tested this one // and it's a pain to find out the exact minimum. - return Utilities.versionCompare(input.cpp.compilerVersion, "19.12.25831") >= 0 - || (input.qbs.toolchain.contains("clang-cl") && input.cpp.compilerVersionMajor >= 7); + return (input.qbs.toolchain.includes("clang-cl") && input.cpp.compilerVersionMajor >= 7) + || Utilities.versionCompare(input.cpp.compilerVersion, "19.12.25831") >= 0; +} + +function hasZCplusPlusOption(input) +{ + // /Zc:__cplusplus is supported starting from Visual Studio 15.7 + // Looks like closest MSVC version is 14.14.26428 (cl ver 19.14.26433) + // At least, this version is tested + // https://docs.microsoft.com/en-us/cpp/build/reference/zc-cplusplus + // clang-cl supports this option starting around-ish versions 8/9, but it + // ignores this option, so this doesn't really matter + // https://reviews.llvm.org/D45877 + return (input.qbs.toolchain.includes("clang-cl") && input.cpp.compilerVersionMajor >= 9) + || Utilities.versionCompare(input.cpp.compilerVersion, "19.14.26433") >= 0; } -function addLanguageVersionFlag(input, args) { +function hasCxx20Option(input) +{ + return (input.qbs.toolchain.includes("clang-cl") && input.cpp.compilerVersionMajor >= 13) + || Utilities.versionCompare(input.cpp.compilerVersion, "19.29.30133.0") >= 0; +} + +function hasCVerOption(input) +{ + return (input.qbs.toolchain.includes("clang-cl") && input.cpp.compilerVersionMajor >= 13) + || Utilities.versionCompare(input.cpp.compilerVersion, "19.29.30138.0") >= 0; +} + +function supportsExternalIncludesOption(input) { + if (input.qbs.toolchain.includes("clang-cl")) + return false; // Exclude clang-cl. + // This option was introcuded since MSVC 2017 v15.6 (aka _MSC_VER 19.13). + // But due to some MSVC bugs: + // * https://developercommunity.visualstudio.com/content/problem/181006/externali-include-paths-not-working.html + // this option has been fixed since MSVC 2017 update 9, v15.9 (aka _MSC_VER 19.16). + return Utilities.versionCompare(input.cpp.compilerVersion, "19.16") >= 0; +} + +function addCxxLanguageVersionFlag(input, args) { var cxxVersion = Cpp.languageVersion(input.cpp.cxxLanguageVersion, - ["c++17", "c++14", "c++11", "c++98"], "C++"); + ["c++23", "c++20", "c++17", "c++14", "c++11", "c++98"], "C++"); if (!cxxVersion) return; - // Visual C++ 2013, Update 3 - var hasStdOption = Utilities.versionCompare(input.cpp.compilerVersion, "18.00.30723") >= 0 - // or clang-cl - || input.qbs.toolchain.contains("clang-cl"); + // Visual C++ 2013, Update 3 or clang-cl + var hasStdOption = input.qbs.toolchain.includes("clang-cl") + || Utilities.versionCompare(input.cpp.compilerVersion, "18.00.30723") >= 0; if (!hasStdOption) return; @@ -84,12 +135,42 @@ function addLanguageVersionFlag(input, args) { flag = "/std:c++14"; else if (cxxVersion === "c++17" && hasCxx17Option(input)) flag = "/std:c++17"; + else if (cxxVersion === "c++20" && hasCxx20Option(input)) + flag = "/std:c++20"; else if (cxxVersion !== "c++11" && cxxVersion !== "c++98") flag = "/std:c++latest"; if (flag) args.push(flag); } +function addCLanguageVersionFlag(input, args) { + var cVersion = Cpp.languageVersion(input.cpp.cLanguageVersion, + ["c17", "c11"], "C"); + if (!cVersion) + return; + + var hasStdOption = hasCVerOption(input); + if (!hasStdOption) + return; + + var flag; + if (cVersion === "c17") + flag = "/std:c17"; + else if (cVersion === "c11") + flag = "/std:c11"; + if (flag) + args.push(flag); +} + +function handleClangClArchitectureFlags(product, architecture, flags) { + if (product.qbs.toolchain.includes("clang-cl")) { + if (architecture === "x86") + flags.push("-m32"); + else if (architecture === "x86_64") + flags.push("-m64"); + } +} + function prepareCompiler(project, product, inputs, outputs, input, output, explicitlyDependsOn) { var i; var debugInformation = input.cpp.debugInformation; @@ -99,7 +180,7 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli // Determine which C-language we're compiling var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(Object.keys(outputs))); - if (!["c", "cpp"].contains(tag)) + if (!["c", "cpp"].includes(tag)) throw ("unsupported source language"); var enableExceptions = input.cpp.enableExceptions; @@ -135,6 +216,8 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli break; } + handleClangClArchitectureFlags(product, input.cpp.architecture, args); + if (debugInformation) { if (product.cpp.separateDebugInformation) args.push('/Zi'); @@ -150,6 +233,8 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli args.push(rtl); } + args = args.concat(Cpp.collectMiscDriverArguments(product)); + // warnings: var warningLevel = input.cpp.warningLevel; if (warningLevel === 'none') @@ -158,24 +243,33 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli args.push('/Wall') if (input.cpp.treatWarningsAsErrors) args.push('/WX') - var allIncludePaths = []; - var includePaths = input.cpp.includePaths; - if (includePaths) - allIncludePaths = allIncludePaths.uniqueConcat(includePaths); - var systemIncludePaths = input.cpp.systemIncludePaths; - if (systemIncludePaths) - allIncludePaths = allIncludePaths.uniqueConcat(systemIncludePaths); - for (i in allIncludePaths) - args.push('/I' + FileInfo.toWindowsSeparators(allIncludePaths[i])) - var allDefines = []; - var platformDefines = input.cpp.platformDefines; - if (platformDefines) - allDefines = allDefines.uniqueConcat(platformDefines); - var defines = input.cpp.defines; - if (defines) - allDefines = allDefines.uniqueConcat(defines); - for (i in allDefines) - args.push('/D' + allDefines[i].replace(/%/g, "%%")); + + var includePaths = Cpp.collectIncludePaths(input); + args = args.concat([].uniqueConcat(includePaths).map(function(path) { + return input.cpp.includeFlag + FileInfo.toWindowsSeparators(path); + })); + + var includeFlag = input.qbs.toolchain.includes("clang-cl") + ? input.cpp.systemIncludeFlag : input.cpp.includeFlag; + if (!input.qbs.toolchain.includes("clang-cl")) { + if (supportsExternalIncludesOption(input)) { + args.push("/experimental:external"); + var enforcesSlashW = + Utilities.versionCompare(input.cpp.compilerVersion, "19.29.30037") >= 0 + if (enforcesSlashW) + args.push("/external:W0") + includeFlag = input.cpp.systemIncludeFlag; + } + } + var systemIncludePaths = Cpp.collectSystemIncludePaths(input); + args = args.concat([].uniqueConcat(systemIncludePaths).map(function(path) { + return includeFlag + FileInfo.toWindowsSeparators(path); + })); + + var defines = Cpp.collectDefines(input); + args = args.concat([].uniqueConcat(defines).map(function(define) { + return input.cpp.defineFlag + define.replace(/%/g, "%%"); + })); var minimumWindowsVersion = product.cpp.minimumWindowsVersion; if (minimumWindowsVersion) { @@ -183,7 +277,7 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli if (hexVersion) { var versionDefs = [ 'WINVER', '_WIN32_WINNT', '_WIN32_WINDOWS' ]; for (i in versionDefs) { - args.push('/D' + versionDefs[i] + '=' + hexVersion); + args.push(input.cpp.defineFlag + versionDefs[i] + '=' + hexVersion); } } } @@ -191,21 +285,29 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli if (product.cpp.debugInformation && product.cpp.separateDebugInformation) args.push("/Fd" + product.targetName + ".cl" + product.cpp.debugInfoSuffix); + if (input.cpp.generateCompilerListingFiles) + args.push("/Fa" + FileInfo.toWindowsSeparators(outputs.lst[0].filePath)); + + if (input.cpp.enableCxxLanguageMacro && hasZCplusPlusOption(input)) + args.push("/Zc:__cplusplus"); + var objectMap = outputs.obj || outputs.intermediate_obj var objOutput = objectMap ? objectMap[0] : undefined args.push('/Fo' + FileInfo.toWindowsSeparators(objOutput.filePath)) args.push(FileInfo.toWindowsSeparators(input.filePath)) - var prefixHeaders = product.cpp.prefixHeaders; - for (i in prefixHeaders) - args.push("/FI" + FileInfo.toWindowsSeparators(prefixHeaders[i])); + var preincludePaths = Cpp.collectPreincludePaths(input); + args = args.concat([].uniqueConcat(preincludePaths).map(function(path) { + return input.cpp.preincludeFlag + FileInfo.toWindowsSeparators(path); + })); // Language if (tag === "cpp") { args.push("/TP"); - addLanguageVersionFlag(input, args); + addCxxLanguageVersionFlag(input, args); } else if (tag === "c") { args.push("/TC"); + addCLanguageVersionFlag(input, args); } // Whether we're compiling a precompiled header or normal source file @@ -213,7 +315,7 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli var pchInputs = explicitlyDependsOn[tag + "_pch"]; if (pchOutput) { // create PCH - if (input.qbs.toolchain.contains("clang-cl")) { + if (input.qbs.toolchain.includes("clang-cl")) { // clang-cl does not support /Yc flag without filename args.push("/Yc" + FileInfo.toWindowsSeparators(input.filePath)); // clang-cl complains when pch file is not included @@ -243,10 +345,7 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli } } - args = args.concat(ModUtils.moduleProperty(input, 'platformFlags'), - ModUtils.moduleProperty(input, 'flags'), - ModUtils.moduleProperty(input, 'platformFlags', tag), - ModUtils.moduleProperty(input, 'flags', tag)); + args = args.concat(Cpp.collectMiscCompilerArguments(input, tag)); var compilerPath = product.cpp.compilerPath; var wrapperArgs = product.cpp.compilerWrapper; @@ -272,74 +371,10 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli return [cmd]; } -function collectLibraryDependencies(product) { - var seen = {}; - var result = []; - - function addFilePath(filePath, wholeArchive, productName) { - result.push({ filePath: filePath, wholeArchive: wholeArchive, productName: productName }); - } - - function addArtifactFilePaths(dep, artifacts) { - if (!artifacts) - return; - var artifactFilePaths = artifacts.map(function(a) { return a.filePath; }); - var wholeArchive = dep.parameters.cpp && dep.parameters.cpp.linkWholeArchive; - var artifactsAreImportLibs = artifacts.length > 0 - && artifacts[0].fileTags.contains("dynamiclibrary_import"); - for (var i = 0; i < artifactFilePaths.length; ++i) { - addFilePath(artifactFilePaths[i], wholeArchive, - artifactsAreImportLibs ? dep.name : undefined); - } - } - - function addExternalLibs(obj) { - if (!obj.cpp) - return; - function ensureArray(a) { - return Array.isArray(a) ? a : []; - } - function sanitizedModuleListProperty(obj, moduleName, propertyName) { - return ensureArray(ModUtils.sanitizedModuleProperty(obj, moduleName, propertyName)); - } - var externalLibs = [].concat( - sanitizedModuleListProperty(obj, "cpp", "staticLibraries"), - sanitizedModuleListProperty(obj, "cpp", "dynamicLibraries")); - externalLibs.forEach(function (libName) { - if (!libName.match(/\.lib$/i) && !libName.startsWith('@')) - libName += ".lib"; - addFilePath(libName, false); - }); - } - - function traverse(dep) { - if (seen.hasOwnProperty(dep.name)) - return; - seen[dep.name] = true; - - if (dep.parameters.cpp && dep.parameters.cpp.link === false) - return; - - var staticLibraryArtifacts = dep.artifacts["staticlibrary"]; - var dynamicLibraryArtifacts = staticLibraryArtifacts - ? null : dep.artifacts["dynamiclibrary_import"]; - if (staticLibraryArtifacts) { - dep.dependencies.forEach(traverse); - addArtifactFilePaths(dep, staticLibraryArtifacts); - addExternalLibs(dep); - } else if (dynamicLibraryArtifacts) { - addArtifactFilePaths(dep, dynamicLibraryArtifacts); - } - } - - product.dependencies.forEach(traverse); - addExternalLibs(product); - return result; -} - function linkerSupportsWholeArchive(product) { - return Utilities.versionCompare(product.cpp.compilerVersion, "19.0.24215.1") >= 0 + return product.qbs.toolchainType.includes("clang-cl") || + Utilities.versionCompare(product.cpp.compilerVersion, "19.0.24215.1") >= 0 } function handleDiscardProperty(product, flags) { @@ -355,49 +390,76 @@ function prepareLinker(project, product, inputs, outputs, input, output) { var linkDLL = (outputs.dynamiclibrary ? true : false) var primaryOutput = (linkDLL ? outputs.dynamiclibrary[0] : outputs.application[0]) var debugInformation = product.cpp.debugInformation; - var additionalManifestInputs = Array.prototype.map.call(inputs["native.pe.manifest"], + var additionalManifestInputs = Array.prototype.map.call(inputs["native.pe.manifest"] || [], + function (a) { + return a.filePath; + }); + var moduleDefinitionInputs = Array.prototype.map.call(inputs["def"] || [], function (a) { return a.filePath; }); var generateManifestFiles = !linkDLL && product.cpp.generateManifestFile; - var canEmbedManifest = (product.cpp.compilerVersionMajor >= 17); // VS 2012 + var useClangCl = product.qbs.toolchain.includes("clang-cl"); + var canEmbedManifest = useClangCl || product.cpp.compilerVersionMajor >= 17 // VS 2012 + + var linkerPath = effectiveLinkerPath(product, inputs); + var useCompilerDriver = linkerPath === product.cpp.compilerPath; + // args variable is built as follows: + // [linkerWrapper] linkerPath /nologo [driverFlags driverLinkerFlags] + // allInputs libDeps [/link] linkerArgs + var args = [] + + if (useCompilerDriver) { + args.push('/nologo'); + args = args.concat(Cpp.collectMiscDriverArguments(product), + Cpp.collectMiscLinkerArguments(product)); + } + + var allInputs = [].concat(Cpp.collectLinkerObjectPaths(inputs), + Cpp.collectResourceObjectPaths(inputs)); + args = args.concat([].uniqueConcat(allInputs).map(function(path) { + return FileInfo.toWindowsSeparators(path); + })); - var args = ['/nologo'] + var linkerArgs = ['/nologo'] if (linkDLL) { - args.push('/DLL'); - args.push('/IMPLIB:' + FileInfo.toWindowsSeparators(outputs.dynamiclibrary_import[0].filePath)); + linkerArgs.push('/DLL'); + linkerArgs.push('/IMPLIB:' + FileInfo.toWindowsSeparators(outputs.dynamiclibrary_import[0].filePath)); } if (debugInformation) { - args.push("/DEBUG"); + linkerArgs.push("/DEBUG"); var debugInfo = outputs.debuginfo_app || outputs.debuginfo_dll; if (debugInfo) - args.push("/PDB:" + debugInfo[0].fileName); + linkerArgs.push("/PDB:" + debugInfo[0].fileName); } else { - args.push('/INCREMENTAL:NO') + linkerArgs.push('/INCREMENTAL:NO') } switch (product.qbs.architecture) { case "x86": - args.push("/MACHINE:X86"); + linkerArgs.push("/MACHINE:X86"); break; case "x86_64": - args.push("/MACHINE:X64"); + linkerArgs.push("/MACHINE:X64"); break; case "ia64": - args.push("/MACHINE:IA64"); + linkerArgs.push("/MACHINE:IA64"); break; case "armv7": - args.push("/MACHINE:ARM"); + linkerArgs.push("/MACHINE:ARM"); break; case "arm64": - args.push("/MACHINE:ARM64"); + linkerArgs.push("/MACHINE:ARM64"); break; } + if (useCompilerDriver) + handleClangClArchitectureFlags(product, product.qbs.architecture, args); + var requireAppContainer = product.cpp.requireAppContainer; if (requireAppContainer !== undefined) - args.push("/APPCONTAINER" + (requireAppContainer ? "" : ":NO")); + linkerArgs.push("/APPCONTAINER" + (requireAppContainer ? "" : ":NO")); var minimumWindowsVersion = product.cpp.minimumWindowsVersion; var subsystemSwitch = undefined; @@ -407,26 +469,29 @@ function prepareLinker(project, product, inputs, outputs, input, output) { subsystemSwitch = product.consoleApplication === false ? '/SUBSYSTEM:WINDOWS' : '/SUBSYSTEM:CONSOLE'; } + var useLldLink = useCompilerDriver && product.cpp.linkerVariant === "lld" + || !useCompilerDriver && product.cpp.linkerName === "lld-link.exe"; if (minimumWindowsVersion) { var subsystemVersion = WindowsUtils.getWindowsVersionInFormat(minimumWindowsVersion, 'subsystem'); if (subsystemVersion) { subsystemSwitch += ',' + subsystemVersion; - if (product.cpp.linkerName !== "lld-link.exe") // llvm linker does not support /OSVERSION - args.push('/OSVERSION:' + subsystemVersion); + // llvm linker does not support /OSVERSION + if (!useLldLink) + linkerArgs.push('/OSVERSION:' + subsystemVersion); } } if (subsystemSwitch) - args.push(subsystemSwitch); + linkerArgs.push(subsystemSwitch); var linkerOutputNativeFilePath = FileInfo.toWindowsSeparators(primaryOutput.filePath); var manifestFileNames = []; if (generateManifestFiles) { if (canEmbedManifest) { - args.push("/MANIFEST:embed"); + linkerArgs.push("/MANIFEST:embed"); additionalManifestInputs.forEach(function (manifestFileName) { - args.push("/MANIFESTINPUT:" + manifestFileName); + linkerArgs.push("/MANIFESTINPUT:" + manifestFileName); }); } else { linkerOutputNativeFilePath @@ -435,20 +500,19 @@ function prepareLinker(project, product, inputs, outputs, input, output) { + primaryOutput.fileName); var manifestFileName = linkerOutputNativeFilePath + ".manifest"; - args.push('/MANIFEST', '/MANIFESTFILE:' + manifestFileName); + linkerArgs.push('/MANIFEST', '/MANIFESTFILE:' + manifestFileName); manifestFileNames = [manifestFileName].concat(additionalManifestInputs); } } - var allInputs = inputs.obj || []; - for (i in allInputs) { - var fileName = FileInfo.toWindowsSeparators(allInputs[i].filePath) - args.push(fileName) - } + if (moduleDefinitionInputs.length === 1) + linkerArgs.push("/DEF:" + moduleDefinitionInputs[0]); + else if (moduleDefinitionInputs.length > 1) + throw new Error("Only one '.def' file can be specified for linking"); var wholeArchiveSupported = linkerSupportsWholeArchive(product); var wholeArchiveRequested = false; - var libDeps = collectLibraryDependencies(product); + var libDeps = Cpp.collectLibraryDependencies(product); var prevLib; for (i = 0; i < libDeps.length; ++i) { var dep = libDeps[i]; @@ -456,8 +520,16 @@ function prepareLinker(project, product, inputs, outputs, input, output) { if (lib === prevLib) continue; prevLib = lib; - args.push((wholeArchiveSupported && dep.wholeArchive ? "/WHOLEARCHIVE:" : "") - + FileInfo.toWindowsSeparators(lib)); + + if (wholeArchiveSupported && dep.wholeArchive) { + // need to pass libraries to the driver to avoid "no input files" error if no object + // files are specified; thus libraries are duplicated when using "WHOLEARCHIVE" + if (useCompilerDriver && allInputs.length === 0) + args.push(FileInfo.toWindowsSeparators(lib)); + linkerArgs.push("/WHOLEARCHIVE:" + FileInfo.toWindowsSeparators(lib)); + } else { + args.push(FileInfo.toWindowsSeparators(lib)); + } if (dep.wholeArchive) wholeArchiveRequested = true; } @@ -468,25 +540,31 @@ function prepareLinker(project, product, inputs, outputs, input, output) { } if (product.cpp.entryPoint) - args.push("/ENTRY:" + product.cpp.entryPoint); + linkerArgs.push("/ENTRY:" + product.cpp.entryPoint); - if (outputs.application && product.cpp.generateLinkerMapFile) - args.push("/MAP:" + outputs.mem_map[0].filePath); - - args.push('/OUT:' + linkerOutputNativeFilePath) - var libraryPaths = product.cpp.libraryPaths; - if (libraryPaths) - libraryPaths = [].uniqueConcat(libraryPaths); - for (i in libraryPaths) { - args.push('/LIBPATH:' + FileInfo.toWindowsSeparators(libraryPaths[i])) + if (outputs.application && product.cpp.generateLinkerMapFile) { + if (useLldLink) + linkerArgs.push("/lldmap:" + outputs.mem_map[0].filePath); + else + linkerArgs.push("/MAP:" + outputs.mem_map[0].filePath); } - handleDiscardProperty(product, args); + + if (useCompilerDriver) + args.push('/Fe' + linkerOutputNativeFilePath); + else + linkerArgs.push('/OUT:' + linkerOutputNativeFilePath); + + var libraryPaths = Cpp.collectLibraryPaths(product); + linkerArgs = linkerArgs.concat([].uniqueConcat(libraryPaths).map(function(path) { + return product.cpp.libraryPathFlag + FileInfo.toWindowsSeparators(path); + })); + + handleDiscardProperty(product, linkerArgs); var linkerFlags = product.cpp.platformLinkerFlags.concat(product.cpp.linkerFlags); - args = args.concat(linkerFlags); + linkerArgs = linkerArgs.concat(linkerFlags); if (product.cpp.allowUnresolvedSymbols) - args.push("/FORCE:UNRESOLVED"); + linkerArgs.push("/FORCE:UNRESOLVED"); - var linkerPath = product.cpp.linkerPath; var wrapperArgs = product.cpp.linkerWrapper; if (wrapperArgs && wrapperArgs.length > 0) { args.unshift(linkerPath); @@ -509,6 +587,10 @@ function prepareLinker(project, product, inputs, outputs, input, output) { } }; commands.push(warningCmd); + + if (linkerArgs.length !== 0) + args = args.concat(useCompilerDriver ? ['/link'] : []).concat(linkerArgs); + var cmd = new Command(linkerPath, args) cmd.description = 'linking ' + primaryOutput.fileName; cmd.highlight = 'linker'; @@ -516,9 +598,13 @@ function prepareLinker(project, product, inputs, outputs, input, output) { cmd.relevantEnvironmentVariables = ["LINK", "_LINK_", "LIB", "TMP"]; cmd.workingDirectory = FileInfo.path(primaryOutput.filePath) cmd.responseFileUsagePrefix = '@'; + cmd.responseFileSeparator = useCompilerDriver ? ' ' : '\n'; cmd.stdoutFilterFunction = function(output) { res = output.replace(/^.*performing full link.*\s*/, ""); - return res.replace(/^ *Creating library.*\r\n$/, ""); + res = res.replace(/^ *Creating library.*\s*/, ""); + res = res.replace(/^\s*Generating code\s*/, ""); + res = res.replace(/^\s*Finished generating code\s*/, ""); + return res; }; commands.push(cmd); @@ -542,6 +628,12 @@ function prepareLinker(project, product, inputs, outputs, input, output) { commands.push(cmd); } + if (product.cpp.shouldSignArtifacts) { + Array.prototype.push.apply( + commands, Codesign.prepareSigntool( + project, product, inputs, outputs, input, output)); + } + return commands; } diff --git a/share/qbs/modules/cpp/qnx-qcc.qbs b/share/qbs/modules/cpp/qnx-qcc.qbs index a39a6a998..69288ddd8 100644 --- a/share/qbs/modules/cpp/qnx-qcc.qbs +++ b/share/qbs/modules/cpp/qnx-qcc.qbs @@ -33,8 +33,8 @@ import qbs.FileInfo UnixGCC { Depends { name: "qnx" } - condition: qbs.targetOS.contains("qnx") && - qbs.toolchain && qbs.toolchain.contains("qcc") + condition: qbs.targetOS.includes("qnx") && + qbs.toolchain && qbs.toolchain.includes("qcc") priority: 1 distributionIncludePaths: FileInfo.joinPaths(qnx.targetDir, "usr", "include") @@ -79,7 +79,7 @@ UnixGCC { toolchainPrefix: target + "-" - targetVendor: ["x86", "x86_64"].contains(qbs.architecture) ? "pc" : base + targetVendor: ["x86", "x86_64"].includes(qbs.architecture) ? "pc" : base targetSystem: "nto" targetAbi: "qnx" + qnx.version + (qnxTargetArchName === "armv7le" ? "eabi" : "") diff --git a/share/qbs/modules/cpp/sdcc.js b/share/qbs/modules/cpp/sdcc.js index 70d0506b9..49da8a715 100644 --- a/share/qbs/modules/cpp/sdcc.js +++ b/share/qbs/modules/cpp/sdcc.js @@ -28,10 +28,12 @@ ** ****************************************************************************/ +var BinaryFile = require("qbs.BinaryFile"); var Cpp = require("cpp.js"); var Environment = require("qbs.Environment"); var File = require("qbs.File"); var FileInfo = require("qbs.FileInfo"); +var Host = require("qbs.Host"); var ModUtils = require("qbs.ModUtils"); var PathTools = require("qbs.PathTools"); var Process = require("qbs.Process"); @@ -40,34 +42,24 @@ var TextFile = require("qbs.TextFile"); var Utilities = require("qbs.Utilities"); var WindowsUtils = require("qbs.WindowsUtils"); -function compilerName(qbs) { - return "sdcc"; -} - -function assemblerName(qbs) { - switch (qbs.architecture) { - case "mcs51": - return "sdas8051"; - case "stm8": - return "sdasstm8"; - } - throw "Unable to deduce assembler name for unsupported architecture: '" - + qbs.architecture + "'"; -} - -function linkerName(qbs) { - switch (qbs.architecture) { - case "mcs51": - return "sdld"; - case "stm8": - return "sdldstm8"; +function toolchainDetails(qbs) { + var architecture = qbs.architecture; + if (architecture === "mcs51") { + return { + "assemblerName": "sdas8051", + "linkerName": "sdld" + } + } else if (architecture === "stm8") { + return { + "assemblerName": "sdasstm8", + "linkerName": "sdldstm8" + } + } else if (architecture === "hcs8") { + return { + "assemblerName": "sdas6808", + "linkerName": "sdld6808" + } } - throw "Unable to deduce linker name for unsupported architecture: '" - + qbs.architecture + "'"; -} - -function archiverName(qbs) { - return "sdar"; } function targetArchitectureFlag(architecture) { @@ -75,6 +67,8 @@ function targetArchitectureFlag(architecture) { return "-mmcs51"; if (architecture === "stm8") return "-mstm8"; + if (architecture === "hcs8") + return "-mhc08"; } function guessArchitecture(macros) { @@ -82,6 +76,8 @@ function guessArchitecture(macros) { return "mcs51"; if (macros["__SDCC_stm8"] === "1") return "stm8"; + if (macros["__SDCC_hc08"] === "1") + return "hcs8"; } function guessEndianness(macros) { @@ -90,10 +86,20 @@ function guessEndianness(macros) { } function guessVersion(macros) { - return { major: parseInt(macros["__SDCC_VERSION_MAJOR"], 10), - minor: parseInt(macros["__SDCC_VERSION_MINOR"], 10), - patch: parseInt(macros["__SDCC_VERSION_PATCH"], 10), - found: macros["SDCC"] } + if ("__SDCC_VERSION_MAJOR" in macros + && "__SDCC_VERSION_MINOR" in macros + && "__SDCC_VERSION_PATCH" in macros) { + return { major: parseInt(macros["__SDCC_VERSION_MAJOR"], 10), + minor: parseInt(macros["__SDCC_VERSION_MINOR"], 10), + patch: parseInt(macros["__SDCC_VERSION_PATCH"], 10) } + } else if ("__SDCC" in macros) { + var versions = macros["__SDCC"].split("_"); + if (versions.length === 3) { + return { major: parseInt(versions[0], 10), + minor: parseInt(versions[1], 10), + patch: parseInt(versions[2], 10) }; + } + } } function dumpMacros(compilerFilePath, architecture) { @@ -107,12 +113,7 @@ function dumpMacros(compilerFilePath, architecture) { var p = new Process(); p.exec(compilerFilePath, args, true); - var map = {}; - p.readStdOut().trim().split(/\r?\n/g).map(function (line) { - var parts = line.split(" ", 3); - map[parts[1]] = parts[2]; - }); - return map; + return Cpp.extractMacros(p.readStdOut()); } function dumpDefaultPaths(compilerFilePath, architecture) { @@ -137,7 +138,8 @@ function dumpDefaultPaths(compilerFilePath, architecture) { || line.startsWith("libpath:")) { addIncludePaths = false; } else if (addIncludePaths) { - includePaths.push(line); + if (File.exists(line)) + includePaths.push(line); } } @@ -163,142 +165,69 @@ function escapeLinkerFlags(product, linkerFlags) { return ["-Wl " + linkerFlags.join(",")]; } -function collectLibraryDependencies(product) { - var seen = {}; - var result = []; - - function addFilePath(filePath) { - result.push({ filePath: filePath }); - } - - function addArtifactFilePaths(dep, artifacts) { - if (!artifacts) - return; - var artifactFilePaths = artifacts.map(function(a) { return a.filePath; }); - artifactFilePaths.forEach(addFilePath); - } - - function addExternalStaticLibs(obj) { - if (!obj.cpp) - return; - function ensureArray(a) { - return Array.isArray(a) ? a : []; - } - function sanitizedModuleListProperty(obj, moduleName, propertyName) { - return ensureArray(ModUtils.sanitizedModuleProperty(obj, moduleName, propertyName)); - } - var externalLibs = [].concat( - sanitizedModuleListProperty(obj, "cpp", "staticLibraries")); - var staticLibrarySuffix = obj.moduleProperty("cpp", "staticLibrarySuffix"); - externalLibs.forEach(function(staticLibraryName) { - if (!staticLibraryName.endsWith(staticLibrarySuffix)) - staticLibraryName += staticLibrarySuffix; - addFilePath(staticLibraryName); - }); - } - - function traverse(dep) { - if (seen.hasOwnProperty(dep.name)) - return; - seen[dep.name] = true; - - if (dep.parameters.cpp && dep.parameters.cpp.link === false) - return; - - var staticLibraryArtifacts = dep.artifacts["staticlibrary"]; - if (staticLibraryArtifacts) { - dep.dependencies.forEach(traverse); - addArtifactFilePaths(dep, staticLibraryArtifacts); - addExternalStaticLibs(dep); - } - } +function escapePreprocessorFlags(preprocessorFlags) { + if (!preprocessorFlags || preprocessorFlags.length === 0) + return preprocessorFlags; + return ["-Wp " + preprocessorFlags.join(",")]; +} - product.dependencies.forEach(traverse); - addExternalStaticLibs(product); - return result; +// We need to use the asm_adb, asm_src, asm_sym and rst_data +// artifacts without of any conditions. Because SDCC always generates +// it (and seems, this behavior can not be disabled for SDCC). +function extraCompilerOutputTags() { + return ["asm_adb", "asm_src", "asm_sym", "rst_data"]; } -function compilerOutputArtifacts(input) { - var obj = { - fileTags: ["obj"], - filePath: Utilities.getHash(input.baseDir) + "/" - + input.fileName + input.cpp.objectSuffix - }; +// We need to use the lk_cmd, and mem_summary artifacts without +// of any conditions. Because SDCC always generates +// it (and seems, this behavior can not be disabled for SDCC). +function extraApplicationLinkerOutputTags() { + return ["lk_cmd", "mem_summary"]; +} - // We need to use the asm_adb, lst, asm_src, asm_sym and rst_data - // artifacts without of any conditions. Because SDCC always generates - // it (and seems, this behavior can not be disabled for SDCC). - var asm_adb = { +// We need to use the asm_adb, asm_src, asm_sym and rst_data +// artifacts without of any conditions. Because SDCC always generates +// it (and seems, this behavior can not be disabled for SDCC). +function extraCompilerOutputArtifacts(input) { + return [{ fileTags: ["asm_adb"], filePath: Utilities.getHash(input.baseDir) + "/" - + input.fileName + ".adb" - }; - var lst = { - fileTags: ["lst"], - filePath: Utilities.getHash(input.baseDir) + "/" - + input.fileName + ".lst" - }; - var asm_src = { + + input.fileName + ".adb" + }, { fileTags: ["asm_src"], filePath: Utilities.getHash(input.baseDir) + "/" - + input.fileName + ".asm" - }; - var asm_sym = { + + input.fileName + ".asm" + }, { fileTags: ["asm_sym"], filePath: Utilities.getHash(input.baseDir) + "/" - + input.fileName + ".sym" - }; - var rst_data = { + + input.fileName + ".sym" + }, { fileTags: ["rst_data"], filePath: Utilities.getHash(input.baseDir) + "/" - + input.fileName + ".rst" - }; - return [obj, asm_adb, lst, asm_src, asm_sym, rst_data]; + + input.fileName + ".rst" + }]; } -function applicationLinkerOutputArtifacts(product) { - var app = { - fileTags: ["application"], - filePath: FileInfo.joinPaths( - product.destinationDirectory, - PathTools.applicationFilePath(product)) - }; - var lk_cmd = { +// We need to use the lk_cmd, and mem_summary artifacts without +// of any conditions. Because SDCC always generates +// it (and seems, this behavior can not be disabled for SDCC). +function extraApplicationLinkerOutputArtifacts(product) { + return [{ fileTags: ["lk_cmd"], filePath: FileInfo.joinPaths( - product.destinationDirectory, - product.targetName + ".lk") - }; - var mem_summary = { + product.destinationDirectory, + product.targetName + ".lk") + }, { fileTags: ["mem_summary"], filePath: FileInfo.joinPaths( - product.destinationDirectory, - product.targetName + ".mem") - }; - var mem_map = { - fileTags: ["mem_map"], - filePath: FileInfo.joinPaths( - product.destinationDirectory, - product.targetName + ".map") - }; - return [app, lk_cmd, mem_summary, mem_map] -} - -function staticLibraryLinkerOutputArtifacts(product) { - var staticLib = { - fileTags: ["staticlibrary"], - filePath: FileInfo.joinPaths( - product.destinationDirectory, - PathTools.staticLibraryFilePath(product)) - }; - return [staticLib] + product.destinationDirectory, + product.targetName + ".mem") + }]; } function compilerFlags(project, product, input, outputs, explicitlyDependsOn) { - // Determine which C-language we"re compiling. - var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(outputs.obj[0].fileTags)); - var args = []; + var escapablePreprocessorFlags = []; // Input. args.push(input.filePath); @@ -307,29 +236,19 @@ function compilerFlags(project, product, input, outputs, explicitlyDependsOn) { args.push("-c"); args.push("-o", outputs.obj[0].filePath); + // Prefix headers. + escapablePreprocessorFlags = escapablePreprocessorFlags.concat( + Cpp.collectPreincludePathsArguments(input)); + // Defines. - var allDefines = []; - var platformDefines = input.cpp.platformDefines; - if (platformDefines) - allDefines = allDefines.uniqueConcat(platformDefines); - var defines = input.cpp.defines; - if (defines) - allDefines = allDefines.uniqueConcat(defines); - args = args.concat(allDefines.map(function(define) { return "-D" + define })); + args = args.concat(Cpp.collectDefinesArguments(input)); // Includes. - var allIncludePaths = []; - var includePaths = input.cpp.includePaths; - if (includePaths) - allIncludePaths = allIncludePaths.uniqueConcat(includePaths); - var systemIncludePaths = input.cpp.systemIncludePaths; - if (systemIncludePaths) - allIncludePaths = allIncludePaths.uniqueConcat(systemIncludePaths); - var compilerIncludePaths = input.cpp.compilerIncludePaths; - if (compilerIncludePaths) - allIncludePaths = allIncludePaths.uniqueConcat(compilerIncludePaths); - args = args.concat(allIncludePaths.map(function(include) { return "-I" + include })); + args = args.concat(Cpp.collectIncludePathsArguments(input)); + escapablePreprocessorFlags = escapablePreprocessorFlags.concat( + Cpp.collectSystemIncludePathsArguments(input)); + // Target MCU flag. var targetFlag = targetArchitectureFlag(input.cpp.architecture); if (targetFlag) args.push(targetFlag); @@ -352,56 +271,56 @@ function compilerFlags(project, product, input, outputs, explicitlyDependsOn) { } // Warning level flags. - if (input.cpp.warningLevel === "none") + var warnings = input.cpp.warningLevel + if (warnings === "none") { args.push("--less-pedantic"); + escapablePreprocessorFlags.push("-w"); + } else if (warnings === "all") { + escapablePreprocessorFlags.push("-Wall"); + } if (input.cpp.treatWarningsAsErrors) args.push("--Werror"); + // Determine which C-language we"re compiling. + var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(outputs.obj[0].fileTags)); + // C language version flags. if (tag === "c") { - var knownValues = ["c11", "c89"]; + var knownValues = ["c2x", "c17", "c11", "c99", "c89"]; var cLanguageVersion = Cpp.languageVersion( input.cpp.cLanguageVersion, knownValues, "C"); switch (cLanguageVersion) { + case "c17": + cLanguageVersion = "c11"; + // fall through case "c89": - args.push("--std-c89"); - break; + case "c99": case "c11": - args.push("--std-c11"); + case "c2x": + args.push("--std-" + cLanguageVersion); break; } } - // Misc flags. - args = args.concat(ModUtils.moduleProperty(input, "platformFlags"), - ModUtils.moduleProperty(input, "flags"), - ModUtils.moduleProperty(input, "platformFlags", tag), - ModUtils.moduleProperty(input, "flags", tag), - ModUtils.moduleProperty(input, "driverFlags", tag)); + var escapedPreprocessorFlags = escapePreprocessorFlags(escapablePreprocessorFlags); + if (escapedPreprocessorFlags) + Array.prototype.push.apply(args, escapedPreprocessorFlags); + // Misc flags. + args = args.concat(Cpp.collectMiscCompilerArguments(input, tag), + Cpp.collectMiscDriverArguments(input)); return args; } function assemblerFlags(project, product, input, outputs, explicitlyDependsOn) { - // Determine which C-language we"re compiling - var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(outputs.obj[0].fileTags)); - var args = []; // Includes. - var allIncludePaths = []; - var systemIncludePaths = input.cpp.systemIncludePaths; - if (systemIncludePaths) - allIncludePaths = allIncludePaths.uniqueConcat(systemIncludePaths); - var compilerIncludePaths = input.cpp.compilerIncludePaths; - if (compilerIncludePaths) - allIncludePaths = allIncludePaths.uniqueConcat(compilerIncludePaths); - args = args.concat(allIncludePaths.map(function(include) { return "-I" + include })); + args = args.concat(Cpp.collectIncludePathsArguments(input)); + args = args.concat(Cpp.collectSystemIncludePathsArguments(input, input.cpp.includeFlag)); // Misc flags. - args = args.concat(ModUtils.moduleProperty(input, "platformFlags", tag), - ModUtils.moduleProperty(input, "flags", tag), - ModUtils.moduleProperty(input, "driverFlags", tag)); + args = args.concat(Cpp.collectMiscAssemblerArguments(input, "asm")); args.push("-ol"); args.push(outputs.obj[0].filePath); @@ -409,7 +328,7 @@ function assemblerFlags(project, product, input, outputs, explicitlyDependsOn) { return args; } -function linkerFlags(project, product, input, outputs) { +function linkerFlags(project, product, inputs, outputs) { var args = []; // Target MCU flag. @@ -417,118 +336,237 @@ function linkerFlags(project, product, input, outputs) { if (targetFlag) args.push(targetFlag); - var allLibraryPaths = []; - var libraryPaths = product.cpp.libraryPaths; - if (libraryPaths) - allLibraryPaths = allLibraryPaths.uniqueConcat(libraryPaths); - var distributionLibraryPaths = product.cpp.distributionLibraryPaths; - if (distributionLibraryPaths) - allLibraryPaths = allLibraryPaths.uniqueConcat(distributionLibraryPaths); - - var libraryDependencies = collectLibraryDependencies(product); - var escapableLinkerFlags = []; // Map file generation flag. if (product.cpp.generateLinkerMapFile) escapableLinkerFlags.push("-m"); - if (product.cpp.platformLinkerFlags) - Array.prototype.push.apply(escapableLinkerFlags, product.cpp.platformLinkerFlags); - if (product.cpp.linkerFlags) - Array.prototype.push.apply(escapableLinkerFlags, product.cpp.linkerFlags); + escapableLinkerFlags = escapableLinkerFlags.concat(Cpp.collectMiscEscapableLinkerArguments(product)); var useCompilerDriver = useCompilerDriverLinker(product); - if (useCompilerDriver) { - // Output. - args.push("-o", outputs.application[0].filePath); - - // Inputs. - if (inputs.obj) - args = args.concat(inputs.obj.map(function(obj) { return obj.filePath })); - - // Library paths. - args = args.concat(allLibraryPaths.map(function(path) { return "-L" + path })); - - // Linker scripts. - var scripts = inputs.linkerscript - ? inputs.linkerscript.map(function(scr) { return "-f" + scr.filePath; }) : []; - if (scripts) - Array.prototype.push.apply(escapableLinkerFlags, scripts); - } else { - // Output. - args.push(outputs.application[0].filePath); - - // Inputs. - if (inputs.obj) - args = args.concat(inputs.obj.map(function(obj) { return obj.filePath })); - - // Library paths. - args = args.concat(allLibraryPaths.map(function(path) { return "-k" + path })); - - // Linker scripts. - // Note: We need to split the '-f' and the file path to separate - // lines; otherwise the linking fails. - inputs.linkerscript.forEach(function(scr) { - escapableLinkerFlags.push("-f", scr.filePath); - }); - } + + // Output. + if (useCompilerDriver) + args.push("-o"); + args.push(outputs.application[0].filePath); + + // Inputs. + args = args.concat(Cpp.collectLinkerObjectPaths(inputs)); + + // Library paths. + var libraryPathFlag = useCompilerDriver ? "-L" : "-k"; + args = args.concat(Cpp.collectLibraryPathsArguments(product, libraryPathFlag)); + + // Linker scripts. + // Note: We need to split the '-f' flag and the file path to separate + // lines when we don't use the compiler driver mode. + escapableLinkerFlags = escapableLinkerFlags.concat( + Cpp.collectLinkerScriptPathsArguments(product, inputs, !useCompilerDriver)); // Library dependencies. - if (libraryDependencies) - args = args.concat(libraryDependencies.map(function(dep) { return "-l" + dep.filePath })); + args = args.concat(Cpp.collectLibraryDependenciesArguments(product)); - // Misc flags. var escapedLinkerFlags = escapeLinkerFlags(product, escapableLinkerFlags); if (escapedLinkerFlags) Array.prototype.push.apply(args, escapedLinkerFlags); - var driverLinkerFlags = useCompilerDriver ? product.cpp.driverLinkerFlags : undefined; - if (driverLinkerFlags) - Array.prototype.push.apply(args, driverLinkerFlags); + + // Misc flags. + if (useCompilerDriver) { + args = args.concat(Cpp.collectMiscLinkerArguments(product), + Cpp.collectMiscDriverArguments(product)); + } return args; } -function archiverFlags(project, product, input, outputs) { +function archiverFlags(project, product, inputs, outputs) { var args = ["-rc"]; args.push(outputs.staticlibrary[0].filePath); - if (inputs.obj) - args = args.concat(inputs.obj.map(function(obj) { return obj.filePath })); + args = args.concat(Cpp.collectLinkerObjectPaths(inputs)); return args; } +function buildLinkerMapFilePath(target, suffix) { + return FileInfo.joinPaths(FileInfo.path(target.filePath), + FileInfo.completeBaseName(target.fileName) + suffix); +} + +// This is the workaround for the SDCC bug on a Windows host: +// * https://sourceforge.net/p/sdcc/bugs/2970/ +// We need to replace the '\r\n\' line endings with the'\n' line +// endings for each generated object file. +function patchObjectFile(project, product, inputs, outputs, input, output) { + var isWindows = Host.os().includes("windows"); + if (isWindows && input.cpp.debugInformation) { + var cmd = new JavaScriptCommand(); + cmd.objectPath = outputs.obj[0].filePath; + cmd.silent = true; + cmd.sourceCode = function() { + var file = new BinaryFile(objectPath, BinaryFile.ReadWrite); + var data = file.read(file.size()); + file.resize(0); + for (var pos = 0; pos < data.length; ++pos) { + // Find the next index of CR (\r) symbol. + var index = data.indexOf(0x0d, pos); + if (index < 0) + index = data.length; + // Write next data chunk between the previous position and the CR + // symbol, exclude the CR symbol. + file.write(data.slice(pos, index)); + pos = index; + } + }; + return cmd; + } +} + +// It is a workaround which removes the generated linker map file +// if it is disabled by cpp.generateLinkerMapFile property. +// Reason is that the SDCC compiler always generates this file, +// and does not have an option to disable generation for a linker +// map file. So, we can to remove a listing files only after the +// linking completes. +function removeLinkerMapFile(project, product, inputs, outputs, input, output) { + if (!product.cpp.generateLinkerMapFile) { + var target = outputs.application[0]; + var cmd = new JavaScriptCommand(); + cmd.mapFilePath = buildLinkerMapFilePath(target, product.cpp.linkerMapSuffix) + cmd.silent = true; + cmd.sourceCode = function() { File.remove(mapFilePath); }; + return cmd; + } +} + +// It is a workaround to rename the extension of the output linker +// map file to the specified one, since the linker generates only +// files with the '.map' extension. +function renameLinkerMapFile(project, product, inputs, outputs, input, output) { + if (product.cpp.generateLinkerMapFile && (product.cpp.linkerMapSuffix !== ".map")) { + var target = outputs.application[0]; + var cmd = new JavaScriptCommand(); + cmd.newMapFilePath = buildLinkerMapFilePath(target, product.cpp.linkerMapSuffix); + cmd.oldMapFilePath = buildLinkerMapFilePath(target, ".map"); + cmd.silent = true; + cmd.sourceCode = function() { File.move(oldMapFilePath, newMapFilePath); }; + return cmd; + } +} + +// It is a workaround which removes the generated listing files +// if it is disabled by cpp.generateCompilerListingFiles property +// or when the cpp.compilerListingSuffix differs with '.lst'. +// Reason is that the SDCC compiler does not have an option to +// disable generation for a listing files. Besides, the SDCC +// compiler use this files and for the linking. So, we can to +// remove a listing files only after the linking completes. +function removeCompilerListingFiles(project, product, inputs, outputs, input, output) { + var cmd = new JavaScriptCommand(); + cmd.silent = true; + cmd.sourceCode = function() { + inputs.obj.forEach(function(object) { + if (!object.filePath.endsWith(".c" + object.cpp.objectSuffix)) + return; // Skip the assembler generated objects. + if (!object.cpp.generateCompilerListingFiles + || (object.cpp.compilerListingSuffix !== ".lst")) { + var listingPath = FileInfo.joinPaths(FileInfo.path(object.filePath), + object.completeBaseName + ".lst"); + File.remove(listingPath); + } + }) + }; + return cmd; +} + +// It is a workaround that duplicates the generated listing files +// but with desired names. The problem is that the SDCC compiler does +// not support an options to specify names for the generated listing +// files. At the same time, the compiler always generates the listing +// files in the form of 'module.c.lst', which makes it impossible to +// change the file suffix to a user-specified one. In addition, these +// files are also somehow used for linking. Thus, we can not rename them +// on the compiling stage. +function duplicateCompilerListingFile(project, product, inputs, outputs, input, output) { + if (input.cpp.generateCompilerListingFiles + && (input.cpp.compilerListingSuffix !== ".lst")) { + var cmd = new JavaScriptCommand(); + cmd.newListing = outputs.lst[0].filePath; + cmd.oldListing = FileInfo.joinPaths(FileInfo.path(outputs.lst[0].filePath), + outputs.lst[0].completeBaseName + ".lst"); + cmd.silent = true; + cmd.sourceCode = function() { File.copy(oldListing, newListing); }; + return cmd; + } +} + function prepareCompiler(project, product, inputs, outputs, input, output, explicitlyDependsOn) { + var cmds = []; var args = compilerFlags(project, product, input, outputs, explicitlyDependsOn); var compilerPath = input.cpp.compilerPath; var cmd = new Command(compilerPath, args); cmd.description = "compiling " + input.fileName; cmd.highlight = "compiler"; - return [cmd]; + cmd.jobPool = "compiler"; + cmds.push(cmd); + + cmd = patchObjectFile(project, product, inputs, outputs, input, output); + if (cmd) + cmds.push(cmd); + + cmd = duplicateCompilerListingFile(project, product, inputs, outputs, input, output); + if (cmd) + cmds.push(cmd); + + return cmds; } function prepareAssembler(project, product, inputs, outputs, input, output, explicitlyDependsOn) { + var cmds = []; var args = assemblerFlags(project, product, input, outputs, explicitlyDependsOn); var assemblerPath = input.cpp.assemblerPath; var cmd = new Command(assemblerPath, args); cmd.description = "assembling " + input.fileName; cmd.highlight = "compiler"; - return [cmd]; + cmd.jobPool = "assembler"; + cmds.push(cmd); + + cmd = patchObjectFile(project, product, inputs, outputs, input, output); + if (cmd) + cmds.push(cmd); + + return cmds; } function prepareLinker(project, product, inputs, outputs, input, output) { - var primaryOutput = outputs.application[0]; - var args = linkerFlags(project, product, input, outputs); + var cmds = []; + var args = linkerFlags(project, product, inputs, outputs); var linkerPath = effectiveLinkerPath(product); var cmd = new Command(linkerPath, args); - cmd.description = "linking " + primaryOutput.fileName; + cmd.description = "linking " + outputs.application[0].fileName; cmd.highlight = "linker"; - return [cmd]; + cmd.jobPool = "linker"; + cmds.push(cmd); + + cmd = removeCompilerListingFiles(project, product, inputs, outputs, input, output); + if (cmd) + cmds.push(cmd); + + cmd = renameLinkerMapFile(project, product, inputs, outputs, input, output); + if (cmd) + cmds.push(cmd); + + cmd = removeLinkerMapFile(project, product, inputs, outputs, input, output); + if (cmd) + cmds.push(cmd); + + return cmds; } function prepareArchiver(project, product, inputs, outputs, input, output) { - var args = archiverFlags(project, product, input, outputs); + var args = archiverFlags(project, product, inputs, outputs); var archiverPath = product.cpp.archiverPath; var cmd = new Command(archiverPath, args); - cmd.description = "linking " + output.fileName; + cmd.description = "creating " + output.fileName; cmd.highlight = "linker"; + cmd.jobPool = "linker"; return [cmd]; } diff --git a/share/qbs/modules/cpp/sdcc.qbs b/share/qbs/modules/cpp/sdcc.qbs index ec763ba47..24cb6d738 100644 --- a/share/qbs/modules/cpp/sdcc.qbs +++ b/share/qbs/modules/cpp/sdcc.qbs @@ -28,15 +28,14 @@ ** ****************************************************************************/ -import qbs 1.0 import qbs.File import qbs.FileInfo -import qbs.ModUtils import qbs.Probes +import "cpp.js" as Cpp import "sdcc.js" as SDCC CppModule { - condition: qbs.toolchain && qbs.toolchain.contains("sdcc") + condition: qbs.toolchain && qbs.toolchain.includes("sdcc") Probes.BinaryProbe { id: compilerPathProbe @@ -48,10 +47,12 @@ CppModule { id: sdccProbe condition: !_skipAllChecks compilerFilePath: compilerPath + enableDefinesByLanguage: enableCompilerDefinesByLanguage preferredArchitecture: qbs.architecture } qbs.architecture: sdccProbe.found ? sdccProbe.architecture : original + qbs.targetPlatform: "none" compilerVersionMajor: sdccProbe.versionMajor compilerVersionMinor: sdccProbe.versionMinor @@ -61,10 +62,7 @@ CppModule { compilerDefinesByLanguage: sdccProbe.compilerDefinesByLanguage compilerIncludePaths: sdccProbe.includePaths - property string toolchainInstallPath: compilerPathProbe.found - ? compilerPathProbe.path : undefined - - property string compilerExtension: qbs.hostOS.contains("windows") ? ".exe" : "" + toolchainInstallPath: compilerPathProbe.found ? compilerPathProbe.path : undefined /* Work-around for QtCreator which expects these properties to exist. */ property string cCompilerName: compilerName @@ -72,47 +70,53 @@ CppModule { property string linkerMode: "automatic" - compilerName: SDCC.compilerName(qbs) + compilerExtension + compilerName: "sdcc" + compilerExtension compilerPath: FileInfo.joinPaths(toolchainInstallPath, compilerName) - assemblerName: SDCC.assemblerName(qbs) + compilerExtension + assemblerName: toolchainDetails.assemblerName + compilerExtension assemblerPath: FileInfo.joinPaths(toolchainInstallPath, assemblerName) - linkerName: SDCC.linkerName(qbs) + compilerExtension + linkerName: toolchainDetails.linkerName + compilerExtension linkerPath: FileInfo.joinPaths(toolchainInstallPath, linkerName) - property string archiverName: SDCC.archiverName(qbs) + compilerExtension + property string archiverName: "sdar" + compilerExtension property string archiverPath: FileInfo.joinPaths(toolchainInstallPath, archiverName) runtimeLibrary: "static" staticLibrarySuffix: ".lib" executableSuffix: ".ihx" - - property string objectSuffix: ".rel" + objectSuffix: ".rel" imageFormat: "ihx" enableExceptions: false enableRtti: false + defineFlag: "-D" + includeFlag: "-I" + systemIncludeFlag: "-isystem" + preincludeFlag: "-include" + libraryDependencyFlag: "-l" + libraryPathFlag: "-L" + linkerScriptFlag: "-f" + + toolchainDetails: SDCC.toolchainDetails(qbs) + + knownArchitectures: ["hcs8", "mcs51", "stm8"] + Rule { id: assembler inputs: ["asm"] - outputFileTags: ["obj", "asm_adb", "lst", "asm_src", "asm_sym", "rst_data"] - outputArtifacts: SDCC.compilerOutputArtifacts(input) + outputFileTags: SDCC.extraCompilerOutputTags().concat( + Cpp.assemblerOutputTags(generateAssemblerListingFiles)) + outputArtifacts: SDCC.extraCompilerOutputArtifacts(input).concat( + Cpp.assemblerOutputArtifacts(input)) prepare: SDCC.prepareAssembler.apply(SDCC, arguments) } FileTagger { - condition: qbs.architecture === "arm"; - patterns: "*.s" - fileTags: ["asm"] - } - - FileTagger { - condition: qbs.architecture === "mcs51"; - patterns: ["*.s51", "*.asm"] + patterns: ["*.s", "*.a51", "*.asm"] fileTags: ["asm"] } @@ -120,8 +124,10 @@ CppModule { id: compiler inputs: ["cpp", "c"] auxiliaryInputs: ["hpp"] - outputFileTags: ["obj", "asm_adb", "lst", "asm_src", "asm_sym", "rst_data"] - outputArtifacts: SDCC.compilerOutputArtifacts(input) + outputFileTags: SDCC.extraCompilerOutputTags().concat( + Cpp.compilerOutputTags(generateCompilerListingFiles)) + outputArtifacts: SDCC.extraCompilerOutputArtifacts(input).concat( + Cpp.compilerOutputArtifacts(input)) prepare: SDCC.prepareCompiler.apply(SDCC, arguments) } @@ -130,8 +136,10 @@ CppModule { multiplex: true inputs: ["obj", "linkerscript"] inputsFromDependencies: ["staticlibrary"] - outputFileTags: ["application", "lk_cmd", "mem_summary", "mem_map"] - outputArtifacts: SDCC.applicationLinkerOutputArtifacts(product) + outputFileTags: SDCC.extraApplicationLinkerOutputTags().concat( + Cpp.applicationLinkerOutputTags(generateLinkerMapFile)) + outputArtifacts: SDCC.extraApplicationLinkerOutputArtifacts(product).concat( + Cpp.applicationLinkerOutputArtifacts(product)) prepare: SDCC.prepareLinker.apply(SDCC, arguments) } @@ -140,8 +148,8 @@ CppModule { multiplex: true inputs: ["obj"] inputsFromDependencies: ["staticlibrary"] - outputFileTags: ["staticlibrary"] - outputArtifacts: SDCC.staticLibraryLinkerOutputArtifacts(product) + outputFileTags: Cpp.staticLibraryLinkerOutputTags() + outputArtifacts: Cpp.staticLibraryLinkerOutputArtifacts(product) prepare: SDCC.prepareArchiver.apply(SDCC, arguments) } } diff --git a/share/qbs/modules/cpp/setuprunenv.js b/share/qbs/modules/cpp/setuprunenv.js index 550d08d63..2ea331a30 100644 --- a/share/qbs/modules/cpp/setuprunenv.js +++ b/share/qbs/modules/cpp/setuprunenv.js @@ -30,11 +30,12 @@ var FileInfo = require("qbs.FileInfo"); var File = require("qbs.File"); +var Host = require("qbs.Host"); var ModUtils = require("qbs.ModUtils"); // TODO: append/prepend functionality should go to qbs.Environment function addNewElement(list, elem) { - if (!list.contains(elem)) + if (!list.includes(elem)) list.push(elem); } @@ -48,7 +49,7 @@ function artifactDir(artifact) function addExternalLibPath(product, list, path) { addNewElement(list, path); - if (product.qbs.hostOS.contains("windows") && FileInfo.fileName(path) === "lib") { + if (Host.os().includes("windows") && FileInfo.fileName(path) === "lib") { var binPath = FileInfo.joinPaths(FileInfo.path(path), "bin"); if (File.exists(binPath)) addNewElement(list, binPath); @@ -57,7 +58,7 @@ function addExternalLibPath(product, list, path) function gatherPaths(product, libPaths, frameworkPaths, seenProducts) { - if (seenProducts.contains(product.name)) + if (seenProducts.includes(product.name)) return; seenProducts.push(product.name); @@ -66,6 +67,8 @@ function gatherPaths(product, libPaths, frameworkPaths, seenProducts) product.cpp.libraryPaths.forEach(function(p) { addExternalLibPath(product, libPaths, p); }); if (product.cpp && product.cpp.frameworkPaths) product.cpp.frameworkPaths.forEach(function(p) { addNewElement(frameworkPaths, p); }); + if (product.cpp && product.cpp.systemFrameworkPaths) + product.cpp.systemFrameworkPaths.forEach(function(p) { addNewElement(frameworkPaths, p); }); // Extract paths from dynamic libraries, if they are given as file paths. if (product.cpp && product.cpp.dynamicLibraries) { @@ -103,10 +106,10 @@ function gatherPaths(product, libPaths, frameworkPaths, seenProducts) function setupRunEnvironment(product, config) { - if (config.contains("ignore-lib-dependencies")) + if (config.includes("ignore-lib-dependencies")) return; - if (product.qbs.hostPlatform !== product.qbs.targetPlatform) + if (Host.platform() !== product.qbs.targetPlatform) return; var libPaths = []; @@ -117,8 +120,8 @@ function setupRunEnvironment(product, config) if (runPaths && runPaths.length > 0) { var canonicalRunPaths = runPaths.map(function(p) { return File.canonicalFilePath(p); }); var filterFunc = function(libPath) { - return !runPaths.contains(libPath) - && !canonicalRunPaths.contains(File.canonicalFilePath(libPath)); + return !runPaths.includes(libPath) + && !canonicalRunPaths.includes(File.canonicalFilePath(libPath)); }; libPaths = libPaths.filter(filterFunc); frameworkPaths = frameworkPaths.filter(filterFunc); @@ -126,19 +129,19 @@ function setupRunEnvironment(product, config) if (libPaths.length > 0) { var envVarName; - if (product.qbs.targetOS.contains("windows")) + if (product.qbs.targetOS.includes("windows")) envVarName = "PATH"; - else if (product.qbs.targetOS.contains("macos")) + else if (product.qbs.targetOS.includes("macos")) envVarName = "DYLD_LIBRARY_PATH"; else envVarName = "LD_LIBRARY_PATH"; - var envVar = new ModUtils.EnvironmentVariable(envVarName, product.qbs.pathListSeparator, - product.qbs.hostOS.contains("windows")); + var envVar = new ModUtils.EnvironmentVariable(envVarName, FileInfo.pathListSeparator(), + Host.os().includes("windows")); libPaths.forEach(function(p) { envVar.prepend(p); }); envVar.set(); } - if (product.qbs.targetOS.contains("macos") && frameworkPaths.length > 0) { + if (product.qbs.targetOS.includes("macos") && frameworkPaths.length > 0) { envVar = new ModUtils.EnvironmentVariable("DYLD_FRAMEWORK_PATH", ':', false); frameworkPaths.forEach(function(p) { envVar.prepend(p); }); envVar.set(); diff --git a/share/qbs/modules/cpp/tvos-gcc.qbs b/share/qbs/modules/cpp/tvos-gcc.qbs index 19bc9b787..eba3b091e 100644 --- a/share/qbs/modules/cpp/tvos-gcc.qbs +++ b/share/qbs/modules/cpp/tvos-gcc.qbs @@ -30,16 +30,16 @@ DarwinGCC { priority: 1 - condition: qbs.targetOS.contains('tvos') && - qbs.toolchain && qbs.toolchain.contains('gcc') + condition: qbs.targetOS.includes('tvos') && + qbs.toolchain && qbs.toolchain.includes('gcc') targetSystem: "tvos" + (minimumTvosVersion || "") minimumDarwinVersion: minimumTvosVersion - minimumDarwinVersionCompilerFlag: qbs.targetOS.contains("tvos-simulator") + minimumDarwinVersionCompilerFlag: qbs.targetOS.includes("tvos-simulator") ? "-mtvos-simulator-version-min" : "-mtvos-version-min" - minimumDarwinVersionLinkerFlag: qbs.targetOS.contains("tvos-simulator") + minimumDarwinVersionLinkerFlag: qbs.targetOS.includes("tvos-simulator") ? "-tvos_simulator_version_min" : "-tvos_version_min" } diff --git a/share/qbs/modules/cpp/watchos-gcc.qbs b/share/qbs/modules/cpp/watchos-gcc.qbs index 46e4bf874..3a160f6d0 100644 --- a/share/qbs/modules/cpp/watchos-gcc.qbs +++ b/share/qbs/modules/cpp/watchos-gcc.qbs @@ -31,16 +31,16 @@ DarwinGCC { priority: 1 - condition: qbs.targetOS.contains('watchos') && - qbs.toolchain && qbs.toolchain.contains('gcc') + condition: qbs.targetOS.includes('watchos') && + qbs.toolchain && qbs.toolchain.includes('gcc') targetSystem: "watchos" + (minimumWatchosVersion || "") minimumDarwinVersion: minimumWatchosVersion - minimumDarwinVersionCompilerFlag: qbs.targetOS.contains("watchos-simulator") + minimumDarwinVersionCompilerFlag: qbs.targetOS.includes("watchos-simulator") ? "-mwatchos-simulator-version-min" : "-mwatchos-version-min" - minimumDarwinVersionLinkerFlag: qbs.targetOS.contains("watchos-simulator") + minimumDarwinVersionLinkerFlag: qbs.targetOS.includes("watchos-simulator") ? "-watchos_simulator_version_min" : "-watchos_version_min" } diff --git a/share/qbs/modules/cpp/watcom.js b/share/qbs/modules/cpp/watcom.js new file mode 100644 index 000000000..9ae893db9 --- /dev/null +++ b/share/qbs/modules/cpp/watcom.js @@ -0,0 +1,568 @@ +/**************************************************************************** +** +** Copyright (C) 2022 Denis Shienkov <denis.shienkov@gmail.com> +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +var Cpp = require("cpp.js"); +var Environment = require("qbs.Environment"); +var File = require("qbs.File"); +var FileInfo = require("qbs.FileInfo"); +var ModUtils = require("qbs.ModUtils"); +var PathTools = require("qbs.PathTools"); +var Process = require("qbs.Process"); +var TemporaryDir = require("qbs.TemporaryDir"); +var TextFile = require("qbs.TextFile"); +var Utilities = require("qbs.Utilities"); + +function toolchainDetails(qbs) { + var platform = qbs.targetPlatform; + var details = {}; + if (platform === "dos") { + details.imageFormat = "mz"; + details.executableSuffix = ".exe"; + } else if (platform === "os2") { + details.imageFormat = "pe"; + details.executableSuffix = ".exe"; + details.dynamicLibrarySuffix = ".dll"; + } else if (platform === "windows") { + details.imageFormat = "pe"; + details.executableSuffix = ".exe"; + details.dynamicLibrarySuffix = ".dll"; + } else if (platform === "linux") { + details.imageFormat = "elf"; + details.executableSuffix = ""; + details.dynamicLibrarySuffix = ".so"; + } + return details; +} + +function languageFlag(tag) { + if (tag === "c") + return "-xc"; + else if (tag === "cpp") + return "-xc++"; +} + +function targetFlag(platform, architecture, type) { + if (platform === "dos") { + if (architecture === "x86_16") + return "-bdos"; + else if (architecture === "x86") + return "-bdos4g"; + } else if (platform === "os2") { + if (architecture === "x86_16") + return "-bos2"; + else if (architecture === "x86") + return "-bos2v2"; + } else if (platform === "windows") { + if (architecture === "x86_16") { + if (type.includes("dynamiclibrary")) + return "-bwindows_dll"; + return "-bwindows"; + } else if (architecture === "x86") { + if (type.includes("dynamiclibrary")) + return "-bnt_dll"; + return "-bnt"; + } + } else if (platform === "linux") { + return "-blinux"; + } +} + +function guessVersion(macros) { + var version = parseInt(macros["__WATCOMC__"], 10) + || parseInt(macros["__WATCOM_CPLUSPLUS__"], 10); + if (version) { + return { major: parseInt((version - 1100) / 100), + minor: parseInt(version / 10) % 10, + patch: ((version % 10) > 0) ? parseInt(version % 10) : 0 } + } +} + +function guessEnvironment(hostOs, platform, architecture, + toolchainInstallPath, pathListSeparator) { + var toolchainRootPath = FileInfo.path(toolchainInstallPath); + if (!File.exists(toolchainRootPath)) { + throw "Unable to deduce environment due to compiler root directory: '" + + toolchainRootPath + "' does not exist"; + } + + var env = {}; + + function setVariable(key, properties, path, separator) { + var values = []; + for (var i = 0; i < properties.length; ++i) { + if (path) { + var fullpath = FileInfo.joinPaths(path, properties[i]); + values.push(FileInfo.toNativeSeparators(fullpath)); + } else { + values.push(properties[i]); + } + } + env[key] = values.join(separator); + } + + setVariable("WATCOM", [toolchainRootPath], undefined, pathListSeparator); + setVariable("EDPATH", ["eddat"], toolchainRootPath, pathListSeparator); + + if (hostOs.includes("linux")) + setVariable("PATH", ["binl64", "binl"], toolchainRootPath, pathListSeparator); + else if (hostOs.includes("windows")) + setVariable("PATH", ["binnt64", "binnt"], toolchainRootPath, pathListSeparator); + + if (platform === "linux") { + setVariable("INCLUDE", ["lh"], toolchainRootPath, pathListSeparator); + } else { + // Common for DOS, Windows, OS/2. + setVariable("WIPFC", ["wipfc"], toolchainRootPath, pathListSeparator); + setVariable("WHTMLHELP", ["binnt/help"], toolchainRootPath, pathListSeparator); + + var includes = ["h"]; + if (platform === "dos") { + // Same includes as before. + } else if (platform === "os2") { + if (architecture === "x86") + includes = includes.concat(["h/os2"]); + else if (architecture === "x86_16") + includes = includes.concat(["h/os21x"]); + } else if (platform === "windows") { + if (architecture === "x86") + includes = includes.concat(["h/nt", "h/nt/directx", "h/nt/ddk"]); + else if (architecture === "x86_16") + includes = includes.concat(["h/win"]); + } else { + throw "Unable to deduce environment for unsupported target platform: '" + + platform + "'"; + } + + setVariable("INCLUDE", includes, toolchainRootPath, pathListSeparator); + } + + return env; +} + +function dumpMacros(environment, compilerPath, platform, architecture, tag) { + // Note: The Open Watcom compiler does not support the predefined + // macros dumping. So, we do it with the following trick, where we try + // to create and compile a special temporary file and to parse the console + // output with the own magic pattern: #define <key> <value>. + + var outputDirectory = new TemporaryDir(); + var outputFilePath = FileInfo.joinPaths(outputDirectory.path(), "dump-macros.c"); + var outputFile = new TextFile(outputFilePath, TextFile.WriteOnly); + outputFile.writeLine("#define VALUE_TO_STRING(x) #x"); + outputFile.writeLine("#define VALUE(x) VALUE_TO_STRING(x)"); + outputFile.writeLine("#define VAR_NAME_VALUE(var) \"#define \"#var\" \"VALUE(var)"); + // Declare all available pre-defined macros of Watcon compiler. + var keys = [ + // Prepare the DOS target macros. + "__DOS__", "_DOS", "MSDOS", + // Prepare the OS/2 target macros. + "__OS2__", + // Prepare the QNX target macros. + "__QNX__", + // Prepare the Netware target macros. + "__NETWARE__", "__NETWARE_386__", + // Prepare the Windows target macros. + "__NT__", "__WINDOWS__", "_WINDOWS", "__WINDOWS_386__", + // Prepare the Linux and Unix target macros. + "__LINUX__", "__UNIX__", + // Prepare the 16-bit target specific macros. + "__I86__", "M_I86", "_M_I86", "_M_IX86", + // Prepare the 32-bit target specific macros. + "__386__", "M_I386", "_M_I386", "_M_IX86", + // Prepare the indicated options macros. + "_MT", "_DLL", "__FPI__", "__CHAR_SIGNED__", "__INLINE_FUNCTIONS__", + "_CPPRTTI", "_CPPUNWIND", "NO_EXT_KEYS", + // Prepare the common memory model macros. + "__FLAT__", "__SMALL__", "__MEDIUM__", + "__COMPACT__", "__LARGE__", "__HUGE__", + // Prepare the 16-bit memory model macros. + "M_I86SM", "_M_I86SM", "M_I86MM", "_M_I86MM", "M_I86CM", + "_M_I86CM", "M_I86LM", "_M_I86LM", "M_I86HM", "_M_I86HM", + // Prepare the 32-bit memory model macros. + "M_386FM", "_M_386FM", "M_386SM", "M_386MM", "_M_386MM", + "M_386CM", "_M_386CM", "M_386LM", "_M_386LM", + // Prepare the compiler macros. + "__X86__", "__cplusplus", "__WATCOMC__", "__WATCOM_CPLUSPLUS__", + "_INTEGRAL_MAX_BITS", "_PUSHPOP_SUPPORTED", "_STDCALL_SUPPORTED", + // Prepare the other macros. + "__3R__", "_based", "_cdecl", "cdecl", "_export", "_far16", "_far", "far", + "_fastcall", "_fortran", "fortran", "_huge", "huge", "_inline", "_interrupt", + "interrupt", "_loadds", "_near", "near", "_pascal", "pascal", "_saveregs", + "_segment", "_segname", "_self", "SOMDLINK", "_STDCALL_SUPPORTED", "__SW_0", + "__SW_3R", "__SW_5", "__SW_FP287", "__SW_FP2", "__SW_FP387", "__SW_FP3", + "__SW_FPI", "__SW_MF", "__SW_MS", "__SW_ZDP", "__SW_ZFP", "__SW_ZGF", + "__SW_ZGP", "_stdcall", "_syscall", "__BIG_ENDIAN" + ]; + for (var i = 0; i < keys.length; ++i) { + var key = keys[i]; + outputFile.writeLine("#if defined(" + key + ")"); + outputFile.writeLine("#pragma message (VAR_NAME_VALUE(" + key + "))"); + outputFile.writeLine("#endif"); + } + outputFile.close(); + + var process = new Process(); + process.setWorkingDirectory(outputDirectory.path()); + for (var envkey in environment) + process.setEnv(envkey, environment[envkey]); + + var target = targetFlag(platform, architecture, ["application"]); + var lang = languageFlag(tag); + var args = [ target, lang, outputFilePath ]; + + process.exec(compilerPath, args, false); + var m = Cpp.extractMacros(process.readStdOut(), /"?(#define(\s\w+){1,2})"?$/); + if (tag === "cpp" && m["__cplusplus"] === "1") + return m; + else if (tag === "c") + return m; +} + +function effectiveLinkerPath(product) { + if (product.cpp.linkerMode === "automatic") { + var compilerPath = product.cpp.compilerPath; + if (compilerPath) + return compilerPath; + console.log("Found no C-language objects, choosing system linker for " + product.name); + } + return product.cpp.linkerPath; +} + +function useCompilerDriverLinker(product) { + var linker = effectiveLinkerPath(product); + var compiler = product.cpp.compilerPath; + return linker === compiler; +} + +function escapeLinkerFlags(useCompilerDriver, linkerFlags) { + if (!linkerFlags || linkerFlags.length === 0) + return []; + + if (useCompilerDriver) { + var sep = ","; + return [["-Wl"].concat(linkerFlags).join(sep)]; + } + return linkerFlags; +} + +function assemblerFlags(project, product, input, outputs, explicitlyDependsOn) { + var args = [FileInfo.toNativeSeparators(input.filePath)]; + args.push("-fo=" + FileInfo.toNativeSeparators(outputs.obj[0].filePath)); + + args = args.concat(Cpp.collectPreincludePaths(input).map(function(path) { + return "-fi" + FileInfo.toNativeSeparators(path); + })); + + args = args.concat(Cpp.collectDefinesArguments(input, "-d")); + + var includePaths = [].concat(Cpp.collectIncludePaths(input)).concat( + Cpp.collectSystemIncludePaths(input)); + args = args.concat(includePaths.map(function(path) { + return "-i" + FileInfo.toNativeSeparators(path); + })); + + if (input.cpp.debugInformation) + args.push("-d1"); + + var warnings = input.cpp.warningLevel + if (warnings === "none") + args.push("-w0"); + else if (warnings === "all") + args.push("-wx"); + if (input.cpp.treatWarningsAsErrors) + args.push("-we"); + + args.push("-zq"); // Silent. + args = args.concat(Cpp.collectMiscAssemblerArguments(input)); + return args; +} + +function compilerFlags(project, product, input, outputs, explicitlyDependsOn) { + var args = ["-g" + (input.cpp.debugInformation ? "3" : "0")]; + + var target = targetFlag(product.qbs.targetPlatform, product.qbs.architecture, + product.type); + args.push(target); + + if (product.type.includes("application")) { + if (product.qbs.targetPlatform === "windows") { + var consoleApplication = product.consoleApplication; + args.push(consoleApplication ? "-mconsole" : "-mwindows"); + } + } else if (product.type.includes("dynamiclibrary")) { + args.push("-shared"); + } + + var optimization = input.cpp.optimization + if (optimization === "fast") + args.push("-Ot"); + else if (optimization === "small") + args.push("-Os"); + else if (optimization === "none") + args.push("-O0"); + + var warnings = input.cpp.warningLevel + if (warnings === "none") { + args.push("-w"); + } else if (warnings === "all") { + args.push("-Wall"); + args.push("-Wextra"); + } + if (input.cpp.treatWarningsAsErrors) + args.push("-Werror"); + + var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(outputs.obj[0].fileTags)); + + var langFlag = languageFlag(tag); + if (langFlag) + args.push(langFlag); + + if (tag === "cpp") { + var enableExceptions = input.cpp.enableExceptions; + if (enableExceptions) { + var ehModel = input.cpp.exceptionHandlingModel; + switch (ehModel) { + case "direct": + args.push("-feh-direct"); + break; + case "table": + args.push("-feh-table"); + break; + default: + args.push("-feh"); + break; + } + } else { + args.push("-fno-eh"); + } + + var enableRtti = input.cpp.enableRtti; + args.push(enableRtti ? "-frtti" : "-fno-rtti"); + } else if (tag === "c") { + var knownValues = ["c99", "c89"]; + var cLanguageVersion = Cpp.languageVersion(input.cpp.cLanguageVersion, knownValues, "C"); + switch (cLanguageVersion) { + case "c89": + args.push("-std=c89"); + break; + case "c99": + args.push("-std=c99"); + break; + } + } + + var preincludePaths = Cpp.collectPreincludePaths(input); + for (var i = 0; i < preincludePaths.length; ++i) + args.push(input.cpp.preincludeFlag, preincludePaths[i]); + + args = args.concat(Cpp.collectDefinesArguments(input)); + + args = args.concat(Cpp.collectIncludePaths(input).map(function(path) { + return input.cpp.includeFlag + FileInfo.toNativeSeparators(path); + })); + args = args.concat(Cpp.collectSystemIncludePaths(input).map(function(path) { + return input.cpp.systemIncludeFlag + FileInfo.toNativeSeparators(path); + })); + + args = args.concat(Cpp.collectMiscCompilerArguments(input, tag), + Cpp.collectMiscDriverArguments(input)); + + args.push("-o", FileInfo.toNativeSeparators(outputs.obj[0].filePath)); + args.push("-c", FileInfo.toNativeSeparators(input.filePath)); + + return args; +} + +function resourceCompilerFlags(project, product, input, outputs) { + var args = [input.filePath]; + args.push("-fo=" + FileInfo.toNativeSeparators(outputs.res[0].filePath)); + args = args.concat(Cpp.collectDefinesArguments(input, "-d")); + + args = args.concat(Cpp.collectIncludePaths(input).map(function(path) { + return input.cpp.includeFlag + FileInfo.toNativeSeparators(path); + })); + args = args.concat(Cpp.collectSystemIncludePaths(input).map(function(path) { + return input.cpp.includeFlag + FileInfo.toNativeSeparators(path); + })); + args.push("-q", "-ad", "-r"); + return args; +} + +function linkerFlags(project, product, inputs, outputs) { + var args = []; + var useCompilerDriver = useCompilerDriverLinker(product); + if (useCompilerDriver) { + var target = targetFlag(product.qbs.targetPlatform, product.qbs.architecture, + product.type); + args.push(target); + + if (product.type.includes("application")) { + args.push("-o", FileInfo.toNativeSeparators(outputs.application[0].filePath)); + if (product.cpp.generateLinkerMapFile) + args.push("-fm=" + FileInfo.toNativeSeparators(outputs.mem_map[0].filePath)); + } else if (product.type.includes("dynamiclibrary")) { + if (product.qbs.targetPlatform === "windows") { + args.push("-Wl, option implib=" + FileInfo.toNativeSeparators( + outputs.dynamiclibrary_import[0].filePath)); + } + args.push("-o", FileInfo.toNativeSeparators(outputs.dynamiclibrary[0].filePath)) + } + + var escapableLinkerFlags = []; + var targetLinkerFlags = product.cpp.targetLinkerFlags; + if (targetLinkerFlags) + escapableLinkerFlags = escapableLinkerFlags.concat(targetLinkerFlags); + + escapableLinkerFlags = escapableLinkerFlags.concat( + Cpp.collectMiscEscapableLinkerArguments(product)); + + var escapedLinkerFlags = escapeLinkerFlags(useCompilerDriver, escapableLinkerFlags); + if (escapedLinkerFlags) + args = args.concat(escapedLinkerFlags); + + args = args.concat(Cpp.collectLibraryPaths(product).map(function(path) { + return product.cpp.libraryPathFlag + FileInfo.toNativeSeparators(path); + })); + args = args.concat(Cpp.collectLinkerObjectPaths(inputs).map(function(path) { + return FileInfo.toNativeSeparators(path); + })); + + var libraryDependencies = Cpp.collectLibraryDependencies(product); + for (var i = 0; i < libraryDependencies.length; ++i) { + var lib = libraryDependencies[i].filePath; + if (FileInfo.isAbsolutePath(lib) || lib.startsWith('@')) + args.push(FileInfo.toNativeSeparators(lib)); + else + args.push("-Wl, libfile " + lib); + } + + var resourcePaths = Cpp.collectResourceObjectPaths(inputs).map(function(path) { + return FileInfo.toNativeSeparators(path); + }); + if (resourcePaths.length > 0) + args = args.concat("-Wl, resource " + resourcePaths.join(",")); + } + + args = args.concat(Cpp.collectMiscLinkerArguments(product), + Cpp.collectMiscDriverArguments(product)); + return args; +} + +function libraryManagerFlags(project, product, inputs, outputs) { + var args = ["-b", "-n", "-q"]; + args = args.concat(Cpp.collectLinkerObjectPaths(inputs).map(function(path) { + return "+" + FileInfo.toNativeSeparators(path); + })); + args.push("-o", FileInfo.toNativeSeparators(outputs.staticlibrary[0].filePath)); + return args; +} + +function disassemblerFlags(project, product, inputs, outputs) { + var objectPath = Cpp.relativePath(product.buildDirectory, outputs.obj[0].filePath); + var listingPath = Cpp.relativePath(product.buildDirectory, outputs.lst[0].filePath); + var args = []; + args.push(FileInfo.toNativeSeparators(objectPath)); + args.push("-l=" + FileInfo.toNativeSeparators(listingPath)); + args.push("-s", "-a"); + return args; +} + +function generateCompilerListing(project, product, inputs, outputs, input, output) { + var args = disassemblerFlags(project, product, input, outputs); + var cmd = new Command(input.cpp.disassemblerPath, args); + cmd.workingDirectory = product.buildDirectory; + cmd.silent = true; + cmd.jobPool = "watcom_job_pool"; + return cmd; +} + +function prepareAssembler(project, product, inputs, outputs, input, output, explicitlyDependsOn) { + var cmds = []; + var args = assemblerFlags(project, product, input, outputs); + var cmd = new Command(input.cpp.assemblerPath, args); + cmd.workingDirectory = product.buildDirectory; + cmd.description = "assembling " + input.fileName; + cmd.highlight = "compiler"; + cmd.jobPool = "watcom_job_pool"; + cmds.push(cmd); + if (input.cpp.generateAssemblerListingFiles) + cmds.push(generateCompilerListing(project, product, inputs, outputs, input, output)); + return cmds; +} + +function prepareCompiler(project, product, inputs, outputs, input, output, explicitlyDependsOn) { + var cmds = []; + var args = compilerFlags(project, product, input, outputs, explicitlyDependsOn); + var cmd = new Command(input.cpp.compilerPath, args); + cmd.workingDirectory = product.buildDirectory; + cmd.description = "compiling " + input.fileName; + cmd.highlight = "compiler"; + cmd.jobPool = "watcom_job_pool"; + cmds.push(cmd); + if (input.cpp.generateCompilerListingFiles) + cmds.push(generateCompilerListing(project, product, inputs, outputs, input, output)); + return cmds; +} + +function prepareResourceCompiler(project, product, inputs, outputs, input, output, + explicitlyDependsOn) { + var args = resourceCompilerFlags(project, product, input, outputs); + var cmd = new Command(input.cpp.resourceCompilerPath, args); + // Set working directory to source directory as a workaround + // to make the resources compilable by resource compiler (it is magic). + cmd.workingDirectory = product.sourceDirectory; + cmd.description = "compiling " + input.fileName; + cmd.highlight = "compiler"; + cmd.jobPool = "watcom_job_pool"; + return [cmd]; +} + +function prepareLinker(project, product, inputs, outputs, input, output) { + var primaryOutput = outputs.dynamiclibrary ? outputs.dynamiclibrary[0] + : outputs.application[0]; + var args = linkerFlags(project, product, inputs, outputs); + var linkerPath = effectiveLinkerPath(product); + var cmd = new Command(linkerPath, args); + cmd.workingDirectory = product.buildDirectory; + cmd.description = "linking " + primaryOutput.fileName; + cmd.highlight = "linker"; + cmd.jobPool = "watcom_job_pool"; + return [cmd]; +} + +function prepareLibraryManager(project, product, inputs, outputs, input, output) { + var args = libraryManagerFlags(project, product, inputs, outputs); + var cmd = new Command(product.cpp.libraryManagerPath, args); + cmd.workingDirectory = product.buildDirectory; + cmd.description = "linking " + outputs.staticlibrary[0].fileName; + cmd.highlight = "linker"; + cmd.jobPool = "watcom_job_pool"; + return [cmd]; +} diff --git a/share/qbs/modules/cpp/watcom.qbs b/share/qbs/modules/cpp/watcom.qbs new file mode 100644 index 000000000..84157b67b --- /dev/null +++ b/share/qbs/modules/cpp/watcom.qbs @@ -0,0 +1,196 @@ +/**************************************************************************** +** +** Copyright (C) 2022 Denis Shienkov <denis.shienkov@gmail.com> +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs 1.0 +import qbs.File +import qbs.FileInfo +import qbs.ModUtils +import qbs.Probes +import "cpp.js" as Cpp +import "watcom.js" as WATCOM + +CppModule { + condition: qbs.toolchain && qbs.toolchain.includes("watcom") + + Probes.BinaryProbe { + id: compilerPathProbe + condition: !toolchainInstallPath && !_skipAllChecks + names: ["owcc"] + } + + Probes.WatcomProbe { + id: watcomProbe + condition: !_skipAllChecks + compilerFilePath: compilerPath + enableDefinesByLanguage: enableCompilerDefinesByLanguage + _pathListSeparator: qbs.pathListSeparator + _toolchainInstallPath: toolchainInstallPath + _targetPlatform: qbs.targetPlatform + _targetArchitecture: qbs.architecture + } + + qbs.architecture: watcomProbe.found ? watcomProbe.architecture : original + qbs.targetPlatform: watcomProbe.found ? watcomProbe.targetPlatform : original + + compilerVersionMajor: watcomProbe.versionMajor + compilerVersionMinor: watcomProbe.versionMinor + compilerVersionPatch: watcomProbe.versionPatch + endianness: watcomProbe.endianness + + compilerDefinesByLanguage: watcomProbe.compilerDefinesByLanguage + compilerIncludePaths: watcomProbe.includePaths + + toolchainInstallPath: compilerPathProbe.found ? compilerPathProbe.path : undefined + + /* Work-around for QtCreator which expects these properties to exist. */ + property string cCompilerName: compilerName + property string cxxCompilerName: compilerName + + compilerName: "owcc" + compilerExtension + compilerPath: FileInfo.joinPaths(toolchainInstallPath, compilerName) + + assemblerName: "wasm" + compilerExtension + assemblerPath: FileInfo.joinPaths(toolchainInstallPath, assemblerName) + + linkerName: "wlink" + compilerExtension + linkerPath: FileInfo.joinPaths(toolchainInstallPath, linkerName) + + property string disassemblerName: "wdis" + compilerExtension + property string disassemblerPath: FileInfo.joinPaths(toolchainInstallPath, + disassemblerName) + property string resourceCompilerName: "wrc" + compilerExtension + property string resourceCompilerPath: FileInfo.joinPaths(toolchainInstallPath, + resourceCompilerName) + property string libraryManagerName: "wlib" + compilerExtension + property string libraryManagerPath: FileInfo.joinPaths(toolchainInstallPath, + libraryManagerName) + + runtimeLibrary: "dynamic" + + staticLibrarySuffix: ".lib" + dynamicLibrarySuffix: toolchainDetails.dynamicLibrarySuffix + executableSuffix: toolchainDetails.executableSuffix + objectSuffix: ".obj" + + imageFormat: toolchainDetails.imageFormat + + defineFlag: "-D" + includeFlag: "-I" + systemIncludeFlag: "-I" + preincludeFlag: "-include" + libraryDependencyFlag: "-l" + libraryPathFlag: "-L" + linkerScriptFlag: "" + + toolchainDetails: WATCOM.toolchainDetails(qbs) + + knownArchitectures: ["x86", "x86_16"] + + property var buildEnv: watcomProbe.environment + setupBuildEnvironment: { + for (var key in product.cpp.buildEnv) { + var v = new ModUtils.EnvironmentVariable(key, product.qbs.pathListSeparator); + v.prepend(product.cpp.buildEnv[key]); + v.set(); + } + } + + Rule { + id: assembler + inputs: ["asm"] + outputFileTags: Cpp.assemblerOutputTags(generateAssemblerListingFiles) + outputArtifacts: Cpp.assemblerOutputArtifacts(input) + prepare: WATCOM.prepareAssembler.apply(WATCOM, arguments) + } + + FileTagger { + patterns: ["*.asm"] + fileTags: ["asm"] + } + + Rule { + id: compiler + inputs: ["cpp", "c"] + auxiliaryInputs: ["hpp"] + outputFileTags: Cpp.compilerOutputTags(generateCompilerListingFiles) + outputArtifacts: Cpp.compilerOutputArtifacts(input) + prepare: WATCOM.prepareCompiler.apply(WATCOM, arguments) + } + + Rule { + id: rccCompiler + inputs: ["rc"] + auxiliaryInputs: ["hpp"] + outputFileTags: Cpp.resourceCompilerOutputTags() + outputArtifacts: Cpp.resourceCompilerOutputArtifacts(input) + prepare: WATCOM.prepareResourceCompiler.apply(WATCOM, arguments) + } + + FileTagger { + patterns: ["*.rc"] + fileTags: ["rc"] + } + + Rule { + id: applicationLinker + multiplex: true + inputs: ["obj", "res", "linkerscript"] + inputsFromDependencies: ["staticlibrary", "dynamiclibrary_import"] + outputFileTags: Cpp.applicationLinkerOutputTags(generateLinkerMapFile) + outputArtifacts: Cpp.applicationLinkerOutputArtifacts(product) + prepare: WATCOM.prepareLinker.apply(WATCOM, arguments) + } + + Rule { + id: dynamicLibraryLinker + condition: qbs.targetOS.includes("windows") + multiplex: true + inputs: ["obj", "res"] + inputsFromDependencies: ["staticlibrary", "dynamiclibrary_import"] + outputFileTags: Cpp.dynamicLibraryLinkerOutputTags(); + outputArtifacts: Cpp.dynamicLibraryLinkerOutputArtifacts(product) + prepare: WATCOM.prepareLinker.apply(WATCOM, arguments) + } + + Rule { + id: libraryManager + multiplex: true + inputs: ["obj"] + inputsFromDependencies: ["staticlibrary", "dynamiclibrary_import"] + outputFileTags: Cpp.staticLibraryLinkerOutputTags() + outputArtifacts: Cpp.staticLibraryLinkerOutputArtifacts(product) + prepare: WATCOM.prepareLibraryManager.apply(WATCOM, arguments) + } + + JobLimit { + jobPool: "watcom_job_pool" + jobCount: 1 + } +} diff --git a/share/qbs/modules/cpp/windows-clang-cl.qbs b/share/qbs/modules/cpp/windows-clang-cl.qbs index 1b2833060..9aa9d7395 100644 --- a/share/qbs/modules/cpp/windows-clang-cl.qbs +++ b/share/qbs/modules/cpp/windows-clang-cl.qbs @@ -28,19 +28,19 @@ ** ****************************************************************************/ -import qbs +import qbs.Host import qbs.ModUtils import qbs.Probes import qbs.FileInfo import 'windows-msvc-base.qbs' as MsvcBaseModule MsvcBaseModule { - condition: qbs.hostOS.contains('windows') && - qbs.targetOS.contains('windows') && - qbs.toolchain && qbs.toolchain.contains('clang-cl') + condition: Host.os().includes('windows') && + qbs.targetOS.includes('windows') && + qbs.toolchain && qbs.toolchain.includes('clang-cl') priority: 100 - Probes.BinaryProbe { + Probes.ClangClBinaryProbe { id: clangPathProbe condition: !toolchainInstallPath && !_skipAllChecks names: ["clang-cl"] @@ -52,9 +52,12 @@ MsvcBaseModule { compilerFilePath: compilerPath vcvarsallFilePath: vcvarsallPath enableDefinesByLanguage: enableCompilerDefinesByLanguage - architecture: qbs.architecture + preferredArchitecture: qbs.architecture + winSdkVersion: windowsSdkVersion } + qbs.architecture: clangClProbe.found ? clangClProbe.architecture : original + compilerVersionMajor: clangClProbe.versionMajor compilerVersionMinor: clangClProbe.versionMinor compilerVersionPatch: clangClProbe.versionPatch @@ -65,20 +68,34 @@ MsvcBaseModule { : undefined buildEnv: clangClProbe.buildEnv - property string vcvarsallPath + property string linkerVariant + PropertyOptions { + name: "linkerVariant" + allowedValues: ["lld", "link"] + description: "Allows to specify the linker variant. Maps to clang-cl's -fuse-ld option." + } + Properties { + condition: linkerVariant + driverLinkerFlags: "-fuse-ld=" + linkerVariant + } + + property string vcvarsallPath : clangPathProbe.found ? clangPathProbe.vcvarsallPath : undefined compilerName: "clang-cl.exe" linkerName: "lld-link.exe" linkerPath: FileInfo.joinPaths(toolchainInstallPath, linkerName) + systemIncludeFlag: "/imsvc" + validateFunc: { + var baseFunc = base; return function() { if (_skipAllChecks) return; var validator = new ModUtils.PropertyValidator("cpp"); validator.setRequiredProperty("vcvarsallPath", vcvarsallPath); validator.validate(); - base(); + baseFunc(); } } } diff --git a/share/qbs/modules/cpp/windows-clang-mingw.qbs b/share/qbs/modules/cpp/windows-clang-mingw.qbs index 8389dbf2e..cfe6f2745 100644 --- a/share/qbs/modules/cpp/windows-clang-mingw.qbs +++ b/share/qbs/modules/cpp/windows-clang-mingw.qbs @@ -30,6 +30,7 @@ import qbs.File import qbs.FileInfo +import qbs.Host import qbs.ModUtils import qbs.Utilities import "msvc.js" as MSVC @@ -37,8 +38,8 @@ import "msvc.js" as MSVC import "setuprunenv.js" as SetupRunEnv MingwBaseModule { - condition: qbs.targetOS.contains("windows") && - qbs.toolchain && qbs.toolchain.contains("clang") + condition: qbs.targetOS.includes("windows") && + qbs.toolchain && qbs.toolchain.includes("clang") priority: 0 // llvm-as and llvm-objopy are not shipped with the official binaries on Windows at the @@ -68,16 +69,16 @@ MingwBaseModule { "llvm-rc" + compilerExtension) setupBuildEnvironment: { - if (product.qbs.hostOS.contains("windows") && product.qbs.sysroot) { - var v = new ModUtils.EnvironmentVariable("PATH", product.qbs.pathListSeparator, true); + if (Host.os().includes("windows") && product.qbs.sysroot) { + var v = new ModUtils.EnvironmentVariable("PATH", FileInfo.pathListSeparator(), true); v.prepend(FileInfo.joinPaths(product.qbs.sysroot, "bin")); v.set(); } } setupRunEnvironment: { - if (product.qbs.hostOS.contains("windows") && product.qbs.sysroot) { - var v = new ModUtils.EnvironmentVariable("PATH", product.qbs.pathListSeparator, true); + if (Host.os().includes("windows") && product.qbs.sysroot) { + var v = new ModUtils.EnvironmentVariable("PATH", FileInfo.pathListSeparator(), true); v.prepend(FileInfo.joinPaths(product.qbs.sysroot, "bin")); v.set(); SetupRunEnv.setupRunEnvironment(product, config); @@ -89,10 +90,11 @@ MingwBaseModule { auxiliaryInputs: ["hpp"] Artifact { - filePath: Utilities.getHash(input.baseDir) + "/" + input.completeBaseName + ".res" + filePath: FileInfo.joinPaths(Utilities.getHash(input.baseDir), + input.completeBaseName + ".res") fileTags: ["obj"] } - prepare: MSVC.createRcCommand(product.cpp.rcFilePath, input, output); + prepare: MSVC.createRcCommand(product.cpp.rcFilePath, input, output) } } diff --git a/share/qbs/modules/cpp/windows-mingw.qbs b/share/qbs/modules/cpp/windows-mingw.qbs index ffed76cdd..9ba4258c5 100644 --- a/share/qbs/modules/cpp/windows-mingw.qbs +++ b/share/qbs/modules/cpp/windows-mingw.qbs @@ -33,11 +33,12 @@ import qbs.FileInfo import qbs.ModUtils import qbs.Utilities +import 'cpp.js' as Cpp import "setuprunenv.js" as SetupRunEnv MingwBaseModule { - condition: qbs.targetOS.contains("windows") && - qbs.toolchain && qbs.toolchain.contains("mingw") + condition: qbs.targetOS.includes("windows") && + qbs.toolchain && qbs.toolchain.includes("mingw") priority: 0 probeEnv: buildEnv @@ -51,13 +52,13 @@ MingwBaseModule { } setupBuildEnvironment: { - var v = new ModUtils.EnvironmentVariable("PATH", product.qbs.pathListSeparator, true); + var v = new ModUtils.EnvironmentVariable("PATH", FileInfo.pathListSeparator(), true); v.prepend(product.cpp.toolchainInstallPath); v.set(); } setupRunEnvironment: { - var v = new ModUtils.EnvironmentVariable("PATH", product.qbs.pathListSeparator, true); + var v = new ModUtils.EnvironmentVariable("PATH", FileInfo.pathListSeparator(), true); v.prepend(product.cpp.toolchainInstallPath); v.set(); SetupRunEnv.setupRunEnvironment(product, config); @@ -66,12 +67,8 @@ MingwBaseModule { Rule { inputs: ["rc"] auxiliaryInputs: ["hpp"] - - Artifact { - filePath: Utilities.getHash(input.baseDir) + "/" + input.completeBaseName + "_res.o" - fileTags: ["obj"] - } - + outputFileTags: Cpp.resourceCompilerOutputTags() + outputArtifacts: Cpp.resourceCompilerOutputArtifacts(input) prepare: { var platformDefines = input.cpp.platformDefines; var defines = input.cpp.defines; @@ -96,6 +93,7 @@ MingwBaseModule { args.push(systemIncludePaths[i]); } + args.push("-O", "coff"); // Set COFF format explicitly. args = args.concat(['-i', input.filePath, '-o', output.filePath]); var cmd = new Command(product.cpp.windresPath, args); cmd.description = 'compiling ' + input.fileName; diff --git a/share/qbs/modules/cpp/windows-msvc-base.qbs b/share/qbs/modules/cpp/windows-msvc-base.qbs index aa1c8256c..9613800e5 100644 --- a/share/qbs/modules/cpp/windows-msvc-base.qbs +++ b/share/qbs/modules/cpp/windows-msvc-base.qbs @@ -35,11 +35,14 @@ import qbs.PathTools import qbs.Probes import qbs.Utilities import qbs.WindowsUtils +import 'cpp.js' as Cpp import 'msvc.js' as MSVC CppModule { condition: false + Depends { name: "codesign" } + windowsApiCharacterSet: "unicode" platformDefines: { var defines = base.concat(WindowsUtils.characterSetDefines(windowsApiCharacterSet)) @@ -81,7 +84,6 @@ CppModule { separateDebugInformation: true property bool generateManifestFile: true - property string toolchainInstallPath architecture: qbs.architecture endianness: "little" @@ -89,9 +91,11 @@ CppModule { dynamicLibrarySuffix: ".dll" executableSuffix: ".exe" debugInfoSuffix: ".pdb" + objectSuffix: ".obj" + precompiledHeaderSuffix: ".pch" imageFormat: "pe" Properties { - condition: product.multiplexByQbsProperties.contains("buildVariants") + condition: product.multiplexByQbsProperties.includes("buildVariants") && qbs.buildVariants && qbs.buildVariants.length > 1 && qbs.buildVariant !== "release" && product.type.containsAny(["staticlibrary", "dynamiclibrary"]) @@ -100,6 +104,9 @@ CppModule { property var buildEnv + readonly property bool shouldSignArtifacts: codesign.enableCodeSigning + property bool enableCxxLanguageMacro: false + setupBuildEnvironment: { for (var key in product.cpp.buildEnv) { var v = new ModUtils.EnvironmentVariable(key, ';'); @@ -108,21 +115,21 @@ CppModule { } } + property string windowsSdkVersion + + defineFlag: "/D" + includeFlag: "/I" + systemIncludeFlag: "/external:I" + preincludeFlag: "/FI" + libraryPathFlag: "/LIBPATH:" + Rule { condition: useCPrecompiledHeader inputs: ["c_pch_src"] auxiliaryInputs: ["hpp"] - Artifact { - fileTags: ['obj'] - filePath: Utilities.getHash(input.completeBaseName) + '_c.obj' - } - Artifact { - fileTags: ['c_pch'] - filePath: product.name + '_c.pch' - } - prepare: { - return MSVC.prepareCompiler.apply(MSVC, arguments); - } + outputFileTags: Cpp.precompiledHeaderOutputTags("c", true) + outputArtifacts: Cpp.precompiledHeaderOutputArtifacts(input, product, "c", true) + prepare: MSVC.prepareCompiler.apply(MSVC, arguments) } Rule { @@ -130,17 +137,9 @@ CppModule { inputs: ["cpp_pch_src"] explicitlyDependsOn: ["c_pch"] // to prevent vc--0.pdb conflict auxiliaryInputs: ["hpp"] - Artifact { - fileTags: ['obj'] - filePath: Utilities.getHash(input.completeBaseName) + '_cpp.obj' - } - Artifact { - fileTags: ['cpp_pch'] - filePath: product.name + '_cpp.pch' - } - prepare: { - return MSVC.prepareCompiler.apply(MSVC, arguments); - } + outputFileTags: Cpp.precompiledHeaderOutputTags("cpp", true) + outputArtifacts: Cpp.precompiledHeaderOutputArtifacts(input, product, "cpp", true) + prepare: MSVC.prepareCompiler.apply(MSVC, arguments) } Rule { @@ -148,21 +147,9 @@ CppModule { inputs: ["cpp", "c"] auxiliaryInputs: ["hpp"] explicitlyDependsOn: ["c_pch", "cpp_pch"] - - outputFileTags: ["obj", "intermediate_obj"] - outputArtifacts: { - var tags = input.fileTags.contains("cpp_intermediate_object") - ? ["intermediate_obj"] - : ["obj"]; - return [{ - fileTags: tags, - filePath: Utilities.getHash(input.baseDir) + "/" + input.fileName + ".obj" - }]; - } - - prepare: { - return MSVC.prepareCompiler.apply(MSVC, arguments); - } + outputFileTags: Cpp.compilerOutputTags(generateCompilerListingFiles) + outputArtifacts: Cpp.compilerOutputArtifacts(input) + prepare: MSVC.prepareCompiler.apply(MSVC, arguments) } FileTagger { @@ -170,16 +157,29 @@ CppModule { fileTags: ["native.pe.manifest"] } + FileTagger { + patterns: ["*.def"] + fileTags: ["def"] + } + Rule { name: "applicationLinker" multiplex: true - inputs: ['obj', 'native.pe.manifest'] + inputs: ['obj', 'res', 'native.pe.manifest', 'def'] inputsFromDependencies: ['staticlibrary', 'dynamiclibrary_import', "debuginfo_app"] - outputFileTags: ["application", "debuginfo_app", "mem_map"] + outputFileTags: { + var tags = ["application", "debuginfo_app"]; + if (generateLinkerMapFile) + tags.push("mem_map"); + if (shouldSignArtifacts) + tags.push("codesign.signed_artifact"); + return tags; + } outputArtifacts: { var app = { - fileTags: ["application"], + fileTags: ["application"].concat( + product.cpp.shouldSignArtifacts ? ["codesign.signed_artifact"] : []), filePath: FileInfo.joinPaths( product.destinationDirectory, PathTools.applicationFilePath(product)) @@ -197,33 +197,39 @@ CppModule { fileTags: ["mem_map"], filePath: FileInfo.joinPaths( product.destinationDirectory, - product.targetName + ".map") + product.targetName + product.cpp.linkerMapSuffix) }); } return artifacts; } - prepare: { - return MSVC.prepareLinker.apply(MSVC, arguments); - } + prepare: MSVC.prepareLinker.apply(MSVC, arguments) } Rule { name: "dynamicLibraryLinker" multiplex: true - inputs: ['obj', 'native.pe.manifest'] + inputs: ['obj', 'res', 'native.pe.manifest', 'def'] inputsFromDependencies: ['staticlibrary', 'dynamiclibrary_import', "debuginfo_dll"] - outputFileTags: ["dynamiclibrary", "dynamiclibrary_import", "debuginfo_dll"] + outputFileTags: { + var tags = ["dynamiclibrary", "dynamiclibrary_import", "debuginfo_dll"]; + if (shouldSignArtifacts) + tags.push("codesign.signed_artifact"); + return tags; + } outputArtifacts: { var artifacts = [ { - fileTags: ["dynamiclibrary"], - filePath: product.destinationDirectory + "/" + PathTools.dynamicLibraryFilePath(product) + fileTags: ["dynamiclibrary"].concat( + product.cpp.shouldSignArtifacts ? ["codesign.signed_artifact"] : []), + filePath: FileInfo.joinPaths(product.destinationDirectory, + PathTools.dynamicLibraryFilePath(product)) }, { fileTags: ["dynamiclibrary_import"], - filePath: product.destinationDirectory + "/" + PathTools.importLibraryFilePath(product), + filePath: FileInfo.joinPaths(product.destinationDirectory, + PathTools.importLibraryFilePath(product)), alwaysUpdated: false } ]; @@ -238,15 +244,13 @@ CppModule { return artifacts; } - prepare: { - return MSVC.prepareLinker.apply(MSVC, arguments); - } + prepare: MSVC.prepareLinker.apply(MSVC, arguments) } Rule { name: "libtool" multiplex: true - inputs: ["obj"] + inputs: ["obj", "res"] inputsFromDependencies: ["staticlibrary", "dynamiclibrary_import"] outputFileTags: ["staticlibrary", "debuginfo_cl"] outputArtifacts: { @@ -274,6 +278,10 @@ CppModule { var fileName = FileInfo.toWindowsSeparators(inputs.obj[i].filePath) args.push(fileName) } + for (var i in inputs.res) { + var fileName = FileInfo.toWindowsSeparators(inputs.res[i].filePath) + args.push(fileName) + } var cmd = new Command("lib.exe", args); cmd.description = 'creating ' + lib.fileName; cmd.highlight = 'linker'; @@ -292,12 +300,8 @@ CppModule { Rule { inputs: ["rc"] auxiliaryInputs: ["hpp"] - - Artifact { - filePath: Utilities.getHash(input.baseDir) + "/" + input.completeBaseName + ".res" - fileTags: ["obj"] - } - + outputFileTags: Cpp.resourceCompilerOutputTags() + outputArtifacts: Cpp.resourceCompilerOutputArtifacts(input) prepare: { // From MSVC 2010 on, the logo can be suppressed. var logo = product.cpp.compilerVersionMajor >= 16 @@ -313,18 +317,14 @@ CppModule { Rule { inputs: ["asm"] - Artifact { - filePath: Utilities.getHash(input.baseDir) + "/" + input.completeBaseName + ".obj" - fileTags: ["obj"] - } + outputFileTags: Cpp.assemblerOutputTags(false) + outputArtifacts: Cpp.assemblerOutputArtifacts(input) prepare: { - var args = ["/nologo", "/c", - "/Fo" + FileInfo.toWindowsSeparators(output.filePath), - FileInfo.toWindowsSeparators(input.filePath)]; + var args = ["/nologo", "/c", "/Fo" + FileInfo.toWindowsSeparators(output.filePath)]; if (product.cpp.debugInformation) args.push("/Zi"); - args = args.concat(ModUtils.moduleProperty(input, 'platformFlags', 'asm'), - ModUtils.moduleProperty(input, 'flags', 'asm')); + args = args.concat(Cpp.collectMiscAssemblerArguments(input, "asm")); + args.push(FileInfo.toWindowsSeparators(input.filePath)); var cmd = new Command(product.cpp.assemblerPath, args); cmd.description = "assembling " + input.fileName; cmd.jobPool = "assembler"; diff --git a/share/qbs/modules/cpp/windows-msvc.qbs b/share/qbs/modules/cpp/windows-msvc.qbs index 59032d28a..c30cec239 100644 --- a/share/qbs/modules/cpp/windows-msvc.qbs +++ b/share/qbs/modules/cpp/windows-msvc.qbs @@ -29,17 +29,19 @@ ** ****************************************************************************/ +import qbs.Host import qbs.Probes import "windows-msvc-base.qbs" as MsvcBaseModule MsvcBaseModule { - condition: qbs.hostOS.contains('windows') && - qbs.targetOS.contains('windows') && - qbs.toolchain && qbs.toolchain.contains('msvc') + condition: Host.os().includes('windows') && + qbs.targetOS.includes('windows') && + qbs.toolchain && qbs.toolchain.includes('msvc') priority: 50 - Probes.BinaryProbe { + Probes.ClBinaryProbe { id: compilerPathProbe + preferredArchitecture: qbs.architecture condition: !toolchainInstallPath && !_skipAllChecks names: ["cl"] } @@ -50,6 +52,7 @@ MsvcBaseModule { compilerFilePath: compilerPath enableDefinesByLanguage: enableCompilerDefinesByLanguage preferredArchitecture: qbs.architecture + winSdkVersion: windowsSdkVersion } qbs.architecture: msvcProbe.found ? msvcProbe.architecture : original @@ -63,4 +66,6 @@ MsvcBaseModule { toolchainInstallPath: compilerPathProbe.found ? compilerPathProbe.path : undefined buildEnv: msvcProbe.buildEnv + + enableCxxLanguageMacro: true } |