import qbs 1.0 import qbs.File import qbs.Process import qbs.TextFile import qbs.FileInfo import '../utils.js' as ModUtils import "bundle-tools.js" as BundleTools import "darwin-tools.js" as DarwinTools UnixGCC { condition: false compilerDefines: ["__GNUC__", "__APPLE__"] dynamicLibrarySuffix: ".dylib" setupBuildEnvironment: { var v = new ModUtils.EnvironmentVariable("PATH", ":", false); if (platformPath) { v.prepend(platformPath + "/Developer/usr/bin"); var platformPathL = platformPath.split("/"); platformPathL.pop(); platformPathL.pop(); var devPath = platformPathL.join("/") v.prepend(devPath + "/usr/bin"); v.set(); } for (var key in buildEnv) { v = new ModUtils.EnvironmentVariable(key); v.value = buildEnv[key]; v.set(); } } // private properties readonly property var buildEnv: { var env = { "EXECUTABLE_NAME": product.targetName, "LANG": "en_US.US-ASCII", "PRODUCT_NAME": product.name } if (minimumIosVersion) env["IPHONEOS_DEPLOYMENT_TARGET"] = minimumIosVersion; if (minimumOsxVersion) env["MACOSX_DEPLOYMENT_TARGET"] = minimumOsxVersion; return env; } readonly property var defaultInfoPlist: { var dict = { CFBundleDevelopmentRegion: "en", // default localization CFBundleDisplayName: product.targetName, // localizable CFBundleExecutable: product.targetName, CFBundleIdentifier: "org.example." + DarwinTools.rfc1034(product.targetName), CFBundleInfoDictionaryVersion: "6.0", CFBundleName: product.targetName, // short display name of the bundle, localizable CFBundlePackageType: BundleTools.packageType(product), CFBundleShortVersionString: product.version || "1.0", // "release" version number, localizable CFBundleSignature: "????", // legacy creator code in Mac OS Classic, can be ignored CFBundleVersion: product.version || "1.0.0" // build version number, must be 3 octets }; if (product.type.contains("applicationbundle")) dict["CFBundleIconFile"] = product.targetName; if (qbs.targetOS.contains("osx") && minimumOsxVersion) dict["LSMinimumSystemVersion"] = minimumOsxVersion; if (qbs.targetOS.contains("ios")) { dict["LSRequiresIPhoneOS"] = true; // architectures supported, to support iPhone 3G for example one has to add // armv6 to the list and also compile for it (with Xcode 4.4.1 or earlier) dict["UIRequiredDeviceCapabilities"] = [ "armv7" ]; var orientations = [ "UIInterfaceOrientationPortrait", "UIInterfaceOrientationLandscapeLeft", "UIInterfaceOrientationLandscapeRight" ]; dict["UISupportedInterfaceOrientations"] = orientations; orientations.splice(1, 0, "UIInterfaceOrientationPortraitUpsideDown"); dict["UISupportedInterfaceOrientations~ipad"] = orientations; } return dict; } readonly property path platformInfoPlist: platformPath ? [platformPath, "Info.plist"].join("/") : undefined readonly property path sdkSettingsPlist: sysroot ? [sysroot, "SDKSettings.plist"].join("/") : undefined readonly property path toolchainInfoPlist: toolchainInstallPath ? [toolchainInstallPath, "../../ToolchainInfo.plist"].join("/") : undefined Rule { multiplex: true inputs: ["infoplist"] Artifact { fileName: product.destinationDirectory + "/" + BundleTools.pkgInfoPath(product) fileTags: ["pkginfo"] } prepare: { var cmd = new JavaScriptCommand(); cmd.description = "generating PkgInfo"; cmd.highlight = "codegen"; cmd.sourceCode = function() { var infoPlist = BundleTools.infoPlistContents(inputs.infoplist[0].fileName); var pkgType = infoPlist['CFBundlePackageType']; if (!pkgType) throw("CFBundlePackageType not found in Info.plist; this should not happen"); var pkgSign = infoPlist['CFBundleSignature']; if (!pkgSign) throw("CFBundleSignature not found in Info.plist; this should not happen"); var pkginfo = new TextFile(outputs.pkginfo[0].fileName, TextFile.WriteOnly); pkginfo.write(pkgType + pkgSign); pkginfo.close(); } return cmd; } } Rule { multiplex: true inputs: ["qbs"] Artifact { fileName: product.destinationDirectory + "/" + BundleTools.infoPlistPath(product) fileTags: ["infoplist"] } prepare: { var cmd = new JavaScriptCommand(); cmd.description = "generating Info.plist"; cmd.highlight = "codegen"; cmd.infoPlistFile = ModUtils.moduleProperty(product, "infoPlistFile"); cmd.infoPlist = ModUtils.moduleProperty(product, "infoPlist") || {}; cmd.processInfoPlist = ModUtils.moduleProperty(product, "processInfoPlist"); cmd.infoPlistFormat = ModUtils.moduleProperty(product, "infoPlistFormat"); cmd.platformPath = product.moduleProperty("cpp", "platformPath"); cmd.toolchainInstallPath = product.moduleProperty("cpp", "toolchainInstallPath"); cmd.sysroot = product.moduleProperty("qbs", "sysroot"); cmd.buildEnv = product.moduleProperty("cpp", "buildEnv"); cmd.platformInfoPlist = product.moduleProperty("cpp", "platformInfoPlist"); cmd.sdkSettingsPlist = product.moduleProperty("cpp", "sdkSettingsPlist"); cmd.toolchainInfoPlist = product.moduleProperty("cpp", "toolchainInfoPlist"); cmd.sourceCode = function() { var process, key; // Contains the combination of default, file, and in-source keys and values // Start out with the contents of this file as the "base", if given var aggregatePlist = BundleTools.infoPlistContents(infoPlistFile) || {}; // Add local key-value pairs (overrides equivalent keys specified in the file if // one was given) for (key in infoPlist) { if (infoPlist.hasOwnProperty(key)) aggregatePlist[key] = infoPlist[key]; } // Do some postprocessing if desired if (processInfoPlist) { // Add default values to the aggregate plist if the corresponding keys // for those values are not already present var defaultValues = ModUtils.moduleProperty(product, "defaultInfoPlist"); for (key in defaultValues) { if (defaultValues.hasOwnProperty(key) && !(key in aggregatePlist)) aggregatePlist[key] = defaultValues[key]; } // Add keys from platform's Info.plist if not already present var platformInfo = {}; if (platformPath) { if (File.exists(platformInfoPlist)) { process = new Process(); process.exec("plutil", ["-convert", "json", "-o", "-", platformInfoPlist], true); platformInfo = JSON.parse(process.readStdOut()); process.close(); var additionalProps = platformInfo["AdditionalInfo"]; for (key in additionalProps) { if (additionalProps.hasOwnProperty(key) && !(key in aggregatePlist)) // override infoPlist? aggregatePlist[key] = defaultValues[key]; } props = platformInfo['OverrideProperties']; for (key in props) { aggregatePlist[key] = props[key]; } if (product.moduleProperty("qbs", "targetOS").contains("ios")) { key = "UIDeviceFamily"; if (key in platformInfo && !(key in aggregatePlist)) aggregatePlist[key] = platformInfo[key]; } } else { print("warning: platform path given but no platform Info.plist found"); } } else { print("no platform path specified"); } var sdkSettings = {}; if (sysroot) { if (File.exists(sdkSettingsPlist)) { process = new Process(); process.exec("plutil", ["-convert", "json", "-o", "-", sdkSettingsPlist], true); sdkSettings = JSON.parse(process.readStdOut()); process.close(); } else { print("warning: sysroot (SDK path) given but no SDKSettings.plist found"); } } else { print("no sysroot (SDK path) specified"); } var toolchainInfo = {}; if (toolchainInstallPath && File.exists(toolchainInfoPlist)) { process = new Process(); process.exec("plutil", ["-convert", "json", "-o", "-", toolchainInfoPlist], true); toolchainInfo = JSON.parse(process.readStdOut()); process.close(); } else { print("could not find a ToolchainInfo.plist near the toolchain install path"); } process = new Process(); process.exec("sw_vers", ["-buildVersion"], true); var osBuildVersion = process.readStdOut().trim(); aggregatePlist["BuildMachineOSBuild"] = osBuildVersion; process.close(); // setup env env = { "SDK_NAME": sdkSettings["CanonicalName"], "XCODE_VERSION_ACTUAL": toolchainInfo["DTXcode"], "SDK_PRODUCT_BUILD_VERSION": toolchainInfo["DTPlatformBuild"], "GCC_VERSION": platformInfo["DTCompiler"], "XCODE_PRODUCT_BUILD_VERSION": platformInfo["DTPlatformBuild"], "PLATFORM_PRODUCT_BUILD_VERSION": platformInfo["ProductBuildVersion"], } env["MAC_OS_X_PRODUCT_BUILD_VERSION"] = osBuildVersion; for (key in buildEnv) env[key] = buildEnv[key]; DarwinTools.doRepl(aggregatePlist, env, true); } // Write the plist contents as JSON var infoplist = new TextFile(outputs.infoplist[0].fileName, TextFile.WriteOnly); infoplist.write(JSON.stringify(aggregatePlist)); infoplist.close(); if (infoPlistFormat === "same-as-input" && infoPlistFile) infoPlistFormat = BundleTools.infoPlistFormat(infoPlistFile); var validFormats = [ "xml1", "binary1", "json" ]; if (!validFormats.contains(infoPlistFormat)) throw("Invalid Info.plist format " + infoPlistFormat + ". " + "Must be in [xml1, binary1, json]."); // Convert the written file to the format appropriate for the current platform process = new Process(); process.exec("plutil", ["-convert", infoPlistFormat, outputs.infoplist[0].fileName], true); process.close(); } return cmd; } } Rule { condition: product.moduleProperty("cpp", "buildDsym") inputs: ["application"] Artifact { fileName: product.destinationDirectory + "/" + PathTools.dwarfDsymFileName() fileTags: ["dsym"] } prepare: { var cmd = new Command("dsymutil", ["--out=" + outputs.dsym[0].fileName, input.fileName]); cmd.description = "generating dsym"; cmd.highlight = "codegen"; return cmd; } } Rule { multiplex: true inputs: ["application", "infoplist", "pkginfo", "dsym", "compiled_nib", "resourcerules", "ipa"] Artifact { fileName: product.destinationDirectory + "/" + BundleTools.wrapperName(product) fileTags: ["applicationbundle"] } prepare: { // This command is intentionally empty; it just lets the user know a bundle has been made var cmd = new JavaScriptCommand(); cmd.description = "creating app bundle"; cmd.highlight = "codegen"; return cmd; } } Rule { multiplex: true inputs: ["dynamiclibrary", "infoplist", "pkginfo", "dsym", "compiled_nib"] Artifact { fileName: product.destinationDirectory + "/" + BundleTools.wrapperName(product) fileTags: ["frameworkbundle"] } prepare: { var commands = []; var cmd = new Command("ln", ["-sf", BundleTools.frameworkVersion(product), "Current"]); cmd.workingDirectory = output.fileName + "/Versions"; cmd.description = "creating framework " + product.targetName; cmd.highlight = "codegen"; commands.push(cmd); cmd = new Command("ln", ["-sf", "Versions/Current/Headers", "Headers"]); cmd.workingDirectory = output.fileName; commands.push(cmd); cmd = new Command("ln", ["-sf", "Versions/Current/Resources", "Resources"]); cmd.workingDirectory = output.fileName; commands.push(cmd); cmd = new Command("ln", ["-sf", "Versions/Current/" + product.targetName, product.targetName]); cmd.workingDirectory = output.fileName; commands.push(cmd); return commands; } } }