From a08c71b90d7dc88fd4175bb64797a43254cc14d7 Mon Sep 17 00:00:00 2001 From: Jake Petroules Date: Wed, 11 Nov 2015 19:23:37 -0800 Subject: Move global auxiliary functions into a new Utilities extension. Change-Id: If0f12b9d28b83080f4435d91b55d70d5cff5d42e Reviewed-by: Christian Kandeler --- doc/reference/items/rule.qdoc | 4 +- .../jsextensions/jsextension-utilities.qdoc | 59 +++++++++ .../jsextensions/jsextensions-general.qdoc | 17 --- share/qbs/imports/qbs/DarwinTools/darwin-tools.js | 3 +- share/qbs/imports/qbs/base/AutotestRunner.qbs | 3 +- share/qbs/modules/Android/sdk/sdk.qbs | 3 +- share/qbs/modules/bundle/BundleModule.qbs | 3 +- share/qbs/modules/cli/windows-dotnet.qbs | 5 +- share/qbs/modules/cpp/CppModule.qbs | 5 +- share/qbs/modules/cpp/GenericGCC.qbs | 5 +- share/qbs/modules/cpp/gcc.js | 3 +- share/qbs/modules/cpp/windows-mingw.qbs | 3 +- share/qbs/modules/cpp/windows-msvc.qbs | 11 +- share/qbs/modules/java/utils.js | 9 +- share/qbs/modules/nsis/NSISModule.qbs | 13 +- share/qbs/modules/qbs/common.qbs | 19 +-- share/qbs/modules/wix/WiXModule.qbs | 13 +- src/lib/corelib/corelib.qbs | 2 + src/lib/corelib/jsextensions/jsextensions.cpp | 2 + src/lib/corelib/jsextensions/jsextensions.pri | 6 +- .../corelib/jsextensions/utilitiesextension.cpp | 140 +++++++++++++++++++++ src/lib/corelib/jsextensions/utilitiesextension.h | 44 +++++++ src/lib/corelib/language/builtinvalue.h | 5 +- src/lib/corelib/language/evaluatorscriptclass.cpp | 64 ---------- src/lib/corelib/language/evaluatorscriptclass.h | 8 -- src/lib/corelib/language/moduleloader.cpp | 6 - src/lib/corelib/language/scriptengine.cpp | 8 -- .../language/testdata/canonicalArchitecture.qbs | 5 +- .../corelib/language/testdata/getNativeSetting.qbs | 9 +- .../language/testdata/rfc1034identifier.qbs | 3 +- 30 files changed, 322 insertions(+), 158 deletions(-) create mode 100644 doc/reference/jsextensions/jsextension-utilities.qdoc create mode 100644 src/lib/corelib/jsextensions/utilitiesextension.cpp create mode 100644 src/lib/corelib/jsextensions/utilitiesextension.h diff --git a/doc/reference/items/rule.qdoc b/doc/reference/items/rule.qdoc index faf41ba4b..1c8690a06 100644 --- a/doc/reference/items/rule.qdoc +++ b/doc/reference/items/rule.qdoc @@ -45,6 +45,8 @@ As a real-world example of a simplex rule, here is a simplified version of \QBS' rule for transforming C++ sources into object files using gcc: \code + import qbs.ModUtils + import qbs.Utilities Rule { id: compiler inputs: ['cpp'] @@ -52,7 +54,7 @@ Artifact { fileTags: ['obj'] - filePath: '.obj/' + qbs.getHash(input.baseDir) + '/' + input.fileName + '.o' + filePath: '.obj/' + Utilities.getHash(input.baseDir) + '/' + input.fileName + '.o' } prepare: { diff --git a/doc/reference/jsextensions/jsextension-utilities.qdoc b/doc/reference/jsextensions/jsextension-utilities.qdoc new file mode 100644 index 000000000..3f9b8c40c --- /dev/null +++ b/doc/reference/jsextensions/jsextension-utilities.qdoc @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of the Qt Build Suite. +** +** 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. +** +****************************************************************************/ + +/*! + \contentspage index.html + \page jsextension-utilities.html + \ingroup list-of-builtin-services + + \title Utilities Service + \brief Provides miscellaneous operations. + + The \c Utilities service offers miscellaneous operations. + + \section1 Available Operations + + \section2 getHash + \code + Utilities.getHash(key: string): string + \endcode + Calculates a 16-byte hash of the input and returns it. + Rules in modules should use this function to find unique locations for output + artifacts in the build directory without duplicating the whole directory structure of + the respective input file (to deal with the case of two files with the same name in different + subdirectories of the same product). + + \section2 rfc1034Identifier + \code + Utilities.rfc1034Identifier(str: string): string + \endcode + Returns an RFC 1034 compliant identifier based on the given string by replacing each character + that is not Latin alphanumeric or \c{.} with \c{-}. +*/ diff --git a/doc/reference/jsextensions/jsextensions-general.qdoc b/doc/reference/jsextensions/jsextensions-general.qdoc index d031dbf52..409b4c838 100644 --- a/doc/reference/jsextensions/jsextensions-general.qdoc +++ b/doc/reference/jsextensions/jsextensions-general.qdoc @@ -56,23 +56,6 @@ Tries to find a variable with the given name in the build environment and returns its value. If no such variable could be found, \c undefined is returned. - \section2 qbs.getHash - \code - qbs.getHash(key: string): string - \endcode - Calculates a 16-byte hash of the input and returns it. - Rules in modules should use this function to find unique locations for output - artifacts in the build directory without duplicating the whole directory structure of - the respective input file (to deal with the case of two files with the same name in different - subdirectories of the same product). - - \section2 qbs.rfc1034Identifier - \code - qbs.rfc1034Identifier(str: string): string - \endcode - Returns an RFC 1034 compliant identifier based on the given string by replacing each character - that is not Latin alphanumeric or \c{.} with \c{-}. - \section2 loadFile \code loadFile(filePath: string): any diff --git a/share/qbs/imports/qbs/DarwinTools/darwin-tools.js b/share/qbs/imports/qbs/DarwinTools/darwin-tools.js index 056b54af6..670ca8181 100644 --- a/share/qbs/imports/qbs/DarwinTools/darwin-tools.js +++ b/share/qbs/imports/qbs/DarwinTools/darwin-tools.js @@ -29,6 +29,7 @@ ****************************************************************************/ var FileInfo = loadExtension("qbs.FileInfo"); +var Utilities = loadExtension("qbs.Utilities"); /** * Returns the numeric identifier corresponding to an Apple device name @@ -193,7 +194,7 @@ function expandPlistEnvironmentVariables(obj, env, warn) { if (varFormatter !== undefined) varFormatter = varFormatter.toLowerCase(); if (varFormatter === "rfc1034identifier") - varValue = qbs.rfc1034Identifier(varValue); + varValue = Utilities.rfc1034Identifier(varValue); value = value.slice(0, i) + varValue + value.slice(j + repl.syntax.close.length); // avoid recursive substitutions to avoid potentially infinite loops i += varValue.length; diff --git a/share/qbs/imports/qbs/base/AutotestRunner.qbs b/share/qbs/imports/qbs/base/AutotestRunner.qbs index b939b8b8b..67c5524a5 100644 --- a/share/qbs/imports/qbs/base/AutotestRunner.qbs +++ b/share/qbs/imports/qbs/base/AutotestRunner.qbs @@ -30,6 +30,7 @@ import qbs import qbs.ModUtils +import qbs.Utilities Product { name: "autotest-runner" @@ -46,7 +47,7 @@ Product { Rule { inputsFromDependencies: "application" Artifact { - filePath: qbs.getHash(input.filePath) + ".result.dummy" // Will never exist. + filePath: Utilities.getHash(input.filePath) + ".result.dummy" // Will never exist. fileTags: "autotest-result" alwaysUpdated: false } diff --git a/share/qbs/modules/Android/sdk/sdk.qbs b/share/qbs/modules/Android/sdk/sdk.qbs index 41cbb474e..9ce60fb35 100644 --- a/share/qbs/modules/Android/sdk/sdk.qbs +++ b/share/qbs/modules/Android/sdk/sdk.qbs @@ -34,6 +34,7 @@ import qbs.FileInfo import qbs.ModUtils import qbs.Probes import qbs.TextFile +import qbs.Utilities import "utils.js" as SdkUtils Module { @@ -107,7 +108,7 @@ Module { Rule { inputs: ["android.aidl"] Artifact { - filePath: FileInfo.joinPaths(qbs.getHash(input.filePath), + filePath: FileInfo.joinPaths(Utilities.getHash(input.filePath), input.completeBaseName + ".java") fileTags: ["java.java"] } diff --git a/share/qbs/modules/bundle/BundleModule.qbs b/share/qbs/modules/bundle/BundleModule.qbs index 5f7b3dfac..8ec0e8458 100644 --- a/share/qbs/modules/bundle/BundleModule.qbs +++ b/share/qbs/modules/bundle/BundleModule.qbs @@ -36,6 +36,7 @@ import qbs.FileInfo import qbs.ModUtils import qbs.PropertyList import qbs.TextFile +import qbs.Utilities Module { additionalProductTypes: ["bundle"] @@ -48,7 +49,7 @@ Module { property bool isShallow: !qbs.targetOS.contains("osx") && product.type.contains("application") property string identifierPrefix: "org.example" - property string identifier: [identifierPrefix, qbs.rfc1034Identifier(product.targetName)].join(".") + property string identifier: [identifierPrefix, Utilities.rfc1034Identifier(product.targetName)].join(".") property string extension: { if (packageType === undefined) { diff --git a/share/qbs/modules/cli/windows-dotnet.qbs b/share/qbs/modules/cli/windows-dotnet.qbs index b437e1276..a51eb4c40 100755 --- a/share/qbs/modules/cli/windows-dotnet.qbs +++ b/share/qbs/modules/cli/windows-dotnet.qbs @@ -1,4 +1,5 @@ import qbs +import qbs.Utilities CLIModule { condition: qbs.toolchain.contains("dotnet") @@ -8,7 +9,7 @@ CLIModule { vbCompilerName: "vbc" fsharpCompilerName: "fsc" - toolchainInstallPath: qbs.getNativeSetting(registryKey, "InstallPath") + toolchainInstallPath: Utilities.getNativeSetting(registryKey, "InstallPath") // private properties property string registryKey: { @@ -21,7 +22,7 @@ CLIModule { ]; for (var i in keys) { var key = keys[i] + "\\v4\\Full"; - if (qbs.getNativeSetting(key, "InstallPath")) + if (Utilities.getNativeSetting(key, "InstallPath")) return key; } } diff --git a/share/qbs/modules/cpp/CppModule.qbs b/share/qbs/modules/cpp/CppModule.qbs index 294590154..f24681b08 100644 --- a/share/qbs/modules/cpp/CppModule.qbs +++ b/share/qbs/modules/cpp/CppModule.qbs @@ -30,6 +30,7 @@ // base for Cpp modules import qbs.ModUtils +import qbs.Utilities Module { condition: false @@ -315,9 +316,9 @@ Module { validate: { var validator = new ModUtils.PropertyValidator("cpp"); validator.addCustomValidator("architecture", architecture, function (value) { - return !architecture || architecture === canonicalArchitecture(architecture); + return !architecture || architecture === Utilities.canonicalArchitecture(architecture); }, "'" + architecture + "' is invalid. You must use the canonical name '" + - canonicalArchitecture(architecture) + "'"); + Utilities.canonicalArchitecture(architecture) + "'"); validator.validate(); } diff --git a/share/qbs/modules/cpp/GenericGCC.qbs b/share/qbs/modules/cpp/GenericGCC.qbs index 0628be578..ae8c5b29d 100644 --- a/share/qbs/modules/cpp/GenericGCC.qbs +++ b/share/qbs/modules/cpp/GenericGCC.qbs @@ -34,6 +34,7 @@ import qbs.FileInfo import qbs.ModUtils import qbs.PathTools import qbs.Process +import qbs.Utilities import qbs.UnixUtils import qbs.WindowsUtils import 'gcc.js' as Gcc @@ -342,7 +343,7 @@ CppModule { Artifact { fileTags: ["obj"] - filePath: FileInfo.joinPaths(".obj", qbs.getHash(input.baseDir), input.fileName + ".o") + filePath: FileInfo.joinPaths(".obj", Utilities.getHash(input.baseDir), input.fileName + ".o") } prepare: { @@ -356,7 +357,7 @@ CppModule { Artifact { fileTags: ["obj"] - filePath: FileInfo.joinPaths(".obj", qbs.getHash(input.baseDir), input.fileName + ".o") + filePath: FileInfo.joinPaths(".obj", Utilities.getHash(input.baseDir), input.fileName + ".o") } prepare: { diff --git a/share/qbs/modules/cpp/gcc.js b/share/qbs/modules/cpp/gcc.js index 6ba260921..ac4d204e4 100644 --- a/share/qbs/modules/cpp/gcc.js +++ b/share/qbs/modules/cpp/gcc.js @@ -35,6 +35,7 @@ var ModUtils = loadExtension("qbs.ModUtils"); var PathTools = loadExtension("qbs.PathTools"); var Process = loadExtension("qbs.Process"); var UnixUtils = loadExtension("qbs.UnixUtils"); +var Utilities = loadExtension("qbs.Utilities"); var WindowsUtils = loadExtension("qbs.WindowsUtils"); function escapeLinkerFlags(product, linkerFlags) { @@ -396,7 +397,7 @@ function compilerFlags(product, input, output) { var toolchain = product.moduleProperty("qbs", "toolchain"); if (!toolchain.contains("clang")) { var hashString = FileInfo.relativePath(project.sourceDirectory, input.filePath); - var hash = qbs.getHash(hashString); + var hash = Utilities.getHash(hashString); args.push("-frandom-seed=0x" + hash.substring(0, 8)); } diff --git a/share/qbs/modules/cpp/windows-mingw.qbs b/share/qbs/modules/cpp/windows-mingw.qbs index 10f360c38..e6478eaec 100644 --- a/share/qbs/modules/cpp/windows-mingw.qbs +++ b/share/qbs/modules/cpp/windows-mingw.qbs @@ -30,6 +30,7 @@ import qbs 1.0 import qbs.ModUtils +import qbs.Utilities import qbs.WindowsUtils GenericGCC { @@ -70,7 +71,7 @@ GenericGCC { auxiliaryInputs: ["hpp"] Artifact { - filePath: ".obj/" + qbs.getHash(input.baseDir) + "/" + input.completeBaseName + "_res.o" + filePath: ".obj/" + Utilities.getHash(input.baseDir) + "/" + input.completeBaseName + "_res.o" fileTags: ["obj"] } diff --git a/share/qbs/modules/cpp/windows-msvc.qbs b/share/qbs/modules/cpp/windows-msvc.qbs index f5dd19d5d..6b5c581cf 100644 --- a/share/qbs/modules/cpp/windows-msvc.qbs +++ b/share/qbs/modules/cpp/windows-msvc.qbs @@ -33,6 +33,7 @@ import qbs.File import qbs.FileInfo import qbs.ModUtils import qbs.PathTools +import qbs.Utilities import qbs.WindowsUtils import 'msvc.js' as MSVC @@ -98,7 +99,7 @@ CppModule { filePath: { var completeBaseName = FileInfo.completeBaseName(product.moduleProperty("cpp", "cPrecompiledHeader")); - return ".obj/" + qbs.getHash(completeBaseName) + '_c.obj' + return ".obj/" + Utilities.getHash(completeBaseName) + '_c.obj' } } Artifact { @@ -119,7 +120,7 @@ CppModule { filePath: { var completeBaseName = FileInfo.completeBaseName(product.moduleProperty("cpp", "cxxPrecompiledHeader")); - return ".obj/" + qbs.getHash(completeBaseName) + '_cpp.obj' + return ".obj/" + Utilities.getHash(completeBaseName) + '_cpp.obj' } } Artifact { @@ -139,7 +140,7 @@ CppModule { Artifact { fileTags: ['obj'] - filePath: ".obj/" + qbs.getHash(input.baseDir) + "/" + input.fileName + ".obj" + filePath: ".obj/" + Utilities.getHash(input.baseDir) + "/" + input.fileName + ".obj" } prepare: { @@ -262,7 +263,7 @@ CppModule { auxiliaryInputs: ["hpp"] Artifact { - filePath: ".obj/" + qbs.getHash(input.baseDir) + "/" + input.completeBaseName + ".res" + filePath: ".obj/" + Utilities.getHash(input.baseDir) + "/" + input.completeBaseName + ".res" fileTags: ["obj"] } @@ -321,7 +322,7 @@ CppModule { Rule { inputs: ["asm"] Artifact { - filePath: ".obj/" + qbs.getHash(input.baseDir) + "/" + input.completeBaseName + ".obj" + filePath: ".obj/" + Utilities.getHash(input.baseDir) + "/" + input.completeBaseName + ".obj" fileTags: ["obj"] } prepare: { diff --git a/share/qbs/modules/java/utils.js b/share/qbs/modules/java/utils.js index 34ee80897..c1104a138 100644 --- a/share/qbs/modules/java/utils.js +++ b/share/qbs/modules/java/utils.js @@ -33,13 +33,14 @@ var FileInfo = loadExtension("qbs.FileInfo"); var ModUtils = loadExtension("qbs.ModUtils"); var Process = loadExtension("qbs.Process"); var TextFile = loadExtension("qbs.TextFile"); +var Utilities = loadExtension("qbs.Utilities"); var WindowsUtils = loadExtension("qbs.WindowsUtils"); function is64bitProcess() { var y = jdkRootRegistryKey(true); var n = jdkRootRegistryKey(false); - y = qbs.getNativeSetting(y + "\\" + qbs.getNativeSetting(y, "CurrentVersion"), "JavaHome"); - n = qbs.getNativeSetting(n + "\\" + qbs.getNativeSetting(n, "CurrentVersion"), "JavaHome"); + y = Utilities.getNativeSetting(y + "\\" + Utilities.getNativeSetting(y, "CurrentVersion"), "JavaHome"); + n = Utilities.getNativeSetting(n + "\\" + Utilities.getNativeSetting(n, "CurrentVersion"), "JavaHome"); return y !== n; } @@ -82,9 +83,9 @@ function findJdkPath(hostOS, arch, environmentPaths, searchPaths) { if (hostOS.contains("windows")) { var rootKey = jdkRootRegistryKey(useWow64Key(arch)); if (rootKey) { - var current = qbs.getNativeSetting(rootKey, "CurrentVersion"); // 1.8 etc. + var current = Utilities.getNativeSetting(rootKey, "CurrentVersion"); // 1.8 etc. if (current) { - var home = qbs.getNativeSetting([rootKey, current].join("\\"), "JavaHome"); + var home = Utilities.getNativeSetting([rootKey, current].join("\\"), "JavaHome"); if (home) { return home; } diff --git a/share/qbs/modules/nsis/NSISModule.qbs b/share/qbs/modules/nsis/NSISModule.qbs index 5930a4dec..50d2c06d5 100644 --- a/share/qbs/modules/nsis/NSISModule.qbs +++ b/share/qbs/modules/nsis/NSISModule.qbs @@ -32,18 +32,19 @@ import qbs 1.0 import qbs.File import qbs.FileInfo import qbs.ModUtils +import qbs.Utilities Module { condition: qbs.targetOS.contains("windows") - property path toolchainInstallPath: qbs.getNativeSetting(registryKey) + property path toolchainInstallPath: Utilities.getNativeSetting(registryKey) property string version: (versionMajor !== undefined && versionMinor !== undefined) ? (versionMajor + "." + versionMinor) : undefined property var versionParts: [ versionMajor, versionMinor, versionPatch, versionBuild ] - property int versionMajor: qbs.getNativeSetting(registryKey, "VersionMajor") - property int versionMinor: qbs.getNativeSetting(registryKey, "VersionMinor") - property int versionPatch: qbs.getNativeSetting(registryKey, "VersionBuild") - property int versionBuild: qbs.getNativeSetting(registryKey, "VersionRevision") + property int versionMajor: Utilities.getNativeSetting(registryKey, "VersionMajor") + property int versionMinor: Utilities.getNativeSetting(registryKey, "VersionMinor") + property int versionPatch: Utilities.getNativeSetting(registryKey, "VersionBuild") + property int versionBuild: Utilities.getNativeSetting(registryKey, "VersionRevision") property string compilerName: "makensis" property string compilerPath: compilerName @@ -94,7 +95,7 @@ Module { var keys = [ "HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS", "HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\NSIS" ]; for (var i in keys) { - if (qbs.getNativeSetting(keys[i])) + if (Utilities.getNativeSetting(keys[i])) return keys[i]; } } diff --git a/share/qbs/modules/qbs/common.qbs b/share/qbs/modules/qbs/common.qbs index ff57f7d53..733a3096f 100644 --- a/share/qbs/modules/qbs/common.qbs +++ b/share/qbs/modules/qbs/common.qbs @@ -32,6 +32,7 @@ import qbs 1.0 import qbs.FileInfo import qbs.ModUtils import qbs.PathTools +import qbs.Utilities Module { property string buildVariant: "debug" @@ -41,20 +42,20 @@ Module { readonly property stringList hostOS: undefined // set internally property string hostOSVersion: { if (hostOS && hostOS.contains("osx")) { - return getNativeSetting("/System/Library/CoreServices/ServerVersion.plist", "ProductVersion") || - getNativeSetting("/System/Library/CoreServices/SystemVersion.plist", "ProductVersion"); + return Utilities.getNativeSetting("/System/Library/CoreServices/ServerVersion.plist", "ProductVersion") || + Utilities.getNativeSetting("/System/Library/CoreServices/SystemVersion.plist", "ProductVersion"); } else if (hostOS && hostOS.contains("windows")) { - var version = getNativeSetting(windowsRegistryKey, "CurrentVersion"); + var version = Utilities.getNativeSetting(windowsRegistryKey, "CurrentVersion"); return version + "." + hostOSBuildVersion; } } property string hostOSBuildVersion: { if (hostOS.contains("osx")) { - return getNativeSetting("/System/Library/CoreServices/ServerVersion.plist", "ProductBuildVersion") || - getNativeSetting("/System/Library/CoreServices/SystemVersion.plist", "ProductBuildVersion"); + return Utilities.getNativeSetting("/System/Library/CoreServices/ServerVersion.plist", "ProductBuildVersion") || + Utilities.getNativeSetting("/System/Library/CoreServices/SystemVersion.plist", "ProductBuildVersion"); } else if (hostOS.contains("windows")) { - return getNativeSetting(windowsRegistryKey, "CurrentBuildNumber"); + return Utilities.getNativeSetting(windowsRegistryKey, "CurrentBuildNumber"); } } @@ -109,16 +110,16 @@ Module { } validator.addCustomValidator("architecture", architecture, function (value) { - return !architecture || architecture === canonicalArchitecture(architecture); + return !architecture || architecture === Utilities.canonicalArchitecture(architecture); }, "'" + architecture + "' is invalid. You must use the canonical name '" + - canonicalArchitecture(architecture) + "'"); + Utilities.canonicalArchitecture(architecture) + "'"); validator.validate(); } // private properties property string windowsRegistryKey: "HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion" - property path windowsSystemRoot: FileInfo.fromWindowsSeparators(getNativeSetting(windowsRegistryKey, "SystemRoot")) + property path windowsSystemRoot: FileInfo.fromWindowsSeparators(Utilities.getNativeSetting(windowsRegistryKey, "SystemRoot")) property path windowsShellPath: FileInfo.fromWindowsSeparators(getEnv("COMSPEC")) || FileInfo.joinPaths(windowsSystemRoot, "System32", "cmd.exe") property var commonRunEnvironment: { diff --git a/share/qbs/modules/wix/WiXModule.qbs b/share/qbs/modules/wix/WiXModule.qbs index e48af0a2e..568a57f64 100644 --- a/share/qbs/modules/wix/WiXModule.qbs +++ b/share/qbs/modules/wix/WiXModule.qbs @@ -32,13 +32,14 @@ import qbs import qbs.File import qbs.FileInfo import qbs.ModUtils +import qbs.Utilities Module { condition: qbs.targetOS.contains("windows") - property path toolchainInstallPath: qbs.getNativeSetting(registryKey, "InstallFolder") - property path toolchainInstallRoot: qbs.getNativeSetting(registryKey, "InstallRoot") - property string version: qbs.getNativeSetting(registryKey, "ProductVersion") + property path toolchainInstallPath: Utilities.getNativeSetting(registryKey, "InstallFolder") + property path toolchainInstallRoot: Utilities.getNativeSetting(registryKey, "InstallRoot") + property string version: Utilities.getNativeSetting(registryKey, "ProductVersion") property var versionParts: version ? version.split('.').map(function(item) { return parseInt(item, 10); }) : [] property int versionMajor: versionParts[0] property int versionMinor: versionParts[1] @@ -130,9 +131,9 @@ Module { var keyWoW64 = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows Installer XML\\"; for (i in knownVersions) { - if (qbs.getNativeSetting(keyNative + knownVersions[i], "ProductVersion")) + if (Utilities.getNativeSetting(keyNative + knownVersions[i], "ProductVersion")) return keyNative + knownVersions[i]; - if (qbs.getNativeSetting(keyWoW64 + knownVersions[i], "ProductVersion")) + if (Utilities.getNativeSetting(keyWoW64 + knownVersions[i], "ProductVersion")) return keyWoW64 + knownVersions[i]; } } @@ -186,7 +187,7 @@ Module { Artifact { fileTags: ["wixobj"] - filePath: FileInfo.joinPaths(".obj", qbs.getHash(input.baseDir), + filePath: FileInfo.joinPaths(".obj", Utilities.getHash(input.baseDir), FileInfo.baseName(input.fileName) + ".wixobj") } diff --git a/src/lib/corelib/corelib.qbs b/src/lib/corelib/corelib.qbs index 814211f55..44f854a31 100644 --- a/src/lib/corelib/corelib.qbs +++ b/src/lib/corelib/corelib.qbs @@ -183,6 +183,8 @@ QbsLibrary { "temporarydir.h", "textfile.cpp", "textfile.h", + "utilitiesextension.cpp", + "utilitiesextension.h", "domxml.cpp", "domxml.h" ] diff --git a/src/lib/corelib/jsextensions/jsextensions.cpp b/src/lib/corelib/jsextensions/jsextensions.cpp index 8ec8adf63..885544afb 100644 --- a/src/lib/corelib/jsextensions/jsextensions.cpp +++ b/src/lib/corelib/jsextensions/jsextensions.cpp @@ -38,6 +38,7 @@ #include "propertylist.h" #include "temporarydir.h" #include "textfile.h" +#include "utilitiesextension.h" #include @@ -76,6 +77,7 @@ JsExtensions::InitializerMap JsExtensions::initializers() m_initializers.insert(QLatin1String("TemporaryDir"), &initializeJsExtensionTemporaryDir); m_initializers.insert(QLatin1String("TextFile"), &initializeJsExtensionTextFile); m_initializers.insert(QLatin1String("PropertyList"), &initializeJsExtensionPropertyList); + m_initializers.insert(QLatin1String("Utilities"), &initializeJsExtensionUtilities); } return m_initializers; } diff --git a/src/lib/corelib/jsextensions/jsextensions.pri b/src/lib/corelib/jsextensions/jsextensions.pri index b656075b6..0258d42e5 100644 --- a/src/lib/corelib/jsextensions/jsextensions.pri +++ b/src/lib/corelib/jsextensions/jsextensions.pri @@ -9,7 +9,8 @@ HEADERS += \ $$PWD/process.h \ $$PWD/moduleproperties.h \ $$PWD/domxml.h \ - $$PWD/jsextensions.h + $$PWD/jsextensions.h \ + $$PWD/utilitiesextension.h SOURCES += \ $$PWD/environmentextension.cpp \ @@ -20,7 +21,8 @@ SOURCES += \ $$PWD/process.cpp \ $$PWD/moduleproperties.cpp \ $$PWD/domxml.cpp \ - $$PWD/jsextensions.cpp + $$PWD/jsextensions.cpp \ + $$PWD/utilitiesextension.cpp mac { HEADERS += $$PWD/propertylist.h $$PWD/propertylistutils.h diff --git a/src/lib/corelib/jsextensions/utilitiesextension.cpp b/src/lib/corelib/jsextensions/utilitiesextension.cpp new file mode 100644 index 000000000..7f4e8469c --- /dev/null +++ b/src/lib/corelib/jsextensions/utilitiesextension.cpp @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of the Qt Build Suite. +** +** 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. +** +****************************************************************************/ + +#include "utilitiesextension.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace qbs { +namespace Internal { + +class UtilitiesExtension : public QObject, QScriptable +{ + Q_OBJECT +public: + static QScriptValue js_ctor(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_canonicalArchitecture(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_getHash(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_getNativeSetting(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_rfc1034identifier(QScriptContext *context, QScriptEngine *engine); +}; + +void initializeJsExtensionUtilities(QScriptValue extensionObject) +{ + QScriptEngine *engine = extensionObject.engine(); + QScriptValue environmentObj = engine->newQMetaObject(&UtilitiesExtension::staticMetaObject, + engine->newFunction(&UtilitiesExtension::js_ctor)); + environmentObj.setProperty(QStringLiteral("canonicalArchitecture"), + engine->newFunction(UtilitiesExtension::js_canonicalArchitecture, 1)); + environmentObj.setProperty(QStringLiteral("getHash"), + engine->newFunction(UtilitiesExtension::js_getHash, 1)); + environmentObj.setProperty(QStringLiteral("getNativeSetting"), + engine->newFunction(UtilitiesExtension::js_getNativeSetting, 3)); + environmentObj.setProperty(QStringLiteral("rfc1034Identifier"), + engine->newFunction(UtilitiesExtension::js_rfc1034identifier, 1)); + extensionObject.setProperty(QStringLiteral("Utilities"), environmentObj); +} + +QScriptValue UtilitiesExtension::js_ctor(QScriptContext *context, QScriptEngine *engine) +{ + // Add instance variables here etc., if needed. + Q_UNUSED(context); + Q_UNUSED(engine); + return true; +} + +QScriptValue UtilitiesExtension::js_canonicalArchitecture(QScriptContext *context, + QScriptEngine *engine) +{ + if (Q_UNLIKELY(context->argumentCount() != 1)) + return context->throwError(QScriptContext::SyntaxError, + QLatin1String("canonicalArchitecture expects 1 argument")); + const QString architecture = context->argument(0).toString(); + return engine->toScriptValue(canonicalArchitecture(architecture)); +} + +QScriptValue UtilitiesExtension::js_getHash(QScriptContext *context, QScriptEngine *engine) +{ + if (Q_UNLIKELY(context->argumentCount() < 1)) { + return context->throwError(QScriptContext::SyntaxError, + QLatin1String("getHash expects 1 argument")); + } + const QByteArray input = context->argument(0).toString().toLatin1(); + const QByteArray hash + = QCryptographicHash::hash(input, QCryptographicHash::Sha1).toHex().left(16); + return engine->toScriptValue(QString::fromLatin1(hash)); +} + +QScriptValue UtilitiesExtension::js_getNativeSetting(QScriptContext *context, QScriptEngine *engine) +{ + if (Q_UNLIKELY(context->argumentCount() < 1 || context->argumentCount() > 3)) { + return context->throwError(QScriptContext::SyntaxError, + QLatin1String("getNativeSetting expects between 1 and 3 arguments")); + } + + QString key = context->argumentCount() > 1 ? context->argument(1).toString() : QString(); + + // We'll let empty string represent the default registry value + if (HostOsInfo::isWindowsHost() && key.isEmpty()) + key = QLatin1String("."); + + QVariant defaultValue = context->argumentCount() > 2 ? context->argument(2).toVariant() : QVariant(); + + QSettings settings(context->argument(0).toString(), QSettings::NativeFormat); + QVariant value = settings.value(key, defaultValue); + return value.isNull() ? engine->undefinedValue() : engine->toScriptValue(value); +} + +QScriptValue UtilitiesExtension::js_rfc1034identifier(QScriptContext *context, + QScriptEngine *engine) +{ + if (Q_UNLIKELY(context->argumentCount() != 1)) + return context->throwError(QScriptContext::SyntaxError, + QLatin1String("rfc1034Identifier expects 1 argument")); + const QString identifier = context->argument(0).toString(); + return engine->toScriptValue(HostOsInfo::rfc1034Identifier(identifier)); +} + +} // namespace Internal +} // namespace qbs + +Q_DECLARE_METATYPE(qbs::Internal::UtilitiesExtension *) + +#include "utilitiesextension.moc" diff --git a/src/lib/corelib/jsextensions/utilitiesextension.h b/src/lib/corelib/jsextensions/utilitiesextension.h new file mode 100644 index 000000000..4815cec51 --- /dev/null +++ b/src/lib/corelib/jsextensions/utilitiesextension.h @@ -0,0 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of the Qt Build Suite. +** +** 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. +** +****************************************************************************/ + +#ifndef QBS_UTILITIESEXTENSION_H +#define QBS_UTILITIESEXTENSION_H + +#include + +namespace qbs { +namespace Internal { + +void initializeJsExtensionUtilities(QScriptValue extensionObject); + +} // namespace Internal +} // namespace qbs + +#endif // QBS_UTILITIESEXTENSION_H diff --git a/src/lib/corelib/language/builtinvalue.h b/src/lib/corelib/language/builtinvalue.h index a8e2d188f..6734eba54 100644 --- a/src/lib/corelib/language/builtinvalue.h +++ b/src/lib/corelib/language/builtinvalue.h @@ -41,11 +41,8 @@ class BuiltinValue : public Value public: enum Builtin { - GetNativeSettingFunction, GetEnvFunction, - CurrentEnvFunction, - CanonicalArchitectureFunction, - Rfc1034IdentifierFunction + CurrentEnvFunction }; static BuiltinValuePtr create(Builtin builtin); diff --git a/src/lib/corelib/language/evaluatorscriptclass.cpp b/src/lib/corelib/language/evaluatorscriptclass.cpp index 971faea0d..ccd4158a0 100644 --- a/src/lib/corelib/language/evaluatorscriptclass.cpp +++ b/src/lib/corelib/language/evaluatorscriptclass.cpp @@ -44,7 +44,6 @@ #include #include -#include #include #include #include @@ -281,12 +280,8 @@ EvaluatorScriptClass::EvaluatorScriptClass(ScriptEngine *scriptEngine, const Log , m_logger(logger) , m_valueCacheEnabled(false) { - m_getNativeSettingBuiltin = scriptEngine->newFunction(js_getNativeSetting, 3); m_getEnvBuiltin = scriptEngine->newFunction(js_getEnv, 1); m_currentEnvBuiltin = scriptEngine->newFunction(js_currentEnv, 0); - m_canonicalArchitectureBuiltin = scriptEngine->newFunction(js_canonicalArchitecture, 1); - m_rfc1034identifierBuiltin = scriptEngine->newFunction(js_rfc1034identifier, 1); - m_getHashBuiltin = scriptEngine->newFunction(js_getHash, 1); } QScriptClass::QueryFlags EvaluatorScriptClass::queryProperty(const QScriptValue &object, @@ -520,41 +515,15 @@ void EvaluatorScriptClass::setValueCacheEnabled(bool enabled) QScriptValue EvaluatorScriptClass::scriptValueForBuiltin(BuiltinValue::Builtin builtin) const { switch (builtin) { - case BuiltinValue::GetNativeSettingFunction: - return m_getNativeSettingBuiltin; case BuiltinValue::GetEnvFunction: return m_getEnvBuiltin; case BuiltinValue::CurrentEnvFunction: return m_currentEnvBuiltin; - case BuiltinValue::CanonicalArchitectureFunction: - return m_canonicalArchitectureBuiltin; - case BuiltinValue::Rfc1034IdentifierFunction: - return m_rfc1034identifierBuiltin; } QBS_ASSERT(!"unhandled builtin", ;); return QScriptValue(); } -QScriptValue EvaluatorScriptClass::js_getNativeSetting(QScriptContext *context, QScriptEngine *engine) -{ - if (Q_UNLIKELY(context->argumentCount() < 1 || context->argumentCount() > 3)) { - return context->throwError(QScriptContext::SyntaxError, - QLatin1String("getNativeSetting expects between 1 and 3 arguments")); - } - - QString key = context->argumentCount() > 1 ? context->argument(1).toString() : QString(); - - // We'll let empty string represent the default registry value - if (HostOsInfo::isWindowsHost() && key.isEmpty()) - key = QLatin1String("."); - - QVariant defaultValue = context->argumentCount() > 2 ? context->argument(2).toVariant() : QVariant(); - - QSettings settings(context->argument(0).toString(), QSettings::NativeFormat); - QVariant value = settings.value(key, defaultValue); - return value.isNull() ? engine->undefinedValue() : engine->toScriptValue(value); -} - QScriptValue EvaluatorScriptClass::js_getEnv(QScriptContext *context, QScriptEngine *engine) { if (Q_UNLIKELY(context->argumentCount() < 1)) { @@ -578,39 +547,6 @@ QScriptValue EvaluatorScriptClass::js_currentEnv(QScriptContext *context, QScrip return envObject; } -QScriptValue EvaluatorScriptClass::js_canonicalArchitecture(QScriptContext *context, QScriptEngine *engine) -{ - if (Q_UNLIKELY(context->argumentCount() < 1)) { - return context->throwError(QScriptContext::SyntaxError, - QLatin1String("canonicalArchitecture expects 1 argument")); - } - const QString architecture = context->argument(0).toString(); - return engine->toScriptValue(canonicalArchitecture(architecture)); -} - -QScriptValue EvaluatorScriptClass::js_rfc1034identifier(QScriptContext *context, - QScriptEngine *engine) -{ - if (Q_UNLIKELY(context->argumentCount() < 1)) { - return context->throwError(QScriptContext::SyntaxError, - QLatin1String("rfc1034Identifier expects 1 argument")); - } - const QString identifier = context->argument(0).toString(); - return engine->toScriptValue(HostOsInfo::rfc1034Identifier(identifier)); -} - -QScriptValue EvaluatorScriptClass::js_getHash(QScriptContext *context, QScriptEngine *engine) -{ - if (Q_UNLIKELY(context->argumentCount() < 1)) { - return context->throwError(QScriptContext::SyntaxError, - QLatin1String("getHash expects 1 argument")); - } - const QByteArray input = context->argument(0).toString().toLatin1(); - const QByteArray hash - = QCryptographicHash::hash(input, QCryptographicHash::Sha1).toHex().left(16); - return engine->toScriptValue(QString::fromLatin1(hash)); -} - QScriptValue EvaluatorScriptClass::js_consoleError(QScriptContext *context, QScriptEngine *engine, Logger *logger) { diff --git a/src/lib/corelib/language/evaluatorscriptclass.h b/src/lib/corelib/language/evaluatorscriptclass.h index 85d2d337f..5277cf9d6 100644 --- a/src/lib/corelib/language/evaluatorscriptclass.h +++ b/src/lib/corelib/language/evaluatorscriptclass.h @@ -62,12 +62,8 @@ public: void setValueCacheEnabled(bool enabled); QScriptValue scriptValueForBuiltin(BuiltinValue::Builtin builtin) const; - static QScriptValue js_getNativeSetting(QScriptContext *context, QScriptEngine *engine); static QScriptValue js_getEnv(QScriptContext *context, QScriptEngine *engine); static QScriptValue js_currentEnv(QScriptContext *context, QScriptEngine *engine); - static QScriptValue js_canonicalArchitecture(QScriptContext *context, QScriptEngine *engine); - static QScriptValue js_rfc1034identifier(QScriptContext *context, QScriptEngine *engine); - static QScriptValue js_getHash(QScriptContext *context, QScriptEngine *engine); static QScriptValue js_consoleError(QScriptContext *context, QScriptEngine *engine, Logger *logger); @@ -105,12 +101,8 @@ private: QueryResult m_queryResult; Logger m_logger; bool m_valueCacheEnabled; - QScriptValue m_getNativeSettingBuiltin; QScriptValue m_getEnvBuiltin; QScriptValue m_currentEnvBuiltin; - QScriptValue m_canonicalArchitectureBuiltin; - QScriptValue m_rfc1034identifierBuiltin; - QScriptValue m_getHashBuiltin; QStack m_sourceValueStack; QSet m_currentNextChain; }; diff --git a/src/lib/corelib/language/moduleloader.cpp b/src/lib/corelib/language/moduleloader.cpp index e3109b2c5..a138323f1 100644 --- a/src/lib/corelib/language/moduleloader.cpp +++ b/src/lib/corelib/language/moduleloader.cpp @@ -1233,17 +1233,11 @@ static QStringList hostOS() void ModuleLoader::setupBaseModulePrototype(Item *prototype) { - prototype->setProperty(QLatin1String("getNativeSetting"), - BuiltinValue::create(BuiltinValue::GetNativeSettingFunction)); prototype->setProperty(QLatin1String("getEnv"), BuiltinValue::create(BuiltinValue::GetEnvFunction)); prototype->setProperty(QLatin1String("currentEnv"), BuiltinValue::create(BuiltinValue::CurrentEnvFunction)); prototype->setProperty(QLatin1String("hostOS"), VariantValue::create(hostOS())); - prototype->setProperty(QLatin1String("canonicalArchitecture"), - BuiltinValue::create(BuiltinValue::CanonicalArchitectureFunction)); - prototype->setProperty(QLatin1String("rfc1034Identifier"), - BuiltinValue::create(BuiltinValue::Rfc1034IdentifierFunction)); prototype->setProperty(QLatin1String("libexecPath"), VariantValue::create(m_parameters.libexecPath())); diff --git a/src/lib/corelib/language/scriptengine.cpp b/src/lib/corelib/language/scriptengine.cpp index 3536722f5..ab72d7d57 100644 --- a/src/lib/corelib/language/scriptengine.cpp +++ b/src/lib/corelib/language/scriptengine.cpp @@ -516,18 +516,10 @@ private: void ScriptEngine::installQbsBuiltins() { globalObject().setProperty(QLatin1String("qbs"), m_qbsObject = newObject()); - installQbsFunction(QLatin1String("getNativeSetting"), - EvaluatorScriptClass::js_getNativeSetting); installQbsFunction(QLatin1String("getEnv"), EvaluatorScriptClass::js_getEnv); installQbsFunction(QLatin1String("currentEnv"), EvaluatorScriptClass::js_currentEnv); - installQbsFunction(QLatin1String("canonicalArchitecture"), - EvaluatorScriptClass::js_canonicalArchitecture); - installQbsFunction(QLatin1String("rfc1034Identifier"), - EvaluatorScriptClass::js_rfc1034identifier); - installQbsFunction(QLatin1String("getHash"), - EvaluatorScriptClass::js_getHash); globalObject().setProperty(QLatin1String("console"), m_consoleObject = newObject()); installConsoleFunction(QLatin1String("debug"), diff --git a/src/lib/corelib/language/testdata/canonicalArchitecture.qbs b/src/lib/corelib/language/testdata/canonicalArchitecture.qbs index 94da7b276..16c040ba0 100644 --- a/src/lib/corelib/language/testdata/canonicalArchitecture.qbs +++ b/src/lib/corelib/language/testdata/canonicalArchitecture.qbs @@ -1,3 +1,6 @@ +import qbs +import qbs.Utilities + Product { - name: qbs.canonicalArchitecture("i386") + name: Utilities.canonicalArchitecture("i386") } diff --git a/src/lib/corelib/language/testdata/getNativeSetting.qbs b/src/lib/corelib/language/testdata/getNativeSetting.qbs index c414c79f9..122b0e657 100644 --- a/src/lib/corelib/language/testdata/getNativeSetting.qbs +++ b/src/lib/corelib/language/testdata/getNativeSetting.qbs @@ -1,23 +1,24 @@ import qbs.FileInfo +import qbs.Utilities Project { Product { name: { if (qbs.hostOS.contains("osx")) { - return qbs.getNativeSetting("/System/Library/CoreServices/SystemVersion.plist", "ProductName"); + return Utilities.getNativeSetting("/System/Library/CoreServices/SystemVersion.plist", "ProductName"); } else if (qbs.hostOS.contains("windows")) { - var productName = qbs.getNativeSetting("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion", "ProductName"); + var productName = Utilities.getNativeSetting("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion", "ProductName"); if (productName.contains("Windows")) { return "Windows"; } return undefined; } else { - return qbs.getNativeSetting(FileInfo.joinPaths(path, "nativesettings.ini"), "osname"); + return Utilities.getNativeSetting(FileInfo.joinPaths(path, "nativesettings.ini"), "osname"); } } } Product { - name: qbs.getNativeSetting("/dev/null", undefined, "fallback"); + name: Utilities.getNativeSetting("/dev/null", undefined, "fallback"); } } diff --git a/src/lib/corelib/language/testdata/rfc1034identifier.qbs b/src/lib/corelib/language/testdata/rfc1034identifier.qbs index b9f26b46c..eb73dc4d6 100644 --- a/src/lib/corelib/language/testdata/rfc1034identifier.qbs +++ b/src/lib/corelib/language/testdata/rfc1034identifier.qbs @@ -1,6 +1,7 @@ import qbs +import qbs.Utilities CppApplication { - name: qbs.rfc1034Identifier("this!has@special#characters$uh-oh,Undersc0r3s_Are.Bad") + name: Utilities.rfc1034Identifier("this!has@special#characters$uh-oh,Undersc0r3s_Are.Bad") bundle.infoPlist: { return {"CFBundleIdentifier": "$(PRODUCT_NAME:rfc1034identifier)"}; } } -- cgit v1.2.3