diff --git a/examples/AUv3Synth/Builds/MacOSX/Info-Shared_Code.plist b/examples/AUv3Synth/Builds/MacOSX/Info-Shared_Code.plist
deleted file mode 100644
index 9bd2f8404e..0000000000
--- a/examples/AUv3Synth/Builds/MacOSX/Info-Shared_Code.plist
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
-
-
- CFBundleExecutable
- ${EXECUTABLE_NAME}
- CFBundleIconFile
-
- CFBundleIdentifier
- $(PRODUCT_BUNDLE_IDENTIFIER)
- CFBundleName
- AUv3Synth
- CFBundleDisplayName
- AUv3Synth
- CFBundlePackageType
- FMWK
- CFBundleSignature
- ????
- CFBundleShortVersionString
- 1.0.0
- CFBundleVersion
- 1.0.0
- NSHumanReadableCopyright
-
- NSHighResolutionCapable
-
-
-
diff --git a/examples/AUv3Synth/Builds/iOS/Info-Shared_Code.plist b/examples/AUv3Synth/Builds/iOS/Info-Shared_Code.plist
deleted file mode 100644
index a63dfa20c6..0000000000
--- a/examples/AUv3Synth/Builds/iOS/Info-Shared_Code.plist
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
-
-
-
- LSRequiresIPhoneOS
-
- UIViewControllerBasedStatusBarAppearance
-
- CFBundleExecutable
- ${EXECUTABLE_NAME}
- CFBundleIdentifier
- $(PRODUCT_BUNDLE_IDENTIFIER)
- CFBundleName
- AUv3Synth
- CFBundleDisplayName
- AUv3Synth
- CFBundlePackageType
- FMWK
- CFBundleSignature
- ????
- CFBundleShortVersionString
- 1.0.0
- CFBundleVersion
- 1.0.0
- NSHumanReadableCopyright
-
- NSHighResolutionCapable
-
- UIRequiresFullScreen
-
- UIStatusBarHidden
-
- UISupportedInterfaceOrientations
-
- UIInterfaceOrientationPortrait
- UIInterfaceOrientationLandscapeLeft
- UIInterfaceOrientationLandscapeRight
-
- UIBackgroundModes
-
-
-
diff --git a/extras/Projucer/Source/Project Saving/jucer_ProjectExport_XCode.h b/extras/Projucer/Source/Project Saving/jucer_ProjectExport_XCode.h
index bb862dd9f8..d70da4499b 100644
--- a/extras/Projucer/Source/Project Saving/jucer_ProjectExport_XCode.h
+++ b/extras/Projucer/Source/Project Saving/jucer_ProjectExport_XCode.h
@@ -1,2782 +1,2752 @@
-/*
- ==============================================================================
-
- This file is part of the JUCE library.
- Copyright (c) 2015 - ROLI Ltd.
-
- Permission is granted to use this software under the terms of either:
- a) the GPL v2 (or any later version)
- b) the Affero GPL v3
-
- Details of these licenses can be found at: www.gnu.org/licenses
-
- JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- ------------------------------------------------------------------------------
-
- To release a closed-source product which uses JUCE, commercial licenses are
- available: visit www.juce.com for more information.
-
- ==============================================================================
-*/
-
-#include "../Application/jucer_Application.h"
-#include "jucer_TextWithDefaultPropertyComponent.h"
-
-namespace
-{
- const char* const osxVersionDefault = "default";
- const int oldestSDKVersion = 5;
- const int currentSDKVersion = 11;
-
- const char* const osxArch_Default = "default";
- const char* const osxArch_Native = "Native";
- const char* const osxArch_32BitUniversal = "32BitUniversal";
- const char* const osxArch_64BitUniversal = "64BitUniversal";
- const char* const osxArch_64Bit = "64BitIntel";
-}
-
-//==============================================================================
-class XCodeProjectExporter : public ProjectExporter
-{
-public:
- //==============================================================================
- static const char* getNameMac() { return "Xcode (MacOSX)"; }
- static const char* getNameiOS() { return "Xcode (iOS)"; }
- static const char* getValueTreeTypeName (bool iOS) { return iOS ? "XCODE_IPHONE" : "XCODE_MAC"; }
-
- //==============================================================================
- XCodeProjectExporter (Project& p, const ValueTree& t, const bool isIOS)
- : ProjectExporter (p, t),
- xcodeCanUseDwarf (true),
- iOS (isIOS)
- {
- name = iOS ? getNameiOS() : getNameMac();
-
- if (getTargetLocationString().isEmpty())
- getTargetLocationValue() = getDefaultBuildsRootFolder() + (iOS ? "iOS" : "MacOSX");
-
- initialiseDependencyPathValues();
-
- if (iOS)
- {
- if (getScreenOrientationValue().toString().isEmpty())
- getScreenOrientationValue() = "portraitlandscape";
- }
- }
-
- static XCodeProjectExporter* createForSettings (Project& project, const ValueTree& settings)
- {
- if (settings.hasType (getValueTreeTypeName (false))) return new XCodeProjectExporter (project, settings, false);
- if (settings.hasType (getValueTreeTypeName (true))) return new XCodeProjectExporter (project, settings, true);
-
- return nullptr;
- }
-
- //==============================================================================
- Value getPListToMergeValue() { return getSetting ("customPList"); }
- String getPListToMergeString() const { return settings ["customPList"]; }
-
- Value getExtraFrameworksValue() { return getSetting (Ids::extraFrameworks); }
- String getExtraFrameworksString() const { return settings [Ids::extraFrameworks]; }
-
- Value getPostBuildScriptValue() { return getSetting (Ids::postbuildCommand); }
- String getPostBuildScript() const { return settings [Ids::postbuildCommand]; }
-
- Value getPreBuildScriptValue() { return getSetting (Ids::prebuildCommand); }
- String getPreBuildScript() const { return settings [Ids::prebuildCommand]; }
-
- Value getScreenOrientationValue() { return getSetting (Ids::iosScreenOrientation); }
- String getScreenOrientationString() const { return settings [Ids::iosScreenOrientation]; }
-
- Value getCustomResourceFoldersValue() { return getSetting (Ids::customXcodeResourceFolders); }
- String getCustomResourceFoldersString() const { return getSettingString (Ids::customXcodeResourceFolders).replaceCharacters ("\r\n", "::"); }
-
- Value getCustomXcassetsFolderValue() { return getSetting (Ids::customXcassetsFolder); }
- String getCustomXcassetsFolderString() const { return settings [Ids::customXcassetsFolder]; }
-
- Value getInAppPurchasesValue() { return getSetting (Ids::iosInAppPurchases); }
- bool isInAppPurchasesEnabled() const { return settings [Ids::iosInAppPurchases]; }
- Value getBackgroundAudioValue() { return getSetting (Ids::iosBackgroundAudio); }
- bool isBackgroundAudioEnabled() const { return settings [Ids::iosBackgroundAudio]; }
- Value getBackgroundBleValue() { return getSetting (Ids::iosBackgroundBle); }
- bool isBackgroundBleEnabled() const { return settings [Ids::iosBackgroundBle]; }
-
- Value getIosDevelopmentTeamIDValue() { return getSetting (Ids::iosDevelopmentTeamID); }
- String getIosDevelopmentTeamIDString() const { return settings [Ids::iosDevelopmentTeamID]; }
-
- bool usesMMFiles() const override { return true; }
- bool canCopeWithDuplicateFiles() override { return true; }
- bool supportsUserDefinedConfigurations() const override { return true; }
-
- bool isXcode() const override { return true; }
- bool isVisualStudio() const override { return false; }
- bool isCodeBlocks() const override { return false; }
- bool isMakefile() const override { return false; }
- bool isAndroidStudio() const override { return false; }
- bool isAndroidAnt() const override { return false; }
-
- bool isAndroid() const override { return false; }
- bool isWindows() const override { return false; }
- bool isLinux() const override { return false; }
- bool isOSX() const override { return ! iOS; }
- bool isiOS() const override { return iOS; }
-
- bool supportsVST() const override { return ! iOS; }
- bool supportsVST3() const override { return ! iOS; }
- bool supportsAAX() const override { return ! iOS; }
- bool supportsRTAS() const override { return ! iOS; }
- bool supportsAU() const override { return ! iOS; }
- bool supportsAUv3() const override { return true; }
- bool supportsStandalone() const override { return true; }
-
- void createExporterProperties (PropertyListBuilder& props) override
- {
- if (iOS)
- {
- props.add (new TextPropertyComponent (getCustomXcassetsFolderValue(), "Custom Xcassets folder", 128, false),
- "If this field is not empty, your Xcode project will use the custom xcassets folder specified here "
- "for the app icons and launchimages, and will ignore the Icon files specified above.");
- }
-
- props.add (new TextPropertyComponent (getCustomResourceFoldersValue(), "Custom Xcode Resource folders", 8192, true),
- "You can specify a list of custom resource folders here (separated by newlines or whitespace). "
- "References to these folders will then be added to the Xcode resources. "
- "This way you can specify them for OS X and iOS separately, and modify the content of the resource folders "
- "without re-saving the Projucer project.");
-
- if (iOS)
- {
- static const char* orientations[] = { "Portrait and Landscape", "Portrait", "Landscape", nullptr };
- static const char* orientationValues[] = { "portraitlandscape", "portrait", "landscape", nullptr };
-
- props.add (new ChoicePropertyComponent (getScreenOrientationValue(), "Screen orientation",StringArray (orientations), Array (orientationValues)),
- "The screen orientations that this app should support");
-
- props.add (new BooleanPropertyComponent (getSetting ("UIFileSharingEnabled"), "File Sharing Enabled", "Enabled"),
- "Enable this to expose your app's files to iTunes.");
-
- props.add (new BooleanPropertyComponent (getSetting ("UIStatusBarHidden"), "Status Bar Hidden", "Enabled"),
- "Enable this to disable the status bar in your app.");
-
- props.add (new BooleanPropertyComponent (getInAppPurchasesValue(), "In-App purchases capability", "Enabled"),
- "Enable this to grant your app the capability for in-app purchases. "
- "This option requires that you specify a valid Development Team ID.");
-
- props.add (new BooleanPropertyComponent (getBackgroundAudioValue(), "Audio background capability", "Enabled"),
- "Enable this to grant your app the capability to access audio when in background mode.");
-
- props.add (new BooleanPropertyComponent (getBackgroundBleValue(), "Bluetooth MIDI background capability", "Enabled"),
- "Enable this to grant your app the capability to connect to Bluetooth LE devices when in background mode.");
- }
- else if (projectType.isGUIApplication())
- {
- props.add (new TextPropertyComponent (getSetting ("documentExtensions"), "Document file extensions", 128, false),
- "A comma-separated list of file extensions for documents that your app can open. "
- "Using a leading '.' is optional, and the extensions are not case-sensitive.");
- }
-
- props.add (new TextPropertyComponent (getPListToMergeValue(), "Custom PList", 8192, true),
- "You can paste the contents of an XML PList file in here, and the settings that it contains will override any "
- "settings that the Projucer creates. BEWARE! When doing this, be careful to remove from the XML any "
- "values that you DO want the Projucer to change!");
-
- props.add (new TextPropertyComponent (getExtraFrameworksValue(), "Extra Frameworks", 2048, false),
- "A comma-separated list of extra frameworks that should be added to the build. "
- "(Don't include the .framework extension in the name)");
-
- props.add (new TextPropertyComponent (getPreBuildScriptValue(), "Pre-build shell script", 32768, true),
- "Some shell-script that will be run before a build starts.");
-
- props.add (new TextPropertyComponent (getPostBuildScriptValue(), "Post-build shell script", 32768, true),
- "Some shell-script that will be run after a build completes.");
-
- props.add (new TextPropertyComponent (getIosDevelopmentTeamIDValue(), "Development Team ID", 10, false),
- "The Development Team ID to be used for setting up code-signing your iOS app. This is a ten-character "
- "string (for example, \"S7B6T5XJ2Q\") that describes the distribution certificate Apple issued to you. "
- "You can find this string in the OS X app Keychain Access under \"Certificates\".");
- }
-
- bool launchProject() override
- {
- #if JUCE_MAC
- return getProjectBundle().startAsProcess();
- #else
- return false;
- #endif
- }
-
- bool canLaunchProject() override
- {
- #if JUCE_MAC
- return true;
- #else
- return false;
- #endif
- }
-
- //==============================================================================
- void create (const OwnedArray&) const override
- {
- for (auto& target : targets)
- if (target->xcodeCreatePList)
- target->infoPlistFile = getTargetFolder().getChildFile (target->getInfoPlistName());
-
- menuNibFile = getTargetFolder().getChildFile ("RecentFilesMenuTemplate.nib");
-
- createIconFile();
-
- File projectBundle (getProjectBundle());
- createDirectoryOrThrow (projectBundle);
-
- createObjects();
-
- File projectFile (projectBundle.getChildFile ("project.pbxproj"));
-
- {
- MemoryOutputStream mo;
- writeProjectFile (mo);
- overwriteFileIfDifferentOrThrow (projectFile, mo);
- }
-
- writeInfoPlistFiles();
-
- // Deleting the .rsrc files can be needed to force Xcode to update the version number.
- deleteRsrcFiles();
-
- if (! ProjucerApplication::getApp().isRunningCommandLine)
- {
- // Workaround for a bug where Xcode thinks the project is invalid if opened immedietely
- // after writing
- Thread::sleep (2000);
- }
- }
-
- //==============================================================================
- void addPlatformSpecificSettingsForProjectType (const ProjectType& type) override
- {
- if (type.isGUIApplication())
- targets.add (new Target (Target::GUIApp, *this));
-
- else if (type.isCommandLineApp())
- targets.add (new Target (Target::ConsoleApp, *this));
-
- else if (type.isStaticLibrary())
- targets.add (new Target (Target::StaticLibrary, *this));
-
- else if (type.isDynamicLibrary())
- targets.add (new Target (Target::DynamicLibrary, *this));
-
- else if (type.isAudioPlugin())
- {
- if (project.shouldBuildVST().getValue() && supportsVST())
- targets.add (new Target (Target::VSTPlugIn, *this));
-
- if (project.shouldBuildVST3().getValue() && supportsVST3())
- targets.add (new Target (Target::VST3PlugIn, *this));
-
- if (project.shouldBuildAU().getValue() && supportsAU())
- targets.add (new Target (Target::AudioUnitPlugIn, *this));
-
- if (project.shouldBuildAUv3().getValue())
- targets.add (new Target (Target::AudioUnitv3PlugIn, *this));
-
- if (project.shouldBuildAAX().getValue() && supportsAAX())
- targets.add (new Target (Target::AAXPlugIn, *this));
-
- if (project.shouldBuildStandalone().getValue())
- targets.add (new Target (Target::StandalonePlugIn, *this));
-
- if (project.shouldBuildRTAS().getValue() && supportsRTAS())
- {
- targets.add (new Target (Target::RTASPlugIn, *this));
- addRTASPluginSettings();
- }
-
- if (targets.size() > 0)
- targets.add (new Target (Target::SharedCodeTarget, *this));
- }
-
- if (targets.size() > 1)
- targets.insert (0, new Target (Target::AggregateTarget, *this));
-
- // If you hit this assert, you tried to generate a project for an exporter
- // that does not support any of your targets!
- jassert (targets.size() > 0);
- }
-
- void updateDeprecatedProjectSettingsInteractively() override
- {
- if (hasInvalidPostBuildScript())
- {
- String alertWindowText = iOS ? "Your Xcode (iOS) Exporter settings use an invalid post-build script. Click 'Update' to remove it."
- : "Your Xcode (OSX) Exporter settings use a pre-JUCE 4.2 post-build script to move the plug-in binaries to their plug-in install folders.\n\n"
- "Since JUCE 4.2, this is instead done using \"AU/VST/VST2/AAX/RTAS Binary Location\" in the Xcode (OS X) configuration settings.\n\n"
- "Click 'Update' to remove the script (otherwise your plug-in may not compile correctly).";
-
- if (AlertWindow::showOkCancelBox (AlertWindow::WarningIcon,
- "Project settings: " + project.getDocumentTitle(),
- alertWindowText, "Update", "Cancel", nullptr, nullptr))
- getPostBuildScriptValue() = var();
- }
- }
-
- bool hasInvalidPostBuildScript() const
- {
- // check whether the script is identical to the old one that the Introjucer used to auto-generate
- return (MD5 (getPostBuildScript().toUTF8()).toHexString() == "265ac212a7e734c5bbd6150e1eae18a1");
- }
-
-protected:
- //==============================================================================
- class XcodeBuildConfiguration : public BuildConfiguration
- {
- public:
- XcodeBuildConfiguration (Project& p, const ValueTree& t, const bool isIOS, const ProjectExporter& e)
- : BuildConfiguration (p, t, e),
- iOS (isIOS),
- osxSDKVersion (config, Ids::osxSDK, nullptr, "default"),
- osxDeploymentTarget (config, Ids::osxCompatibility, nullptr, "default"),
- iosDeploymentTarget (config, Ids::iosCompatibility, nullptr, "default"),
- osxArchitecture (config, Ids::osxArchitecture, nullptr, "default"),
- customXcodeFlags (config, Ids::customXcodeFlags, nullptr),
- cppLanguageStandard (config, Ids::cppLanguageStandard, nullptr),
- cppStandardLibrary (config, Ids::cppLibType, nullptr),
- codeSignIdentity (config, Ids::codeSigningIdentity, nullptr, iOS ? "iPhone Developer" : "Mac Developer"),
- fastMathEnabled (config, Ids::fastMath, nullptr),
- linkTimeOptimisationEnabled (config, Ids::linkTimeOptimisation, nullptr),
- vstBinaryLocation (config, Ids::xcodeVstBinaryLocation, nullptr, "$(HOME)/Library/Audio/Plug-Ins/VST/"),
- vst3BinaryLocation (config, Ids::xcodeVst3BinaryLocation, nullptr, "$(HOME)/Library/Audio/Plug-Ins/VST3/"),
- auBinaryLocation (config, Ids::xcodeAudioUnitBinaryLocation, nullptr, "$(HOME)/Library/Audio/Plug-Ins/Components/"),
- rtasBinaryLocation (config, Ids::xcodeRtasBinaryLocation, nullptr, "/Library/Application Support/Avid/Audio/Plug-Ins/"),
- aaxBinaryLocation (config, Ids::xcodeAaxBinaryLocation, nullptr, "/Library/Application Support/Digidesign/Plug-Ins/")
- {
- }
-
- //==========================================================================
- bool iOS;
-
- CachedValue osxSDKVersion, osxDeploymentTarget, iosDeploymentTarget, osxArchitecture,
- customXcodeFlags, cppLanguageStandard, cppStandardLibrary, codeSignIdentity;
- CachedValue fastMathEnabled, linkTimeOptimisationEnabled;
- CachedValue vstBinaryLocation, vst3BinaryLocation, auBinaryLocation, rtasBinaryLocation, aaxBinaryLocation;
-
- //==========================================================================
- var getDefaultOptimisationLevel() const override { return var ((int) (isDebug() ? gccO0 : gccO3)); }
-
- void createConfigProperties (PropertyListBuilder& props) override
- {
- addXcodePluginInstallPathProperties (props);
- addGCCOptimisationProperty (props);
-
- if (iOS)
- {
- const char* iosVersions[] = { "Use Default", "7.0", "7.1", "8.0", "8.1", "8.2", "8.3", "8.4", "9.0", "9.1", "9.2", "9.3", 0 };
- const char* iosVersionValues[] = { osxVersionDefault, "7.0", "7.1", "8.0", "8.1", "8.2", "8.3", "8.4", "9.0", "9.1", "9.2", "9.3", 0 };
-
- props.add (new ChoicePropertyComponent (iosDeploymentTarget.getPropertyAsValue(), "iOS Deployment Target",
- StringArray (iosVersions), Array (iosVersionValues)),
- "The minimum version of iOS that the target binary will run on.");
- }
- else
- {
- StringArray sdkVersionNames, osxVersionNames;
- Array versionValues;
-
- sdkVersionNames.add ("Use Default");
- osxVersionNames.add ("Use Default");
- versionValues.add (osxVersionDefault);
-
- for (int ver = oldestSDKVersion; ver <= currentSDKVersion; ++ver)
- {
- sdkVersionNames.add (getSDKName (ver));
- osxVersionNames.add (getOSXVersionName (ver));
- versionValues.add (getSDKName (ver));
- }
-
- props.add (new ChoicePropertyComponent (osxSDKVersion.getPropertyAsValue(), "OSX Base SDK Version", sdkVersionNames, versionValues),
- "The version of OSX to link against in the XCode build.");
-
- props.add (new ChoicePropertyComponent (osxDeploymentTarget.getPropertyAsValue(), "OSX Deployment Target", osxVersionNames, versionValues),
- "The minimum version of OSX that the target binary will be compatible with.");
-
- const char* osxArch[] = { "Use Default", "Native architecture of build machine",
- "Universal Binary (32-bit)", "Universal Binary (32/64-bit)", "64-bit Intel", 0 };
- const char* osxArchValues[] = { osxArch_Default, osxArch_Native, osxArch_32BitUniversal,
- osxArch_64BitUniversal, osxArch_64Bit, 0 };
-
- props.add (new ChoicePropertyComponent (osxArchitecture.getPropertyAsValue(), "OSX Architecture",
- StringArray (osxArch), Array (osxArchValues)),
- "The type of OSX binary that will be produced.");
- }
-
- props.add (new TextPropertyComponent (customXcodeFlags.getPropertyAsValue(), "Custom Xcode flags", 8192, false),
- "A comma-separated list of custom Xcode setting flags which will be appended to the list of generated flags, "
- "e.g. MACOSX_DEPLOYMENT_TARGET_i386 = 10.5, VALID_ARCHS = \"ppc i386 x86_64\"");
-
- const char* cppLanguageStandardNames[] = { "Use Default", "C++98", "GNU++98", "C++11", "GNU++11", "C++14", "GNU++14", nullptr };
- Array cppLanguageStandardValues;
- cppLanguageStandardValues.add (var::null);
- cppLanguageStandardValues.add ("c++98");
- cppLanguageStandardValues.add ("gnu++98");
- cppLanguageStandardValues.add ("c++11");
- cppLanguageStandardValues.add ("gnu++11");
- cppLanguageStandardValues.add ("c++14");
- cppLanguageStandardValues.add ("gnu++14");
-
- props.add (new ChoicePropertyComponent (cppLanguageStandard.getPropertyAsValue(), "C++ Language Standard",
- StringArray (cppLanguageStandardNames), cppLanguageStandardValues),
- "The standard of the C++ language that will be used for compilation.");
-
- const char* cppLibNames[] = { "Use Default", "LLVM libc++", "GNU libstdc++", nullptr };
- Array cppLibValues;
- cppLibValues.add (var::null);
- cppLibValues.add ("libc++");
- cppLibValues.add ("libstdc++");
-
- props.add (new ChoicePropertyComponent (cppStandardLibrary.getPropertyAsValue(), "C++ Library", StringArray (cppLibNames), cppLibValues),
- "The type of C++ std lib that will be linked.");
-
- props.add (new TextWithDefaultPropertyComponent (codeSignIdentity, "Code-signing Identity", 1024),
- "The name of a code-signing identity for Xcode to apply.");
-
- props.add (new BooleanPropertyComponent (fastMathEnabled.getPropertyAsValue(), "Relax IEEE compliance", "Enabled"),
- "Enable this to use FAST_MATH non-IEEE mode. (Warning: this can have unexpected results!)");
-
- props.add (new BooleanPropertyComponent (linkTimeOptimisationEnabled.getPropertyAsValue(), "Link-Time Optimisation", "Enabled"),
- "Enable this to perform link-time code generation. This is recommended for release builds.");
- }
-
- private:
- //==========================================================================
- void addXcodePluginInstallPathProperties (PropertyListBuilder& props)
- {
- if (project.shouldBuildVST().getValue())
- props.add (new TextWithDefaultPropertyComponent (vstBinaryLocation, "VST Binary location", 1024),
- "The folder in which the compiled VST binary should be placed.");
-
- if (project.shouldBuildVST3().getValue())
- props.add (new TextWithDefaultPropertyComponent (vst3BinaryLocation, "VST3 Binary location", 1024),
- "The folder in which the compiled VST3 binary should be placed.");
-
- if (project.shouldBuildAU().getValue())
- props.add (new TextWithDefaultPropertyComponent (auBinaryLocation, "AU Binary location", 1024),
- "The folder in which the compiled AU binary should be placed.");
-
- if (project.shouldBuildRTAS().getValue())
- props.add (new TextWithDefaultPropertyComponent (rtasBinaryLocation, "RTAS Binary location", 1024),
- "The folder in which the compiled RTAS binary should be placed.");
-
- if (project.shouldBuildAAX().getValue())
- props.add (new TextWithDefaultPropertyComponent (aaxBinaryLocation, "AAX Binary location", 1024),
- "The folder in which the compiled AAX binary should be placed.");
- }
- };
-
- BuildConfiguration::Ptr createBuildConfig (const ValueTree& v) const override
- {
- return new XcodeBuildConfiguration (project, v, iOS, *this);
- }
-
-public:
- //==============================================================================
- /* The numbers for these enum values are defined by Xcode for the different
- possible destinations of a "copy files" post-build step.
- */
- enum XcodeCopyFilesDestinationIDs
- {
- kWrapperFolder = 1,
- kExecutablesFolder = 6,
- kResourcesFolder = 7,
- kFrameworksFolder = 10,
- kSharedFrameworksFolder = 11,
- kSharedSupportFolder = 12,
- kPluginsFolder = 13,
- kJavaResourcesFolder = 15,
- kXPCServicesFolder = 16
- };
-
- //==============================================================================
- struct Target
- {
- enum Type
- {
- GUIApp = 0,
- ConsoleApp = 1,
- StaticLibrary = 2,
- DynamicLibrary = 3,
-
- VSTPlugIn = 10,
- VST3PlugIn = 11,
- AAXPlugIn = 12,
- RTASPlugIn = 13,
- AudioUnitPlugIn = 14,
- AudioUnitv3PlugIn = 15,
- StandalonePlugIn = 16,
-
- SharedCodeTarget = 20, // internal
- AggregateTarget = 21,
-
- unspecified = 30
- };
-
- //==============================================================================
- Target (Type targetType, const XCodeProjectExporter& exporter)
- : type (targetType),
- owner (exporter)
- {
- switch (type)
- {
- case GUIApp:
- xcodeIsBundle = false;
- xcodeIsExecutable = true;
- xcodeCreatePList = true;
- xcodePackageType = "APPL";
- xcodeBundleSignature = "????";
- xcodeFileType = "wrapper.application";
- xcodeBundleExtension = ".app";
- xcodeProductType = "com.apple.product-type.application";
- xcodeCopyToProductInstallPathAfterBuild = false;
- break;
-
- case ConsoleApp:
- xcodeIsBundle = false;
- xcodeIsExecutable = true;
- xcodeCreatePList = false;
- xcodeFileType = "compiled.mach-o.executable";
- xcodeBundleExtension = String::empty;
- xcodeProductType = "com.apple.product-type.tool";
- xcodeCopyToProductInstallPathAfterBuild = false;
- break;
-
- case StaticLibrary:
- xcodeIsBundle = false;
- xcodeIsExecutable = false;
- xcodeCreatePList = false;
- xcodeFileType = "archive.ar";
- xcodeProductType = "com.apple.product-type.library.static";
- xcodeCopyToProductInstallPathAfterBuild = false;
- break;
-
- case DynamicLibrary:
- xcodeIsBundle = false;
- xcodeIsExecutable = false;
- xcodeCreatePList = false;
- xcodeFileType = "compiled.mach-o.dylib";
- xcodeProductType = "com.apple.product-type.library.dynamic";
- xcodeBundleExtension = ".dylib";
- xcodeCopyToProductInstallPathAfterBuild = false;
-
- break;
-
- case VSTPlugIn:
- xcodeIsBundle = true;
- xcodeIsExecutable = false;
- xcodeCreatePList = true;
- xcodePackageType = "BNDL";
- xcodeBundleSignature = "????";
- xcodeFileType = "wrapper.cfbundle";
- xcodeBundleExtension = ".vst";
- xcodeProductType = "com.apple.product-type.bundle";
- xcodeCopyToProductInstallPathAfterBuild = true;
-
- break;
-
- case VST3PlugIn:
- xcodeIsBundle = true;
- xcodeIsExecutable = false;
- xcodeCreatePList = true;
- xcodePackageType = "BNDL";
- xcodeBundleSignature = "????";
- xcodeFileType = "wrapper.cfbundle";
- xcodeBundleExtension = ".vst3";
- xcodeProductType = "com.apple.product-type.bundle";
- xcodeCopyToProductInstallPathAfterBuild = true;
-
- break;
-
- case AudioUnitPlugIn:
- xcodeIsBundle = true;
- xcodeIsExecutable = false;
- xcodeCreatePList = true;
- xcodePackageType = "BNDL";
- xcodeBundleSignature = "????";
- xcodeFileType = "wrapper.cfbundle";
- xcodeBundleExtension = ".component";
- xcodeProductType = "com.apple.product-type.bundle";
- xcodeCopyToProductInstallPathAfterBuild = true;
-
- addExtraAudioUnitTargetSettings();
- break;
-
- case StandalonePlugIn:
- xcodeIsBundle = false;
- xcodeIsExecutable = true;
- xcodeCreatePList = true;
- xcodePackageType = "APPL";
- xcodeBundleSignature = "????";
- xcodeCreatePList = true;
- xcodeFileType = "wrapper.application";
- xcodeBundleExtension = ".app";
- xcodeProductType = "com.apple.product-type.application";
- xcodeCopyToProductInstallPathAfterBuild = false;
- break;
-
- case AudioUnitv3PlugIn:
- xcodeIsBundle = false;
- xcodeIsExecutable = false;
- xcodeCreatePList = true;
- xcodePackageType = "XPC!";
- xcodeBundleSignature = "????";
- xcodeFileType = "wrapper.app-extension";
- xcodeBundleExtension = ".appex";
- xcodeBundleIDSubPath = "AUv3";
- xcodeProductType = "com.apple.product-type.app-extension";
- xcodeCopyToProductInstallPathAfterBuild = false;
-
- addExtraAudioUnitv3PlugInTargetSettings();
- break;
-
- case AAXPlugIn:
- xcodeIsBundle = true;
- xcodeIsExecutable = false;
- xcodeCreatePList = true;
- xcodePackageType = "TDMw";
- xcodeBundleSignature = "PTul";
- xcodeFileType = "wrapper.cfbundle";
- xcodeBundleExtension = ".aaxplugin";
- xcodeProductType = "com.apple.product-type.bundle";
- xcodeCopyToProductInstallPathAfterBuild = true;
-
- addExtraAAXTargetSettings();
- break;
-
- case RTASPlugIn:
- xcodeIsBundle = true;
- xcodeIsExecutable = false;
- xcodeCreatePList = true;
- xcodePackageType = "TDMw";
- xcodeBundleSignature = "PTul";
- xcodeFileType = "wrapper.cfbundle";
- xcodeBundleExtension = ".dpm";
- xcodeProductType = "com.apple.product-type.bundle";
- xcodeCopyToProductInstallPathAfterBuild = true;
-
- addExtraRTASTargetSettings();
- break;
-
- case SharedCodeTarget:
- xcodeIsBundle = false;
- xcodeIsExecutable = false;
- xcodeCreatePList = true;
- xcodePackageType = "FMWK";
- xcodeBundleSignature = "????";
- xcodeFileType = "wrapper.framework";
- xcodeBundleExtension = ".framework";
- xcodeProductType = "com.apple.product-type.framework";
- xcodeBundleIDSubPath = "Framework";
- xcodeCopyToProductInstallPathAfterBuild = false;
-
- break;
-
- case AggregateTarget:
- xcodeIsBundle = false;
- xcodeIsExecutable = false;
- xcodeCreatePList = false;
- xcodeCopyToProductInstallPathAfterBuild = false;
- break;
-
- default:
- // unknown target type!
- jassertfalse;
- break;
- }
- }
-
- const char* getName() const noexcept
- {
- switch (type)
- {
- case GUIApp: return "App";
- case ConsoleApp: return "ConsoleApp";
- case StaticLibrary: return "Static Library";
- case DynamicLibrary: return "Dynamic Library";
- case VSTPlugIn: return "VST";
- case VST3PlugIn: return "VST3";
- case AudioUnitPlugIn: return "AU";
- case StandalonePlugIn: return "AUv3 Standalone";
- case AudioUnitv3PlugIn: return "AUv3 AppExtension";
- case AAXPlugIn: return "AAX";
- case RTASPlugIn: return "RTAS";
- case SharedCodeTarget: return "Shared Code";
- case AggregateTarget: return "All";
- default: return "undefined";
- }
- }
-
- String getXCodeSchemeName() const
- {
- return owner.projectName + " (" + getName() + ")";
- }
-
- bool shouldBuildVST() const { return owner.supportsVST() && owner.project.shouldBuildVST().getValue() && (type == SharedCodeTarget || type == VSTPlugIn); }
- bool shouldBuildVST3() const { return owner.supportsVST3() && owner.project.shouldBuildVST3().getValue() && (type == SharedCodeTarget || type == VST3PlugIn); }
- bool shouldBuildAAX() const { return owner.supportsAAX() && owner.project.shouldBuildAAX().getValue() && (type == SharedCodeTarget || type == AAXPlugIn); }
- bool shouldBuildRTAS() const { return owner.supportsRTAS() && owner.project.shouldBuildRTAS().getValue() && (type == SharedCodeTarget || type == RTASPlugIn); }
- bool shouldBuildAU() const { return owner.supportsAU() && owner.project.shouldBuildAU().getValue() && (type == SharedCodeTarget || type == AudioUnitPlugIn); }
- bool shouldBuildAUv3() const { return owner.supportsAUv3() && owner.project.shouldBuildAUv3().getValue() && (type == SharedCodeTarget || type == AudioUnitv3PlugIn); }
- bool shouldBuildStandalone() const { return owner.project.shouldBuildStandalone().getValue() && (type == SharedCodeTarget || type == StandalonePlugIn); }
-
- String getID() const
- {
- return owner.createID (String ("__target") + getName());
- }
-
- String getInfoPlistName() const
- {
- return String ("Info-") + String (getName()).replace (" ", "_") + String (".plist");
- }
-
- String xcodePackageType, xcodeBundleSignature, xcodeBundleExtension;
- String xcodeProductType, xcodeFileType;
- String xcodeOtherRezFlags, xcodeExcludedFiles64Bit, xcodeBundleIDSubPath;
- bool xcodeIsBundle, xcodeCreatePList, xcodeIsExecutable, xcodeCopyToProductInstallPathAfterBuild;
- StringArray xcodeFrameworks, xcodeLibs;
- Type type;
- Array xcodeExtraPListEntries;
- Array xcodeExtraLibrariesDebug, xcodeExtraLibrariesRelease;
-
- StringArray frameworkIDs, buildPhaseIDs, configIDs, sourceIDs, rezFileIDs;
- String dependencyID, mainBuildProductID;
- File infoPlistFile;
-
- //==============================================================================
- void addMainBuildProduct() const
- {
- jassert (xcodeFileType.isNotEmpty());
- jassert (xcodeBundleExtension.isEmpty() || xcodeBundleExtension.startsWithChar ('.'));
-
- if (ProjectExporter::BuildConfiguration::Ptr config = owner.getConfiguration(0))
- {
- String productName (owner.replacePreprocessorTokens (*config, config->getTargetBinaryNameString()));
-
- if (xcodeFileType == "archive.ar")
- productName = getLibbedFilename (productName);
- else
- productName += xcodeBundleExtension;
-
- addBuildProduct (xcodeFileType, productName);
- }
- }
-
- //==============================================================================
- void addBuildProduct (const String& fileType, const String& binaryName) const
- {
- ValueTree* v = new ValueTree (owner.createID (String ("__productFileID") + getName()));
- v->setProperty ("isa", "PBXFileReference", nullptr);
- v->setProperty ("explicitFileType", fileType, nullptr);
- v->setProperty ("includeInIndex", (int) 0, nullptr);
- v->setProperty ("path", sanitisePath (binaryName), nullptr);
- v->setProperty ("sourceTree", "BUILT_PRODUCTS_DIR", nullptr);
- owner.pbxFileReferences.add (v);
- }
-
- //==============================================================================
- void addDependency()
- {
- jassert (dependencyID.isEmpty());
-
- dependencyID = owner.createID (String ("__dependency") + getName());
- ValueTree* const v = new ValueTree (dependencyID);
-
- v->setProperty ("isa", "PBXTargetDependency", nullptr);
- v->setProperty ("target", getID(), nullptr);
-
- owner.misc.add (v);
- }
-
- String getDependencyID() const
- {
- jassert (dependencyID.isNotEmpty());
-
- return dependencyID;
- }
-
- //==============================================================================
- void addTargetConfig (const String& configName, const StringArray& buildSettings)
- {
- String configID = owner.createID (String ("targetconfigid_") + getName() + String ("_") + configName);
-
- ValueTree* v = new ValueTree (configID);
- v->setProperty ("isa", "XCBuildConfiguration", nullptr);
- v->setProperty ("buildSettings", indentBracedList (buildSettings), nullptr);
- v->setProperty (Ids::name, configName, nullptr);
-
- configIDs.add (configID);
- owner.targetConfigs.add (v);
- }
-
- //==============================================================================
- String getTargetAttributes() const
- {
- String attributes;
-
- attributes << getID() << " = { ";
-
- String developmentTeamID = owner.getIosDevelopmentTeamIDString();
- if (developmentTeamID.isNotEmpty())
- attributes << "DevelopmentTeam = " << developmentTeamID << "; ";
-
- const int inAppPurchasesEnabled = (owner.iOS && owner.isInAppPurchasesEnabled()) ? 1 : 0;
- const int sandboxEnabled = (type == Target::AudioUnitv3PlugIn ? 1 : 0);
-
- attributes << "SystemCapabilities = {";
- attributes << "com.apple.InAppPurchase = { enabled = " << inAppPurchasesEnabled << "; }; ";
- attributes << "com.apple.Sandbox = { enabled = " << sandboxEnabled << "; }; ";
- attributes << "}; };";
-
- return attributes;
- }
-
- //==============================================================================
- ValueTree& addBuildPhase (const String& buildPhaseType, const StringArray& fileIds, const StringRef humanReadableName = StringRef())
- {
- String buildPhaseName = buildPhaseType + String ("_") + getName() + String ("_") + (humanReadableName.isNotEmpty() ? String (humanReadableName) : String ("resbuildphase"));
- String buildPhaseId (owner.createID (buildPhaseName));
-
- int n = 0;
- while (buildPhaseIDs.contains (buildPhaseId))
- buildPhaseId = owner.createID (buildPhaseName + String (++n));
-
- buildPhaseIDs.add (buildPhaseId);
-
- ValueTree* v = new ValueTree (buildPhaseId);
- v->setProperty ("isa", buildPhaseType, nullptr);
- v->setProperty ("buildActionMask", "2147483647", nullptr);
- v->setProperty ("files", indentParenthesisedList (fileIds), nullptr);
- v->setProperty ("runOnlyForDeploymentPostprocessing", (int) 0, nullptr);
-
- if (humanReadableName.isNotEmpty())
- v->setProperty ("name", String (humanReadableName), nullptr);
-
- owner.misc.add (v);
- return *v;
- }
-
- //==============================================================================
- StringArray getTargetSettings (const XcodeBuildConfiguration& config) const
- {
- if (type == AggregateTarget)
- // the aggregate target should not specify any settings at all!
- // it just defines dependencies on the other targets.
- return StringArray();
-
- StringArray s;
-
- String bundleIdentifier = owner.project.getBundleIdentifier().toString();
- if (xcodeBundleIDSubPath.isNotEmpty())
- {
- StringArray bundleIdSegments = StringArray::fromTokens (bundleIdentifier, ".", StringRef());
-
- jassert (bundleIdSegments.size() > 0);
- bundleIdentifier += String (".") + bundleIdSegments[bundleIdSegments.size() - 1] + xcodeBundleIDSubPath;
- }
-
- s.add ("PRODUCT_BUNDLE_IDENTIFIER = " + bundleIdentifier);
-
- const String arch ((! owner.isiOS() && type == Target::AudioUnitv3PlugIn) ? osxArch_64Bit : config.osxArchitecture.get());
- if (arch == osxArch_Native) s.add ("ARCHS = \"$(NATIVE_ARCH_ACTUAL)\"");
- else if (arch == osxArch_32BitUniversal) s.add ("ARCHS = \"$(ARCHS_STANDARD_32_BIT)\"");
- else if (arch == osxArch_64BitUniversal) s.add ("ARCHS = \"$(ARCHS_STANDARD_32_64_BIT)\"");
- else if (arch == osxArch_64Bit) s.add ("ARCHS = \"$(ARCHS_STANDARD_64_BIT)\"");
-
- s.add ("HEADER_SEARCH_PATHS = " + owner.getHeaderSearchPaths (config));
- s.add ("GCC_OPTIMIZATION_LEVEL = " + config.getGCCOptimisationFlag());
-
- if (xcodeCreatePList)
- s.add ("INFOPLIST_FILE = " + infoPlistFile.getFileName());
-
- if (config.linkTimeOptimisationEnabled.get())
- s.add ("LLVM_LTO = YES");
-
- if (config.fastMathEnabled.get())
- s.add ("GCC_FAST_MATH = YES");
-
- const String extraFlags (owner.replacePreprocessorTokens (config, owner.getExtraCompilerFlagsString()).trim());
- if (extraFlags.isNotEmpty())
- s.add ("OTHER_CPLUSPLUSFLAGS = \"" + extraFlags + "\"");
-
- String installPath = getInstallPathForConfiguration (config);
-
- if (installPath.isNotEmpty())
- {
- s.add ("INSTALL_PATH = \"" + installPath + "\"");
-
- if (xcodeCopyToProductInstallPathAfterBuild)
- {
- s.add ("DEPLOYMENT_LOCATION = YES");
- s.add ("DSTROOT = /");
- }
- }
-
- if (xcodeIsBundle)
- {
- s.add ("LIBRARY_STYLE = Bundle");
- s.add ("WRAPPER_EXTENSION = " + xcodeBundleExtension.substring (1));
- s.add ("GENERATE_PKGINFO_FILE = YES");
- }
-
- if (xcodeOtherRezFlags.isNotEmpty())
- s.add ("OTHER_REZFLAGS = \"" + xcodeOtherRezFlags + "\"");
-
- String configurationBuildDir = "$(PROJECT_DIR)/build/$(CONFIGURATION)";
-
- if (config.getTargetBinaryRelativePathString().isNotEmpty())
- {
- // a target's position can either be defined via installPath + xcodeCopyToProductInstallPathAfterBuild
- // (= for audio plug-ins) or using a custom binary path (for everything else), but not both (= conflict!)
- jassert (! xcodeCopyToProductInstallPathAfterBuild);
-
- RelativePath binaryPath (config.getTargetBinaryRelativePathString(), RelativePath::projectFolder);
- configurationBuildDir = sanitisePath (binaryPath.rebased (owner.projectFolder, owner.getTargetFolder(), RelativePath::buildTargetFolder)
- .toUnixStyle());
- }
-
- s.add ("CONFIGURATION_BUILD_DIR = " + addQuotesIfRequired (configurationBuildDir));
-
- String gccVersion ("com.apple.compilers.llvm.clang.1_0");
-
- if (owner.iOS)
- {
- s.add ("ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon");
- s.add ("ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage");
- }
- else
- {
- const String sdk (config.osxSDKVersion.get());
- const String sdkCompat (config.osxDeploymentTarget.get());
-
- for (int ver = oldestSDKVersion; ver <= currentSDKVersion; ++ver)
- {
- if (sdk == getSDKName (ver)) s.add ("SDKROOT = macosx10." + String (ver));
- if (sdkCompat == getSDKName (ver)) s.add ("MACOSX_DEPLOYMENT_TARGET = 10." + String (ver));
- }
-
- s.add ("MACOSX_DEPLOYMENT_TARGET_ppc = 10.4");
- s.add ("SDKROOT_ppc = macosx10.5");
-
- if (xcodeExcludedFiles64Bit.isNotEmpty())
- {
- s.add ("EXCLUDED_SOURCE_FILE_NAMES = \"$(EXCLUDED_SOURCE_FILE_NAMES_$(CURRENT_ARCH))\"");
- s.add ("EXCLUDED_SOURCE_FILE_NAMES_x86_64 = " + xcodeExcludedFiles64Bit);
- }
- }
-
- s.add ("GCC_VERSION = " + gccVersion);
- s.add ("CLANG_CXX_LANGUAGE_STANDARD = \"c++0x\"");
- s.add ("CLANG_LINK_OBJC_RUNTIME = NO");
-
- if (! config.codeSignIdentity.isUsingDefault())
- s.add ("CODE_SIGN_IDENTITY = " + config.codeSignIdentity.get().quoted());
-
- if (config.cppLanguageStandard.get().isNotEmpty())
- s.add ("CLANG_CXX_LANGUAGE_STANDARD = " + config.cppLanguageStandard.get().quoted());
-
- if (config.cppStandardLibrary.get().isNotEmpty())
- s.add ("CLANG_CXX_LIBRARY = " + config.cppStandardLibrary.get().quoted());
-
- s.add ("COMBINE_HIDPI_IMAGES = YES");
-
- {
- StringArray linkerFlags, librarySearchPaths;
- getLinkerFlags (config, linkerFlags, librarySearchPaths);
-
- if (linkerFlags.size() > 0)
- s.add ("OTHER_LDFLAGS = \"" + linkerFlags.joinIntoString (" ") + "\"");
-
- librarySearchPaths.addArray (config.getLibrarySearchPaths());
- librarySearchPaths = getCleanedStringArray (librarySearchPaths);
-
- if (librarySearchPaths.size() > 0)
- {
- String libPaths ("LIBRARY_SEARCH_PATHS = (\"$(inherited)\"");
-
- for (int i = 0; i < librarySearchPaths.size(); ++i)
- libPaths += ", \"\\\"" + librarySearchPaths[i] + "\\\"\"";
-
- s.add (libPaths + ")");
- }
- }
-
- StringPairArray defines;
-
- if (config.isDebug())
- {
- defines.set ("_DEBUG", "1");
- defines.set ("DEBUG", "1");
- s.add ("COPY_PHASE_STRIP = NO");
- s.add ("GCC_DYNAMIC_NO_PIC = NO");
- }
- else
- {
- defines.set ("_NDEBUG", "1");
- defines.set ("NDEBUG", "1");
- s.add ("GCC_GENERATE_DEBUGGING_SYMBOLS = NO");
- s.add ("GCC_SYMBOLS_PRIVATE_EXTERN = YES");
- s.add ("DEAD_CODE_STRIPPING = YES");
- }
-
- if (type == Target::SharedCodeTarget)
- defines.set ("JUCE_SHARED_CODE", "1");
-
- if (owner.project.getProjectType().isAudioPlugin())
- {
- if (type == Target::SharedCodeTarget)
- {
- s.add ("DYLIB_INSTALL_NAME_BASE = \"@rpath\"");
- s.add ("LD_DYLIB_INSTALL_NAME = \"$(DYLIB_INSTALL_NAME_BASE:standardizepath)/$(EXECUTABLE_PATH)\"");
- }
- else if (type == Target::AudioUnitv3PlugIn)
- {
- String frameworksFolderPath = (owner.isiOS() ? "@loader_path/../../Frameworks" : "@loader_path/../../../../Frameworks");
- s.add (String ("LD_RUNPATH_SEARCH_PATHS = \"") + frameworksFolderPath + String ("\""));
-
- if (! owner.isiOS())
- s.add (String ("CODE_SIGN_ENTITLEMENTS = \"") + owner.getEntitlementsFileName() + String ("\""));
- }
- else
- {
- String executableType = xcodeIsExecutable ? "@executable_path" : "@loader_path";
- String frameworksFolderPath = executableType + (owner.isiOS() ? "/Frameworks" : "/../Frameworks");
- s.add (String ("LD_RUNPATH_SEARCH_PATHS = \"") + frameworksFolderPath + String ("\""));
- }
- }
-
- if (owner.project.getProjectType().isAudioPlugin())
- {
- defines.set ("JucePlugin_Build_VST", (shouldBuildVST() ? "1" : "0"));
- defines.set ("JucePlugin_Build_VST3", (shouldBuildVST3() ? "1" : "0"));
- defines.set ("JucePlugin_Build_AU", (shouldBuildAU() ? "1" : "0"));
- defines.set ("JucePlugin_Build_AUv3", (shouldBuildAUv3() ? "1" : "0"));
- defines.set ("JucePlugin_Build_RTAS", (shouldBuildRTAS() ? "1" : "0"));
- defines.set ("JucePlugin_Build_AAX", (shouldBuildAAX() ? "1" : "0"));
- defines.set ("JucePlugin_Build_Standalone", (shouldBuildStandalone() ? "1" : "0"));
- }
-
- defines = mergePreprocessorDefs (defines, owner.getAllPreprocessorDefs (config));
-
- StringArray defsList;
-
- for (int i = 0; i < defines.size(); ++i)
- {
- String def (defines.getAllKeys()[i]);
- const String value (defines.getAllValues()[i]);
- if (value.isNotEmpty())
- def << "=" << value.replace ("\"", "\\\"");
-
- defsList.add ("\"" + def + "\"");
- }
-
- s.add ("GCC_PREPROCESSOR_DEFINITIONS = " + indentParenthesisedList (defsList));
-
- s.addTokens (config.customXcodeFlags.get(), ",", "\"'");
-
- return getCleanedStringArray (s);
- }
-
- String getInstallPathForConfiguration (const XcodeBuildConfiguration& config) const
- {
- switch (type)
- {
- case GUIApp: return "$(HOME)/Applications";
- case ConsoleApp: return "/usr/bin";
- case VSTPlugIn: return config.vstBinaryLocation.get();
- case VST3PlugIn: return config.vst3BinaryLocation.get();
- case AudioUnitPlugIn: return config.auBinaryLocation.get();
- case RTASPlugIn: return config.rtasBinaryLocation.get();
- case AAXPlugIn: return config.aaxBinaryLocation.get();
- case SharedCodeTarget: return owner.isiOS() ? "@executable_path/Frameworks" : "@executable_path/../Frameworks";
- default: return String();
- }
- }
-
- //==============================================================================
- void getLinkerFlags (const BuildConfiguration& config, StringArray& flags, StringArray& librarySearchPaths) const
- {
- if (xcodeIsBundle)
- flags.add ("-bundle");
-
- const Array& extraLibs = config.isDebug() ? xcodeExtraLibrariesDebug
- : xcodeExtraLibrariesRelease;
-
- for (int i = 0; i < extraLibs.size(); ++i)
- owner.getLinkerFlagsForStaticLibrary (extraLibs.getReference(i), flags, librarySearchPaths);
-
- flags.add (owner.replacePreprocessorTokens (config, owner.getExtraLinkerFlagsString()));
- flags.add (owner.getExternalLibraryFlags (config));
-
- StringArray libs (owner.xcodeLibs);
- libs.addArray (xcodeLibs);
-
- for (int i = 0; i < libs.size(); ++i)
- flags.add (getLinkerFlagForLib (libs[i]));
-
- flags = getCleanedStringArray (flags);
- }
-
- //========================================================================== c
- void writeInfoPlistFile() const
- {
- if (! xcodeCreatePList)
- return;
-
- ScopedPointer plist (XmlDocument::parse (owner.getPListToMergeString()));
-
- if (plist == nullptr || ! plist->hasTagName ("plist"))
- plist = new XmlElement ("plist");
-
- XmlElement* dict = plist->getChildByName ("dict");
-
- if (dict == nullptr)
- dict = plist->createNewChildElement ("dict");
-
- if (owner.iOS)
- {
- addPlistDictionaryKeyBool (dict, "LSRequiresIPhoneOS", true);
-
- if (type != AudioUnitv3PlugIn)
- addPlistDictionaryKeyBool (dict, "UIViewControllerBasedStatusBarAppearance", false);
- }
-
- addPlistDictionaryKey (dict, "CFBundleExecutable", "${EXECUTABLE_NAME}");
-
- if (! owner.iOS) // (NB: on iOS this causes error ITMS-90032 during publishing)
- addPlistDictionaryKey (dict, "CFBundleIconFile", owner.iconFile.exists() ? owner.iconFile.getFileName() : String());
-
- addPlistDictionaryKey (dict, "CFBundleIdentifier", "$(PRODUCT_BUNDLE_IDENTIFIER)");
- addPlistDictionaryKey (dict, "CFBundleName", owner.projectName);
-
- // needed by NSExtension on iOS
- addPlistDictionaryKey (dict, "CFBundleDisplayName", owner.projectName);
- addPlistDictionaryKey (dict, "CFBundlePackageType", xcodePackageType);
- addPlistDictionaryKey (dict, "CFBundleSignature", xcodeBundleSignature);
- addPlistDictionaryKey (dict, "CFBundleShortVersionString", owner.project.getVersionString());
- addPlistDictionaryKey (dict, "CFBundleVersion", owner.project.getVersionString());
- addPlistDictionaryKey (dict, "NSHumanReadableCopyright", owner.project.getCompanyName().toString());
- addPlistDictionaryKeyBool (dict, "NSHighResolutionCapable", true);
-
- StringArray documentExtensions;
- documentExtensions.addTokens (replacePreprocessorDefs (owner.getAllPreprocessorDefs(), owner.settings ["documentExtensions"]),
- ",", StringRef());
- documentExtensions.trim();
- documentExtensions.removeEmptyStrings (true);
-
- if (documentExtensions.size() > 0 && type != AudioUnitv3PlugIn)
- {
- dict->createNewChildElement ("key")->addTextElement ("CFBundleDocumentTypes");
- XmlElement* dict2 = dict->createNewChildElement ("array")->createNewChildElement ("dict");
- XmlElement* arrayTag = nullptr;
-
- for (int i = 0; i < documentExtensions.size(); ++i)
- {
- String ex (documentExtensions[i]);
- if (ex.startsWithChar ('.'))
- ex = ex.substring (1);
-
- if (arrayTag == nullptr)
- {
- dict2->createNewChildElement ("key")->addTextElement ("CFBundleTypeExtensions");
- arrayTag = dict2->createNewChildElement ("array");
-
- addPlistDictionaryKey (dict2, "CFBundleTypeName", ex);
- addPlistDictionaryKey (dict2, "CFBundleTypeRole", "Editor");
- addPlistDictionaryKey (dict2, "CFBundleTypeIconFile", "Icon");
- addPlistDictionaryKey (dict2, "NSPersistentStoreTypeKey", "XML");
- }
-
- arrayTag->createNewChildElement ("string")->addTextElement (ex);
- }
- }
-
- if (owner.settings ["UIFileSharingEnabled"] && type != AudioUnitv3PlugIn)
- addPlistDictionaryKeyBool (dict, "UIFileSharingEnabled", true);
-
- if (owner.settings ["UIStatusBarHidden"] && type != AudioUnitv3PlugIn)
- addPlistDictionaryKeyBool (dict, "UIStatusBarHidden", true);
-
- if (owner.iOS && type != AudioUnitv3PlugIn)
- {
- // Forcing full screen disables the split screen feature and prevents error ITMS-90475
- addPlistDictionaryKeyBool (dict, "UIRequiresFullScreen", true);
- addPlistDictionaryKeyBool (dict, "UIStatusBarHidden", true);
-
- addIosScreenOrientations (dict);
- addIosBackgroundModes (dict);
- }
-
- for (int i = 0; i < xcodeExtraPListEntries.size(); ++i)
- dict->addChildElement (new XmlElement (xcodeExtraPListEntries.getReference(i)));
-
- MemoryOutputStream mo;
- plist->writeToStream (mo, "");
-
- overwriteFileIfDifferentOrThrow (infoPlistFile, mo);
- }
-
- //==============================================================================
- void addIosScreenOrientations (XmlElement* dict) const
- {
- String screenOrientation = owner.getScreenOrientationString();
- StringArray iOSOrientations;
-
- if (screenOrientation.contains ("portrait")) { iOSOrientations.add ("UIInterfaceOrientationPortrait"); }
- if (screenOrientation.contains ("landscape")) { iOSOrientations.add ("UIInterfaceOrientationLandscapeLeft"); iOSOrientations.add ("UIInterfaceOrientationLandscapeRight"); }
-
- addArrayToPlist (dict, "UISupportedInterfaceOrientations", iOSOrientations);
-
- }
-
- //==============================================================================
- void addIosBackgroundModes (XmlElement* dict) const
- {
- StringArray iosBackgroundModes;
- if (owner.isBackgroundAudioEnabled()) iosBackgroundModes.add ("audio");
- if (owner.isBackgroundBleEnabled()) iosBackgroundModes.add ("bluetooth-central");
-
- addArrayToPlist (dict, "UIBackgroundModes", iosBackgroundModes);
- }
-
- //==============================================================================
- static void addArrayToPlist (XmlElement* dict, String arrayKey, const StringArray& arrayElements)
- {
- dict->createNewChildElement ("key")->addTextElement (arrayKey);
- XmlElement* plistStringArray = dict->createNewChildElement ("array");
-
- for (int i = 0; i < arrayElements.size(); ++i)
- plistStringArray->createNewChildElement ("string")->addTextElement (arrayElements[i]);
- }
-
- //==============================================================================
- void addShellScriptBuildPhase (const String& phaseName, const String& script)
- {
- if (script.trim().isNotEmpty())
- {
- ValueTree& v = addBuildPhase ("PBXShellScriptBuildPhase", StringArray());
- v.setProperty (Ids::name, phaseName, nullptr);
- v.setProperty ("shellPath", "/bin/sh", nullptr);
- v.setProperty ("shellScript", script.replace ("\\", "\\\\")
- .replace ("\"", "\\\"")
- .replace ("\r\n", "\\n")
- .replace ("\n", "\\n"), nullptr);
- }
- }
-
- void addCopyFilesPhase (const String& phaseName, const StringArray& files, XcodeCopyFilesDestinationIDs dst)
- {
- ValueTree& v = addBuildPhase ("PBXCopyFilesBuildPhase", files, phaseName);
- v.setProperty ("dstPath", "", nullptr);
- v.setProperty ("dstSubfolderSpec", (int) dst, nullptr);
- }
-
- private:
- //==============================================================================
- void addExtraAudioUnitTargetSettings()
- {
- xcodeOtherRezFlags = "-d ppc_$ppc -d i386_$i386 -d ppc64_$ppc64 -d x86_64_$x86_64"
- " -I /System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework/Versions/A/Headers"
- " -I \\\"$(DEVELOPER_DIR)/Extras/CoreAudio/AudioUnits/AUPublic/AUBase\\\"";
-
- xcodeFrameworks.addTokens ("AudioUnit CoreAudioKit", false);
-
- XmlElement plistKey ("key");
- plistKey.addTextElement ("AudioComponents");
-
- XmlElement plistEntry ("array");
- XmlElement* dict = plistEntry.createNewChildElement ("dict");
-
- addPlistDictionaryKey (dict, "name", owner.project.getPluginManufacturer().toString()
- + ": " + owner.project.getPluginName().toString());
- addPlistDictionaryKey (dict, "description", owner.project.getPluginDesc().toString());
- addPlistDictionaryKey (dict, "factoryFunction", owner.project.getPluginAUExportPrefix().toString() + "Factory");
- addPlistDictionaryKey (dict, "manufacturer", owner.project.getPluginManufacturerCode().toString().trim().substring (0, 4));
- addPlistDictionaryKey (dict, "type", owner.project.getAUMainTypeCode());
- addPlistDictionaryKey (dict, "subtype", owner.project.getPluginCode().toString().trim().substring (0, 4));
- addPlistDictionaryKeyInt (dict, "version", owner.project.getVersionAsHexInteger());
-
- xcodeExtraPListEntries.add (plistKey);
- xcodeExtraPListEntries.add (plistEntry);
- }
-
- void addExtraAudioUnitv3PlugInTargetSettings()
- {
- if (owner.isiOS())
- xcodeFrameworks.addTokens ("CoreAudioKit AVFoundation", false);
- else
- xcodeFrameworks.addTokens ("AudioUnit CoreAudioKit AVFoundation", false);
-
- XmlElement plistKey ("key");
- plistKey.addTextElement ("NSExtension");
-
- XmlElement plistEntry ("dict");
-
- addPlistDictionaryKey (&plistEntry, "NSExtensionPrincipalClass", owner.project.getPluginAUExportPrefix().toString() + "FactoryAUv3");
- addPlistDictionaryKey (&plistEntry, "NSExtensionPointIdentifier", "com.apple.AudioUnit-UI");
- plistEntry.createNewChildElement ("key")->addTextElement ("NSExtensionAttributes");
-
- XmlElement* dict = plistEntry.createNewChildElement ("dict");
- dict->createNewChildElement ("key")->addTextElement ("AudioComponents");
- XmlElement* componentArray = dict->createNewChildElement ("array");
-
- XmlElement* componentDict = componentArray->createNewChildElement ("dict");
-
- addPlistDictionaryKey (componentDict, "name", owner.project.getPluginManufacturer().toString()
- + ": " + owner.project.getPluginName().toString());
- addPlistDictionaryKey (componentDict, "description", owner.project.getPluginDesc().toString());
- addPlistDictionaryKey (componentDict, "factoryFunction",owner.project. getPluginAUExportPrefix().toString() + "FactoryAUv3");
- addPlistDictionaryKey (componentDict, "manufacturer", owner.project.getPluginManufacturerCode().toString().trim().substring (0, 4));
- addPlistDictionaryKey (componentDict, "type", owner.project.getAUMainTypeCode());
- addPlistDictionaryKey (componentDict, "subtype", owner.project.getPluginCode().toString().trim().substring (0, 4));
- addPlistDictionaryKeyInt (componentDict, "version", owner.project.getVersionAsHexInteger());
- addPlistDictionaryKeyBool (componentDict, "sandboxSafe", true);
-
- componentDict->createNewChildElement ("key")->addTextElement ("tags");
- XmlElement* tagsArray = componentDict->createNewChildElement ("array");
-
- tagsArray->createNewChildElement ("string")->addTextElement (static_cast (owner.project.getPluginIsSynth().getValue()) ? "Synth" : "Effects");
-
- xcodeExtraPListEntries.add (plistKey);
- xcodeExtraPListEntries.add (plistEntry);
- }
-
- void addExtraAAXTargetSettings()
- {
- const RelativePath aaxLibsFolder = RelativePath (owner.getAAXPathValue().toString(), RelativePath::projectFolder).getChildFile ("Libs");
-
- xcodeExtraLibrariesDebug.add (aaxLibsFolder.getChildFile ("Debug/libAAXLibrary.a"));
- xcodeExtraLibrariesRelease.add (aaxLibsFolder.getChildFile ("Release/libAAXLibrary.a"));
- }
-
- void addExtraRTASTargetSettings()
- {
- RelativePath rtasFolder (owner.getRTASPathValue().toString(), RelativePath::projectFolder);
-
- xcodeExtraLibrariesDebug.add (rtasFolder.getChildFile ("MacBag/Libs/Debug/libPluginLibrary.a"));
- xcodeExtraLibrariesRelease.add (rtasFolder.getChildFile ("MacBag/Libs/Release/libPluginLibrary.a"));
- }
-
- //==============================================================================
- const XCodeProjectExporter& owner;
-
- Target& operator= (const Target&) JUCE_DELETED_FUNCTION;
- };
-
- mutable StringArray xcodeFrameworks;
- StringArray xcodeLibs;
-
-private:
- //==============================================================================
- bool xcodeCanUseDwarf;
- OwnedArray targets;
-
- mutable OwnedArray pbxBuildFiles, pbxFileReferences, pbxGroups, misc, projectConfigs, targetConfigs;
- mutable StringArray resourceIDs, sourceIDs, targetIDs;
- mutable StringArray frameworkFileIDs, rezFileIDs, resourceFileRefs;
- mutable File menuNibFile, iconFile;
- mutable StringArray buildProducts;
-
- const bool iOS;
-
- static String sanitisePath (const String& path)
- {
- if (path.startsWithChar ('~'))
- return "$(HOME)" + path.substring (1);
-
- return path;
- }
-
- static String addQuotesIfRequired (const String& s)
- {
- return s.containsAnyOf (" $") ? s.quoted() : s;
- }
-
- File getProjectBundle() const { return getTargetFolder().getChildFile (project.getProjectFilenameRoot()).withFileExtension (".xcodeproj"); }
-
- //==============================================================================
- void createObjects() const
- {
- prepareTargets();
-
- addFrameworks();
- addCustomResourceFolders();
- addPlistFileReferences();
-
- if (iOS && ! projectType.isStaticLibrary())
- addXcassets();
- else
- addNibFiles();
-
- addIcons();
- addBuildConfigurations();
-
- addProjectConfigList (projectConfigs, createID ("__projList"));
-
- {
- StringArray topLevelGroupIDs;
-
- addFilesAndGroupsToProject (topLevelGroupIDs);
- addBuildPhases();
- addExtraGroupsToProject (topLevelGroupIDs);
-
- addGroup (createID ("__mainsourcegroup"), "Source", topLevelGroupIDs);
- }
-
- addProjectObject();
- removeMismatchedXcuserdata();
- }
-
- void prepareTargets() const
- {
- for (int targetIdx = 0; targetIdx < targets.size(); ++targetIdx)
- {
- Target& target = *targets[targetIdx];
-
- if (target.type == Target::AggregateTarget)
- continue;
-
- target.addMainBuildProduct();
-
- String targetName = target.getName();
- String fileID (createID (targetName + String ("__targetbuildref")));
- String fileRefID (createID (String ("__productFileID") + targetName));
-
- ValueTree* v = new ValueTree (fileID);
- v->setProperty ("isa", "PBXBuildFile", nullptr);
- v->setProperty ("fileRef", fileRefID, nullptr);
-
- target.mainBuildProductID = fileID;
-
- pbxBuildFiles.add (v);
- target.addDependency();
- }
- }
-
- void addPlistFileReferences() const
- {
- for (int targetIdx = 0; targetIdx < targets.size(); ++targetIdx)
- {
- Target& target = *targets[targetIdx];
-
- if (target.type == Target::AggregateTarget)
- continue;
-
- if (target.xcodeCreatePList)
- {
- RelativePath plistPath (target.infoPlistFile, getTargetFolder(), RelativePath::buildTargetFolder);
- addFileReference (plistPath.toUnixStyle());
- resourceFileRefs.add (createFileRefID (plistPath));
- }
- }
-
- }
-
- void addNibFiles() const
- {
- MemoryOutputStream nib;
- nib.write (BinaryData::RecentFilesMenuTemplate_nib, BinaryData::RecentFilesMenuTemplate_nibSize);
- overwriteFileIfDifferentOrThrow (menuNibFile, nib);
-
- RelativePath menuNibPath (menuNibFile, getTargetFolder(), RelativePath::buildTargetFolder);
- addFileReference (menuNibPath.toUnixStyle());
- resourceIDs.add (addBuildFile (menuNibPath, false, false));
- resourceFileRefs.add (createFileRefID (menuNibPath));
- }
-
- void addIcons() const
- {
- if (iconFile.exists())
- {
- RelativePath iconPath (iconFile, getTargetFolder(), RelativePath::buildTargetFolder);
- addFileReference (iconPath.toUnixStyle());
- resourceIDs.add (addBuildFile (iconPath, false, false));
- resourceFileRefs.add (createFileRefID (iconPath));
- }
- }
-
- void addBuildConfigurations() const
- {
- // add build configurations
- for (ConstConfigIterator config (*this); config.next();)
- {
- const XcodeBuildConfiguration& xcodeConfig = dynamic_cast (*config);
- addProjectConfig (config->getName(), getProjectSettings (xcodeConfig));
- }
- }
-
- void addFilesAndGroupsToProject (StringArray& topLevelGroupIDs) const
- {
- if (! isiOS() && project.getProjectType().isAudioPlugin())
- topLevelGroupIDs.add (addEntitlementsFile());
-
- for (int i = 0; i < getAllGroups().size(); ++i)
- {
- const Project::Item& group = getAllGroups().getReference(i);
-
- if (group.getNumChildren() > 0)
- topLevelGroupIDs.add (addProjectItem (group));
- }
- }
-
- void addExtraGroupsToProject (StringArray& topLevelGroupIDs) const
- {
- { // Add 'resources' group
- String resourcesGroupID (createID ("__resources"));
- addGroup (resourcesGroupID, "Resources", resourceFileRefs);
- topLevelGroupIDs.add (resourcesGroupID);
- }
-
- { // Add 'frameworks' group
- String frameworksGroupID (createID ("__frameworks"));
- addGroup (frameworksGroupID, "Frameworks", frameworkFileIDs);
- topLevelGroupIDs.add (frameworksGroupID);
- }
-
- { // Add 'products' group
- String productsGroupID (createID ("__products"));
- addGroup (productsGroupID, "Products", buildProducts);
- topLevelGroupIDs.add (productsGroupID);
- }
- }
-
- void addBuildPhases() const
- {
- // add build phases
- for (int i = 0; i < targets.size(); ++i)
- {
- Target& target = *targets[i];
-
- if (target.type != Target::AggregateTarget)
- buildProducts.add (createID (String ("__productFileID") + String (target.getName())));
-
- for (ConstConfigIterator config (*this); config.next();)
- {
- const XcodeBuildConfiguration& xcodeConfig = dynamic_cast (*config);
- target.addTargetConfig (config->getName(), target.getTargetSettings (xcodeConfig));
- }
-
- addConfigList (target, targetConfigs, createID (String ("__configList") + target.getName()));
-
- target.addShellScriptBuildPhase ("Pre-build script", getPreBuildScript());
-
- if (target.type != Target::AggregateTarget)
- {
- // TODO: ideally resources wouldn't be copied into the AUv3 bundle as well.
- // However, fixing this requires supporting App groups -> TODO: add app groups
- if (! projectType.isStaticLibrary() && target.type != Target::SharedCodeTarget)
- target.addBuildPhase ("PBXResourcesBuildPhase", resourceIDs);
-
- StringArray rezFiles (rezFileIDs);
- rezFiles.addArray (target.rezFileIDs);
-
- if (rezFiles.size() > 0)
- target.addBuildPhase ("PBXRezBuildPhase", rezFiles);
-
- if (project.getProjectType().isAudioPlugin() && target.type != Target::SharedCodeTarget && target.type != Target::AudioUnitv3PlugIn)
- {
- if (Target* sharedCodeTarget = getTargetOfType (Target::SharedCodeTarget))
- {
- StringArray files;
- files.add (sharedCodeTarget->mainBuildProductID);
- target.addCopyFilesPhase ("Embed Frameworks", files, kFrameworksFolder);
- }
- }
-
- StringArray sourceFiles (target.sourceIDs);
-
- if (target.type == Target::SharedCodeTarget
- || (! project.getProjectType().isAudioPlugin()))
- sourceFiles.addArray (sourceIDs);
-
- target.addBuildPhase ("PBXSourcesBuildPhase", sourceFiles);
-
- if (! projectType.isStaticLibrary())
- target.addBuildPhase ("PBXFrameworksBuildPhase", target.frameworkIDs);
-
- target.addShellScriptBuildPhase ("Post-build script", getPostBuildScript());
- }
-
- if (project.getProjectType().isAudioPlugin() && project.shouldBuildAUv3().getValue()
- && project.shouldBuildStandalone().getValue() && target.type == Target::StandalonePlugIn)
- embedAppExtension();
-
- addTargetObject (target);
- }
- }
-
- void embedAppExtension() const
- {
- if (Target* standaloneTarget = getTargetOfType (Target::StandalonePlugIn))
- {
- if (Target* auv3Target = getTargetOfType (Target::AudioUnitv3PlugIn))
- {
- StringArray files;
- files.add (auv3Target->mainBuildProductID);
- standaloneTarget->addCopyFilesPhase ("Embed App Extensions", files, kPluginsFolder);
- }
- }
- }
-
- static Image fixMacIconImageSize (Drawable& image)
- {
- const int validSizes[] = { 16, 32, 48, 128, 256, 512, 1024 };
-
- const int w = image.getWidth();
- const int h = image.getHeight();
-
- int bestSize = 16;
-
- for (int i = 0; i < numElementsInArray (validSizes); ++i)
- {
- if (w == h && w == validSizes[i])
- {
- bestSize = w;
- break;
- }
-
- if (jmax (w, h) > validSizes[i])
- bestSize = validSizes[i];
- }
-
- return rescaleImageForIcon (image, bestSize);
- }
-
- //==============================================================================
- Target* getTargetOfType (Target::Type type) const
- {
- for (auto& target : targets)
- if (target->type == type)
- return target;
-
- return nullptr;
- }
-
- void addTargetObject (Target& target) const
- {
- String targetName = target.getName();
-
- String targetID = target.getID();
- ValueTree* const v = new ValueTree (targetID);
- v->setProperty ("isa", target.type == Target::AggregateTarget ? "PBXAggregateTarget" : "PBXNativeTarget", nullptr);
- v->setProperty ("buildConfigurationList", createID (String ("__configList") + targetName), nullptr);
-
- if (target.type != Target::AggregateTarget)
- {
- v->setProperty ("buildPhases", indentParenthesisedList (target.buildPhaseIDs), nullptr);
- v->setProperty ("buildRules", "( )", nullptr);
- }
-
- v->setProperty ("dependencies", indentParenthesisedList (getTargetDependencies (target)), nullptr);
- v->setProperty (Ids::name, target.getXCodeSchemeName(), nullptr);
- v->setProperty ("productName", projectName, nullptr);
-
- if (target.type != Target::AggregateTarget)
- {
- v->setProperty ("productReference", createID (String ("__productFileID") + targetName), nullptr);
-
- jassert (target.xcodeProductType.isNotEmpty());
- v->setProperty ("productType", target.xcodeProductType, nullptr);
- }
-
- targetIDs.add (targetID);
- misc.add (v);
- }
-
- StringArray getTargetDependencies (const Target& target) const
- {
- StringArray dependencies;
-
- if (project.getProjectType().isAudioPlugin())
- {
- if (target.type == Target::StandalonePlugIn) // depends on AUv3 and shared code
- {
- if (Target* auv3Target = getTargetOfType (Target::AudioUnitv3PlugIn))
- dependencies.add (auv3Target->getDependencyID());
-
- if (Target* sharedCodeTarget = getTargetOfType (Target::SharedCodeTarget))
- dependencies.add (sharedCodeTarget->getDependencyID());
- }
- else if (target.type == Target::AggregateTarget) // depends on all other targets
- {
- for (int i = 1; i < targets.size(); ++i)
- dependencies.add (targets[i]->getDependencyID());
- }
- else if (target.type != Target::SharedCodeTarget) // shared code doesn't depend on anything; all other targets depend only on the shared code
- {
- if (Target* sharedCodeTarget = getTargetOfType (Target::SharedCodeTarget))
- dependencies.add (sharedCodeTarget->getDependencyID());
- }
- }
-
- return dependencies;
- }
-
- static void writeOldIconFormat (MemoryOutputStream& out, const Image& image, const char* type, const char* maskType)
- {
- const int w = image.getWidth();
- const int h = image.getHeight();
-
- out.write (type, 4);
- out.writeIntBigEndian (8 + 4 * w * h);
-
- const Image::BitmapData bitmap (image, Image::BitmapData::readOnly);
-
- for (int y = 0; y < h; ++y)
- {
- for (int x = 0; x < w; ++x)
- {
- const Colour pixel (bitmap.getPixelColour (x, y));
- out.writeByte ((char) pixel.getAlpha());
- out.writeByte ((char) pixel.getRed());
- out.writeByte ((char) pixel.getGreen());
- out.writeByte ((char) pixel.getBlue());
- }
- }
-
- out.write (maskType, 4);
- out.writeIntBigEndian (8 + w * h);
-
- for (int y = 0; y < h; ++y)
- {
- for (int x = 0; x < w; ++x)
- {
- const Colour pixel (bitmap.getPixelColour (x, y));
- out.writeByte ((char) pixel.getAlpha());
- }
- }
- }
-
- static void writeNewIconFormat (MemoryOutputStream& out, const Image& image, const char* type)
- {
- MemoryOutputStream pngData;
- PNGImageFormat pngFormat;
- pngFormat.writeImageToStream (image, pngData);
-
- out.write (type, 4);
- out.writeIntBigEndian (8 + (int) pngData.getDataSize());
- out << pngData;
- }
-
- void writeIcnsFile (const OwnedArray& images, OutputStream& out) const
- {
- MemoryOutputStream data;
- int smallest = 0x7fffffff;
- Drawable* smallestImage = nullptr;
-
- for (int i = 0; i < images.size(); ++i)
- {
- const Image image (fixMacIconImageSize (*images.getUnchecked(i)));
- jassert (image.getWidth() == image.getHeight());
-
- if (image.getWidth() < smallest)
- {
- smallest = image.getWidth();
- smallestImage = images.getUnchecked(i);
- }
-
- switch (image.getWidth())
- {
- case 16: writeOldIconFormat (data, image, "is32", "s8mk"); break;
- case 32: writeOldIconFormat (data, image, "il32", "l8mk"); break;
- case 48: writeOldIconFormat (data, image, "ih32", "h8mk"); break;
- case 128: writeOldIconFormat (data, image, "it32", "t8mk"); break;
- case 256: writeNewIconFormat (data, image, "ic08"); break;
- case 512: writeNewIconFormat (data, image, "ic09"); break;
- case 1024: writeNewIconFormat (data, image, "ic10"); break;
- default: break;
- }
- }
-
- jassert (data.getDataSize() > 0); // no suitable sized images?
-
- // If you only supply a 1024 image, the file doesn't work on 10.8, so we need
- // to force a smaller one in there too..
- if (smallest > 512 && smallestImage != nullptr)
- writeNewIconFormat (data, rescaleImageForIcon (*smallestImage, 512), "ic09");
-
- out.write ("icns", 4);
- out.writeIntBigEndian ((int) data.getDataSize() + 8);
- out << data;
- }
-
- void getIconImages (OwnedArray& images) const
- {
- ScopedPointer bigIcon (getBigIcon());
- if (bigIcon != nullptr)
- images.add (bigIcon.release());
-
- ScopedPointer smallIcon (getSmallIcon());
- if (smallIcon != nullptr)
- images.add (smallIcon.release());
- }
-
- void createiOSIconFiles (File appIconSet) const
- {
- const Array types (getiOSAppIconTypes());
-
- OwnedArray images;
- getIconImages (images);
-
- if (images.size() > 0)
- {
- for (int i = 0; i < types.size(); ++i)
- {
- const AppIconType type = types.getUnchecked(i);
- const Image image (rescaleImageForIcon (*images.getFirst(), type.size));
-
- MemoryOutputStream pngData;
- PNGImageFormat pngFormat;
- pngFormat.writeImageToStream (image, pngData);
-
- overwriteFileIfDifferentOrThrow (appIconSet.getChildFile (type.filename), pngData);
- }
- }
- }
-
- void createIconFile() const
- {
- OwnedArray images;
- getIconImages (images);
-
- if (images.size() > 0)
- {
- MemoryOutputStream mo;
- writeIcnsFile (images, mo);
-
- iconFile = getTargetFolder().getChildFile ("Icon.icns");
- overwriteFileIfDifferentOrThrow (iconFile, mo);
- }
- }
-
- void writeInfoPlistFiles() const
- {
- for (auto& target : targets)
- target->writeInfoPlistFile();
- }
-
- void deleteRsrcFiles() const
- {
- for (DirectoryIterator di (getTargetFolder().getChildFile ("build"), true, "*.rsrc", File::findFiles); di.next();)
- di.getFile().deleteFile();
- }
-
- String getHeaderSearchPaths (const BuildConfiguration& config) const
- {
- StringArray paths (extraSearchPaths);
- paths.addArray (config.getHeaderSearchPaths());
- paths.add ("$(inherited)");
-
- paths = getCleanedStringArray (paths);
-
- for (int i = 0; i < paths.size(); ++i)
- {
- String& s = paths.getReference(i);
-
- s = replacePreprocessorTokens (config, s);
-
- if (s.containsChar (' '))
- s = "\"\\\"" + s + "\\\"\""; // crazy double quotes required when there are spaces..
- else
- s = "\"" + s + "\"";
- }
-
- return "(" + paths.joinIntoString (", ") + ")";
- }
-
- static String getLinkerFlagForLib (String library)
- {
- if (library.substring (0, 3) == "lib")
- library = library.substring (3);
-
- return "-l" + library.upToLastOccurrenceOf (".", false, false);
- }
-
- void getLinkerFlagsForStaticLibrary (const RelativePath& library, StringArray& flags, StringArray& librarySearchPaths) const
- {
- flags.add (getLinkerFlagForLib (library.getFileNameWithoutExtension()));
-
- String searchPath (library.toUnixStyle().upToLastOccurrenceOf ("/", false, false));
-
- if (! library.isAbsolute())
- {
- String srcRoot (rebaseFromProjectFolderToBuildTarget (RelativePath (".", RelativePath::projectFolder)).toUnixStyle());
-
- if (srcRoot.endsWith ("/.")) srcRoot = srcRoot.dropLastCharacters (2);
- if (! srcRoot.endsWithChar ('/')) srcRoot << '/';
-
- searchPath = srcRoot + searchPath;
- }
-
- librarySearchPaths.add (sanitisePath (searchPath));
- }
-
- StringArray getProjectSettings (const XcodeBuildConfiguration& config) const
- {
- StringArray s;
- s.add ("ALWAYS_SEARCH_USER_PATHS = NO");
- s.add ("GCC_C_LANGUAGE_STANDARD = c99");
- s.add ("GCC_WARN_ABOUT_RETURN_TYPE = YES");
- s.add ("GCC_WARN_CHECK_SWITCH_STATEMENTS = YES");
- s.add ("GCC_WARN_UNUSED_VARIABLE = YES");
- s.add ("GCC_WARN_MISSING_PARENTHESES = YES");
- s.add ("GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES");
- s.add ("GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES");
- s.add ("WARNING_CFLAGS = -Wreorder");
- s.add ("GCC_MODEL_TUNING = G5");
-
- if (projectType.isStaticLibrary())
- {
- s.add ("GCC_INLINES_ARE_PRIVATE_EXTERN = NO");
- s.add ("GCC_SYMBOLS_PRIVATE_EXTERN = NO");
- }
- else
- {
- s.add ("GCC_INLINES_ARE_PRIVATE_EXTERN = YES");
- }
-
- if (config.isDebug())
- {
- s.add ("ENABLE_TESTABILITY = YES");
-
- if (config.osxArchitecture.get() == osxArch_Default || config.osxArchitecture.get().isEmpty())
- s.add ("ONLY_ACTIVE_ARCH = YES");
- }
-
- if (iOS)
- {
- s.add ("\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = " + config.codeSignIdentity.get().quoted());
- s.add ("SDKROOT = iphoneos");
- s.add ("TARGETED_DEVICE_FAMILY = \"1,2\"");
-
- const String iosVersion (config.iosDeploymentTarget.get());
- if (iosVersion.isNotEmpty() && iosVersion != osxVersionDefault)
- s.add ("IPHONEOS_DEPLOYMENT_TARGET = " + iosVersion);
- }
- else
- {
- if (! config.codeSignIdentity.isUsingDefault() || getIosDevelopmentTeamIDString().isNotEmpty())
- s.add ("\"CODE_SIGN_IDENTITY\" = " + config.codeSignIdentity.get().quoted());
- }
-
- s.add ("ZERO_LINK = NO");
-
- if (xcodeCanUseDwarf)
- s.add ("DEBUG_INFORMATION_FORMAT = \"dwarf\"");
-
- s.add ("PRODUCT_NAME = \"" + replacePreprocessorTokens (config, config.getTargetBinaryNameString()) + "\"");
- return s;
- }
-
- void addFrameworks() const
- {
- if (! projectType.isStaticLibrary())
- {
- if (iOS && isInAppPurchasesEnabled())
- xcodeFrameworks.addIfNotAlreadyThere ("StoreKit");
-
- xcodeFrameworks.addTokens (getExtraFrameworksString(), ",;", "\"'");
- xcodeFrameworks.trim();
-
- StringArray s (xcodeFrameworks);
-
- for (auto& target : targets)
- s.addArray (target->xcodeFrameworks);
-
- if (project.getConfigFlag ("JUCE_QUICKTIME") == Project::configFlagDisabled)
- s.removeString ("QuickTime");
-
- s.trim();
- s.removeDuplicates (true);
- s.sort (true);
-
- for (int i = 0; i < s.size(); ++i)
- {
- String frameworkID = addFramework (s[i]);
-
- // find all the targets that are referring to this object
- for (auto& target : targets)
- {
- if (xcodeFrameworks.contains (s[i]) || target->xcodeFrameworks.contains (s[i]))
- target->frameworkIDs.add (frameworkID);
- }
- }
-
- if (project.getProjectType().isAudioPlugin())
- {
- if (Target* sharedCodeTarget = getTargetOfType (Target::SharedCodeTarget))
- {
- for (auto& target : targets)
- if (target->type != Target::SharedCodeTarget && target->type != Target::AggregateTarget)
- target->frameworkIDs.add (sharedCodeTarget->mainBuildProductID);
- }
- }
- }
- }
-
- void addCustomResourceFolders() const
- {
- StringArray crf;
-
- crf.addTokens (getCustomResourceFoldersString(), ":", "");
- crf.trim();
-
- for (int i = 0; i < crf.size(); ++i)
- addCustomResourceFolder (crf[i]);
- }
-
- void addXcassets() const
- {
- String customXcassetsPath = getCustomXcassetsFolderString();
-
- if (customXcassetsPath.isEmpty())
- createXcassetsFolderFromIcons();
- else
- addCustomResourceFolder (customXcassetsPath, "folder.assetcatalog");
- }
-
- void addCustomResourceFolder (String folderPathRelativeToProjectFolder, const String fileType = "folder") const
- {
- String folderPath = RelativePath (folderPathRelativeToProjectFolder, RelativePath::projectFolder)
- .rebased (projectFolder, getTargetFolder(), RelativePath::buildTargetFolder)
- .toUnixStyle();
-
- const String fileRefID (createFileRefID (folderPath));
-
- addFileOrFolderReference (folderPath, "", fileType);
-
- resourceIDs.add (addBuildFile (folderPath, fileRefID, false, false));
- resourceFileRefs.add (createFileRefID (folderPath));
- }
-
- //==============================================================================
- void writeProjectFile (OutputStream& output) const
- {
- output << "// !$*UTF8*$!\n{\n"
- "\tarchiveVersion = 1;\n"
- "\tclasses = {\n\t};\n"
- "\tobjectVersion = 46;\n"
- "\tobjects = {\n\n";
-
- Array objects;
- objects.addArray (pbxBuildFiles);
- objects.addArray (pbxFileReferences);
- objects.addArray (pbxGroups);
- objects.addArray (targetConfigs);
- objects.addArray (projectConfigs);
- objects.addArray (misc);
-
- for (int i = 0; i < objects.size(); ++i)
- {
- ValueTree& o = *objects.getUnchecked(i);
- output << "\t\t" << o.getType().toString() << " = {";
-
- for (int j = 0; j < o.getNumProperties(); ++j)
- {
- const Identifier propertyName (o.getPropertyName(j));
- String val (o.getProperty (propertyName).toString());
-
- if (val.isEmpty() || (val.containsAnyOf (" \t;<>()=,&+-_@~\r\n\\#%^`*")
- && ! (val.trimStart().startsWithChar ('(')
- || val.trimStart().startsWithChar ('{'))))
- val = "\"" + val + "\"";
-
- output << propertyName.toString() << " = " << val << "; ";
- }
-
- output << "};\n";
- }
-
- output << "\t};\n\trootObject = " << createID ("__root") << ";\n}\n";
- }
-
- String addBuildFile (const String& path, const String& fileRefID, bool addToSourceBuildPhase, bool inhibitWarnings, Target* xcodeTarget = nullptr) const
- {
- String fileID (createID (path + "buildref"));
-
- if (addToSourceBuildPhase)
- {
- if (xcodeTarget != nullptr) xcodeTarget->sourceIDs.add (fileID);
- else sourceIDs.add (fileID);
- }
-
- ValueTree* v = new ValueTree (fileID);
- v->setProperty ("isa", "PBXBuildFile", nullptr);
- v->setProperty ("fileRef", fileRefID, nullptr);
-
- if (inhibitWarnings)
- v->setProperty ("settings", "{COMPILER_FLAGS = \"-w\"; }", nullptr);
-
- pbxBuildFiles.add (v);
- return fileID;
- }
-
- String addBuildFile (const RelativePath& path, bool addToSourceBuildPhase, bool inhibitWarnings, Target* xcodeTarget = nullptr) const
- {
- return addBuildFile (path.toUnixStyle(), createFileRefID (path), addToSourceBuildPhase, inhibitWarnings, xcodeTarget);
- }
-
- String addFileReference (String pathString) const
- {
- String sourceTree ("SOURCE_ROOT");
- RelativePath path (pathString, RelativePath::unknown);
-
- if (pathString.startsWith ("${"))
- {
- sourceTree = pathString.substring (2).upToFirstOccurrenceOf ("}", false, false);
- pathString = pathString.fromFirstOccurrenceOf ("}/", false, false);
- }
- else if (path.isAbsolute())
- {
- sourceTree = "";
- }
-
- String fileType = getFileType (path);
-
- return addFileOrFolderReference (pathString, sourceTree, fileType);
- }
-
- String addFileOrFolderReference (String pathString, String sourceTree, String fileType) const
- {
- const String fileRefID (createFileRefID (pathString));
-
- ScopedPointer v (new ValueTree (fileRefID));
- v->setProperty ("isa", "PBXFileReference", nullptr);
- v->setProperty ("lastKnownFileType", fileType, nullptr);
- v->setProperty (Ids::name, pathString.fromLastOccurrenceOf ("/", false, false), nullptr);
- v->setProperty ("path", sanitisePath (pathString), nullptr);
- v->setProperty ("sourceTree", sourceTree, nullptr);
-
- const int existing = pbxFileReferences.indexOfSorted (*this, v);
-
- if (existing >= 0)
- {
- // If this fails, there's either a string hash collision, or the same file is being added twice (incorrectly)
- jassert (pbxFileReferences.getUnchecked (existing)->isEquivalentTo (*v));
- }
- else
- {
- pbxFileReferences.addSorted (*this, v.release());
- }
-
- return fileRefID;
- }
-
-public:
- static int compareElements (const ValueTree* first, const ValueTree* second)
- {
- return first->getType().getCharPointer().compare (second->getType().getCharPointer());
- }
-
-private:
- static String getFileType (const RelativePath& file)
- {
- if (file.hasFileExtension (cppFileExtensions)) return "sourcecode.cpp.cpp";
- if (file.hasFileExtension (".mm")) return "sourcecode.cpp.objcpp";
- if (file.hasFileExtension (".m")) return "sourcecode.c.objc";
- if (file.hasFileExtension (".c")) return "sourcecode.c.c";
- if (file.hasFileExtension (headerFileExtensions)) return "sourcecode.c.h";
- if (file.hasFileExtension (asmFileExtensions)) return "sourcecode.c.asm";
- if (file.hasFileExtension (".framework")) return "wrapper.framework";
- if (file.hasFileExtension (".jpeg;.jpg")) return "image.jpeg";
- if (file.hasFileExtension ("png;gif")) return "image" + file.getFileExtension();
- if (file.hasFileExtension ("html;htm")) return "text.html";
- if (file.hasFileExtension ("xml;zip;wav")) return "file" + file.getFileExtension();
- if (file.hasFileExtension ("txt;rtf")) return "text" + file.getFileExtension();
- if (file.hasFileExtension ("plist")) return "text.plist.xml";
- if (file.hasFileExtension ("entitlements")) return "text.plist.xml";
- if (file.hasFileExtension ("app")) return "wrapper.application";
- if (file.hasFileExtension ("component;vst;plugin")) return "wrapper.cfbundle";
- if (file.hasFileExtension ("xcodeproj")) return "wrapper.pb-project";
- if (file.hasFileExtension ("a")) return "archive.ar";
- if (file.hasFileExtension ("xcassets")) return "folder.assetcatalog";
-
- return "file" + file.getFileExtension();
- }
-
- String addFile (const RelativePath& path, bool shouldBeCompiled, bool shouldBeAddedToBinaryResources,
- bool shouldBeAddedToXcodeResources, bool inhibitWarnings, Target* xcodeTarget) const
- {
- const String pathAsString (path.toUnixStyle());
- const String refID (addFileReference (path.toUnixStyle()));
-
- if (shouldBeCompiled)
- {
- addBuildFile (pathAsString, refID, true, inhibitWarnings, xcodeTarget);
- }
- else if (! shouldBeAddedToBinaryResources || shouldBeAddedToXcodeResources)
- {
- const String fileType (getFileType (path));
-
- if (shouldBeAddedToXcodeResources || fileType.startsWith ("image.") || fileType.startsWith ("text.") || fileType.startsWith ("file."))
- {
- resourceIDs.add (addBuildFile (pathAsString, refID, false, false));
- resourceFileRefs.add (refID);
- }
- }
-
- return refID;
- }
-
- String addRezFile (const RelativePath& path) const
- {
- const String pathAsString (path.toUnixStyle());
- const String refID (addFileReference (path.toUnixStyle()));
-
- Target* auTarget = getTargetOfType (Target::AudioUnitPlugIn);
-
- if (auTarget == nullptr)
- return String();
-
- String rezFileID = addBuildFile (pathAsString, refID, false, false, auTarget);
- auTarget->rezFileIDs.add (rezFileID);
-
- return refID;
- }
-
- String getEntitlementsFileName() const
- {
- return project.getProjectFilenameRoot() + String (".entitlements");
- }
-
- String addEntitlementsFile() const
- {
- const char* sandboxEntitlement =
- ""
- ""
- ""
- ""
- " com.apple.security.app-sandbox"
- " "
- ""
- "";
-
- File entitlementsFile = getTargetFolder().getChildFile (getEntitlementsFileName());
- overwriteFileIfDifferentOrThrow (entitlementsFile, sandboxEntitlement);
-
- RelativePath plistPath (entitlementsFile, getTargetFolder(), RelativePath::buildTargetFolder);
- return addFile (plistPath, false, false, false, false, nullptr);
- }
-
- String addProjectItem (const Project::Item& projectItem) const
- {
- if (projectItem.isGroup())
- {
- StringArray childIDs;
- for (int i = 0; i < projectItem.getNumChildren(); ++i)
- {
- const String childID (addProjectItem (projectItem.getChild(i)));
-
- if (childID.isNotEmpty())
- childIDs.add (childID);
- }
-
- return addGroup (projectItem, childIDs);
- }
-
- if (projectItem.shouldBeAddedToTargetProject())
- {
- const String itemPath (projectItem.getFilePath());
- RelativePath path;
-
- if (itemPath.startsWith ("${"))
- path = RelativePath (itemPath, RelativePath::unknown);
- else
- path = RelativePath (projectItem.getFile(), getTargetFolder(), RelativePath::buildTargetFolder);
-
- if (path.hasFileExtension (".r") && LibraryModule::CompileUnit::hasSuffix (projectItem.getFile(), "_AU"))
- return addRezFile (path);
-
- Target* xcodeTarget = nullptr;
- if (projectItem.isModuleCode() && projectItem.shouldBeCompiled())
- {
- const File& file = projectItem.getFile();
-
- Target::Type targetType = Target::unspecified;
- if (LibraryModule::CompileUnit::hasSuffix (file, "_AU")) targetType = Target::AudioUnitPlugIn;
- else if (LibraryModule::CompileUnit::hasSuffix (file, "_AUv3")) targetType = Target::AudioUnitv3PlugIn;
- else if (LibraryModule::CompileUnit::hasSuffix (file, "_AAX")) targetType = Target::AAXPlugIn;
- else if (LibraryModule::CompileUnit::hasSuffix (file, "_RTAS")) targetType = Target::RTASPlugIn;
- else if (LibraryModule::CompileUnit::hasSuffix (file, "_VST2")) targetType = Target::VSTPlugIn;
- else if (LibraryModule::CompileUnit::hasSuffix (file, "_VST3")) targetType = Target::VST3PlugIn;
- else if (LibraryModule::CompileUnit::hasSuffix (file, "_Standalone")) targetType = Target::StandalonePlugIn;
-
- xcodeTarget = getTargetOfType (targetType);
- }
-
- return addFile (path, projectItem.shouldBeCompiled(),
- projectItem.shouldBeAddedToBinaryResources(),
- projectItem.shouldBeAddedToXcodeResources(),
- projectItem.shouldInhibitWarnings(),
- xcodeTarget);
- }
-
- return String();
- }
-
- String addFramework (const String& frameworkName) const
- {
- String path (frameworkName);
- if (! File::isAbsolutePath (path))
- path = "System/Library/Frameworks/" + path;
-
- if (! path.endsWithIgnoreCase (".framework"))
- path << ".framework";
-
- const String fileRefID (createFileRefID (path));
-
- addFileReference ((File::isAbsolutePath (frameworkName) ? "" : "${SDKROOT}/") + path);
- frameworkFileIDs.add (fileRefID);
-
- return addBuildFile (path, fileRefID, false, false);
- }
-
- void addGroup (const String& groupID, const String& groupName, const StringArray& childIDs) const
- {
- ValueTree* v = new ValueTree (groupID);
- v->setProperty ("isa", "PBXGroup", nullptr);
- v->setProperty ("children", indentParenthesisedList (childIDs), nullptr);
- v->setProperty (Ids::name, groupName, nullptr);
- v->setProperty ("sourceTree", "", nullptr);
- pbxGroups.add (v);
- }
-
- String addGroup (const Project::Item& item, StringArray& childIDs) const
- {
- const String groupName (item.getName());
- const String groupID (getIDForGroup (item));
- addGroup (groupID, groupName, childIDs);
- return groupID;
- }
-
- void addProjectConfig (const String& configName, const StringArray& buildSettings) const
- {
- ValueTree* v = new ValueTree (createID ("projectconfigid_" + configName));
- v->setProperty ("isa", "XCBuildConfiguration", nullptr);
- v->setProperty ("buildSettings", indentBracedList (buildSettings), nullptr);
- v->setProperty (Ids::name, configName, nullptr);
- projectConfigs.add (v);
- }
-
- void addConfigList (Target& target, const OwnedArray & configsToUse, const String& listID) const
- {
- ValueTree* v = new ValueTree (listID);
- v->setProperty ("isa", "XCConfigurationList", nullptr);
- v->setProperty ("buildConfigurations", indentParenthesisedList (target.configIDs), nullptr);
- v->setProperty ("defaultConfigurationIsVisible", (int) 0, nullptr);
-
- if (configsToUse[0] != nullptr)
- v->setProperty ("defaultConfigurationName", configsToUse[0]->getProperty (Ids::name), nullptr);
-
- misc.add (v);
- }
-
- void addProjectConfigList (const OwnedArray & configsToUse, const String& listID) const
- {
- StringArray configIDs;
-
- for (int i = 0; i < configsToUse.size(); ++i)
- configIDs.add (configsToUse[i]->getType().toString());
-
- ValueTree* v = new ValueTree (listID);
- v->setProperty ("isa", "XCConfigurationList", nullptr);
- v->setProperty ("buildConfigurations", indentParenthesisedList (configIDs), nullptr);
- v->setProperty ("defaultConfigurationIsVisible", (int) 0, nullptr);
-
- if (configsToUse[0] != nullptr)
- v->setProperty ("defaultConfigurationName", configsToUse[0]->getProperty (Ids::name), nullptr);
-
- misc.add (v);
- }
-
- void addProjectObject() const
- {
- ValueTree* const v = new ValueTree (createID ("__root"));
- v->setProperty ("isa", "PBXProject", nullptr);
- v->setProperty ("buildConfigurationList", createID ("__projList"), nullptr);
- v->setProperty ("attributes", getProjectObjectAttributes(), nullptr);
- v->setProperty ("compatibilityVersion", "Xcode 3.2", nullptr);
- v->setProperty ("hasScannedForEncodings", (int) 0, nullptr);
- v->setProperty ("mainGroup", createID ("__mainsourcegroup"), nullptr);
- v->setProperty ("projectDirPath", "\"\"", nullptr);
- v->setProperty ("projectRoot", "\"\"", nullptr);
-
- String targetString = "(" + targetIDs.joinIntoString (", ") + ")";
- v->setProperty ("targets", targetString, nullptr);
- misc.add (v);
- }
-
- //==============================================================================
- void removeMismatchedXcuserdata() const
- {
- File xcuserdata = getProjectBundle().getChildFile ("xcuserdata");
-
- if (! xcuserdata.exists())
- return;
-
- if (! xcuserdataMatchesTargets (xcuserdata))
- {
- xcuserdata.deleteRecursively();
- getProjectBundle().getChildFile ("project.xcworkspace").deleteRecursively();
- }
- }
-
- bool xcuserdataMatchesTargets (const File& xcuserdata) const
- {
- Array xcschemeManagementPlists;
- xcuserdata.findChildFiles (xcschemeManagementPlists, File::findFiles, true, "xcschememanagement.plist");
-
- for (auto& plist : xcschemeManagementPlists)
- if (! xcschemeManagementPlistMatchesTargets (plist))
- return false;
-
- return true;
- }
-
- static StringArray parseNamesOfTargetsFromPlist (const XmlElement& dictXML)
- {
- forEachXmlChildElementWithTagName (dictXML, schemesKey, "key")
- {
- if (schemesKey->getAllSubText().trim().equalsIgnoreCase ("SchemeUserState"))
- {
- if (auto* dict = schemesKey->getNextElement())
- {
- if (dict->hasTagName ("dict"))
- {
- StringArray names;
-
- forEachXmlChildElementWithTagName (*dict, key, "key")
- names.add (key->getAllSubText().upToLastOccurrenceOf (".xcscheme", false, false).trim());
-
- names.sort (false);
- return names;
- }
- }
- }
- }
-
- return StringArray();
- }
-
- StringArray getNamesOfTargets() const
- {
- StringArray names;
-
- for (auto& target : targets)
- names.add (target->getXCodeSchemeName());
-
- names.sort (false);
- return names;
- }
-
- bool xcschemeManagementPlistMatchesTargets (const File& plist) const
- {
- ScopedPointer xml (XmlDocument::parse (plist));
-
- if (xml != nullptr)
- if (auto* dict = xml->getChildByName ("dict"))
- return parseNamesOfTargetsFromPlist (*dict) == getNamesOfTargets();
-
- return false;
- }
-
- //==============================================================================
- struct AppIconType
- {
- const char* idiom;
- const char* sizeString;
- const char* filename;
- const char* scale;
- int size;
- };
-
- static Array getiOSAppIconTypes()
- {
- AppIconType types[] =
- {
- { "iphone", "29x29", "Icon-29.png", "1x", 29 },
- { "iphone", "29x29", "Icon-29@2x.png", "2x", 58 },
- { "iphone", "29x29", "Icon-29@3x.png", "3x", 87 },
- { "iphone", "40x40", "Icon-Spotlight-40@2x.png", "2x", 80 },
- { "iphone", "40x40", "Icon-Spotlight-40@3x.png", "3x", 120 },
- { "iphone", "57x57", "Icon.png", "1x", 57 },
- { "iphone", "57x57", "Icon@2x.png", "2x", 114 },
- { "iphone", "60x60", "Icon-60@2x.png", "2x", 120 },
- { "iphone", "60x60", "Icon-@3x.png", "3x", 180 },
- { "ipad", "29x29", "Icon-Small-1.png", "1x", 29 },
- { "ipad", "29x29", "Icon-Small@2x-1.png", "2x", 58 },
- { "ipad", "40x40", "Icon-Spotlight-40.png", "1x", 40 },
- { "ipad", "40x40", "Icon-Spotlight-40@2x-1.png", "2x", 80 },
- { "ipad", "50x50", "Icon-Small-50.png", "1x", 50 },
- { "ipad", "50x50", "Icon-Small-50@2x.png", "2x", 100 },
- { "ipad", "72x72", "Icon-72.png", "1x", 72 },
- { "ipad", "72x72", "Icon-72@2x.png", "2x", 144 },
- { "ipad", "76x76", "Icon-76.png", "1x", 76 },
- { "ipad", "76x76", "Icon-76@2x.png", "2x", 152 },
- { "ipad", "83.5x83.5", "Icon-83.5@2x.png", "2x", 167 }
- };
-
- return Array (types, numElementsInArray (types));
- }
-
- static String getiOSAppIconContents()
- {
- const Array types (getiOSAppIconTypes());
- var images;
-
- for (int i = 0; i < types.size(); ++i)
- {
- AppIconType type = types.getUnchecked(i);
-
- DynamicObject::Ptr d = new DynamicObject();
- d->setProperty ("idiom", type.idiom);
- d->setProperty ("size", type.sizeString);
- d->setProperty ("filename", type.filename);
- d->setProperty ("scale", type.scale);
- images.append (var (d));
- }
-
- return getiOSAssetContents (images);
- }
-
- String getProjectObjectAttributes() const
- {
- String attributes;
-
- attributes << "{ LastUpgradeCheck = 0440; ";
-
- if (projectType.isGUIApplication() || projectType.isAudioPlugin())
- {
- attributes << "TargetAttributes = { ";
-
- for (auto& target : targets)
- attributes << target->getTargetAttributes();
-
- attributes << " }; ";
- }
-
- attributes << "}";
-
- return attributes;
- }
-
- //==============================================================================
- struct ImageType
- {
- const char* orientation;
- const char* idiom;
- const char* subtype;
- const char* extent;
- const char* scale;
- const char* filename;
- int width;
- int height;
- };
-
- static Array getiOSLaunchImageTypes()
- {
- ImageType types[] =
- {
- { "portrait", "iphone", nullptr, "full-screen", "2x", "LaunchImage-iphone-2x.png", 640, 960 },
- { "portrait", "iphone", "retina4", "full-screen", "2x", "LaunchImage-iphone-retina4.png", 640, 1136 },
- { "portrait", "ipad", nullptr, "full-screen", "1x", "LaunchImage-ipad-portrait-1x.png", 768, 1024 },
- { "landscape","ipad", nullptr, "full-screen", "1x", "LaunchImage-ipad-landscape-1x.png", 1024, 768 },
- { "portrait", "ipad", nullptr, "full-screen", "2x", "LaunchImage-ipad-portrait-2x.png", 1536, 2048 },
- { "landscape","ipad", nullptr, "full-screen", "2x", "LaunchImage-ipad-landscape-2x.png", 2048, 1536 }
- };
-
- return Array (types, numElementsInArray (types));
- }
-
- static String getiOSLaunchImageContents()
- {
- const Array types (getiOSLaunchImageTypes());
- var images;
-
- for (int i = 0; i < types.size(); ++i)
- {
- const ImageType& type = types.getReference(i);
-
- DynamicObject::Ptr d = new DynamicObject();
- d->setProperty ("orientation", type.orientation);
- d->setProperty ("idiom", type.idiom);
- d->setProperty ("extent", type.extent);
- d->setProperty ("minimum-system-version", "7.0");
- d->setProperty ("scale", type.scale);
- d->setProperty ("filename", type.filename);
-
- if (type.subtype != nullptr)
- d->setProperty ("subtype", type.subtype);
-
- images.append (var (d));
- }
-
- return getiOSAssetContents (images);
- }
-
- static void createiOSLaunchImageFiles (const File& launchImageSet)
- {
- const Array types (getiOSLaunchImageTypes());
-
- for (int i = 0; i < types.size(); ++i)
- {
- const ImageType& type = types.getReference(i);
-
- Image image (Image::ARGB, type.width, type.height, true); // (empty black image)
- image.clear (image.getBounds(), Colours::black);
-
- MemoryOutputStream pngData;
- PNGImageFormat pngFormat;
- pngFormat.writeImageToStream (image, pngData);
- overwriteFileIfDifferentOrThrow (launchImageSet.getChildFile (type.filename), pngData);
- }
- }
-
- //==============================================================================
- static String getiOSAssetContents (var images)
- {
- DynamicObject::Ptr v (new DynamicObject());
-
- var info (new DynamicObject());
- info.getDynamicObject()->setProperty ("version", 1);
- info.getDynamicObject()->setProperty ("author", "xcode");
-
- v->setProperty ("images", images);
- v->setProperty ("info", info);
-
- return JSON::toString (var (v));
- }
-
- void createXcassetsFolderFromIcons() const
- {
- const File assets (getTargetFolder().getChildFile (project.getProjectFilenameRoot())
- .getChildFile ("Images.xcassets"));
- const File iconSet (assets.getChildFile ("AppIcon.appiconset"));
- const File launchImage (assets.getChildFile ("LaunchImage.launchimage"));
-
- overwriteFileIfDifferentOrThrow (iconSet.getChildFile ("Contents.json"), getiOSAppIconContents());
- createiOSIconFiles (iconSet);
-
- overwriteFileIfDifferentOrThrow (launchImage.getChildFile ("Contents.json"), getiOSLaunchImageContents());
- createiOSLaunchImageFiles (launchImage);
-
- RelativePath assetsPath (assets, getTargetFolder(), RelativePath::buildTargetFolder);
- addFileReference (assetsPath.toUnixStyle());
- resourceIDs.add (addBuildFile (assetsPath, false, false));
- resourceFileRefs.add (createFileRefID (assetsPath));
- }
-
- //==============================================================================
- static String indentBracedList (const StringArray& list) { return "{" + indentList (list, ";", 0, true) + " }"; }
- static String indentParenthesisedList (const StringArray& list) { return "(" + indentList (list, ",", 1, false) + " )"; }
-
- static String indentList (const StringArray& list, const String& separator, int extraTabs, bool shouldSort)
- {
- if (list.size() == 0)
- return " ";
-
- const String tabs ("\n" + String::repeatedString ("\t", extraTabs + 4));
-
- if (shouldSort)
- {
- StringArray sorted (list);
- sorted.sort (true);
-
- return tabs + sorted.joinIntoString (separator + tabs) + separator;
- }
-
- return tabs + list.joinIntoString (separator + tabs) + separator;
- }
-
- String createID (String rootString) const
- {
- if (rootString.startsWith ("${"))
- rootString = rootString.fromFirstOccurrenceOf ("}/", false, false);
-
- rootString += project.getProjectUID();
-
- return MD5 (rootString.toUTF8()).toHexString().substring (0, 24).toUpperCase();
- }
-
- String createFileRefID (const RelativePath& path) const { return createFileRefID (path.toUnixStyle()); }
- String createFileRefID (const String& path) const { return createID ("__fileref_" + path); }
- String getIDForGroup (const Project::Item& item) const { return createID (item.getID()); }
-
- bool shouldFileBeCompiledByDefault (const RelativePath& file) const override
- {
- return file.hasFileExtension (sourceFileExtensions);
- }
-
- static String getOSXVersionName (int version)
- {
- jassert (version >= 4);
- return "10." + String (version);
- }
-
- static String getSDKName (int version)
- {
- return getOSXVersionName (version) + " SDK";
- }
-
- void initialiseDependencyPathValues()
- {
- vst2Path.referTo (Value (new DependencyPathValueSource (getSetting (Ids::vstFolder), Ids::vst2Path, TargetOS::osx)));
- vst3Path.referTo (Value (new DependencyPathValueSource (getSetting (Ids::vst3Folder), Ids::vst3Path, TargetOS::osx)));
- aaxPath. referTo (Value (new DependencyPathValueSource (getSetting (Ids::aaxFolder), Ids::aaxPath, TargetOS::osx)));
- rtasPath.referTo (Value (new DependencyPathValueSource (getSetting (Ids::rtasFolder), Ids::rtasPath, TargetOS::osx)));
- }
-
- void addRTASPluginSettings()
- {
- xcodeCanUseDwarf = false;
-
- extraSearchPaths.add ("$(DEVELOPER_DIR)/Headers/FlatCarbon");
- extraSearchPaths.add ("$(SDKROOT)/Developer/Headers/FlatCarbon");
-
- static const char* p[] = { "AlturaPorts/TDMPlugIns/PlugInLibrary/Controls",
- "AlturaPorts/TDMPlugIns/PlugInLibrary/CoreClasses",
- "AlturaPorts/TDMPlugIns/PlugInLibrary/DSPClasses",
- "AlturaPorts/TDMPlugIns/PlugInLibrary/EffectClasses",
- "AlturaPorts/TDMPlugIns/PlugInLibrary/MacBuild",
- "AlturaPorts/TDMPlugIns/PlugInLibrary/Meters",
- "AlturaPorts/TDMPlugIns/PlugInLibrary/ProcessClasses",
- "AlturaPorts/TDMPlugIns/PlugInLibrary/ProcessClasses/Interfaces",
- "AlturaPorts/TDMPlugIns/PlugInLibrary/RTASP_Adapt",
- "AlturaPorts/TDMPlugIns/PlugInLibrary/Utilities",
- "AlturaPorts/TDMPlugIns/PlugInLibrary/ViewClasses",
- "AlturaPorts/TDMPlugIns/DSPManager/**",
- "AlturaPorts/TDMPlugIns/SupplementalPlugInLib/Encryption",
- "AlturaPorts/TDMPlugIns/SupplementalPlugInLib/GraphicsExtensions",
- "AlturaPorts/TDMPlugIns/common/**",
- "AlturaPorts/TDMPlugIns/common/PI_LibInterface",
- "AlturaPorts/TDMPlugIns/PACEProtection/**",
- "AlturaPorts/TDMPlugIns/SignalProcessing/**",
- "AlturaPorts/OMS/Headers",
- "AlturaPorts/Fic/Interfaces/**",
- "AlturaPorts/Fic/Source/SignalNets",
- "AlturaPorts/DSIPublicInterface/PublicHeaders",
- "DAEWin/Include",
- "AlturaPorts/DigiPublic/Interfaces",
- "AlturaPorts/DigiPublic",
- "AlturaPorts/NewFileLibs/DOA",
- "AlturaPorts/NewFileLibs/Cmn",
- "xplat/AVX/avx2/avx2sdk/inc",
- "xplat/AVX/avx2/avx2sdk/utils" };
-
- RelativePath rtasFolder (getRTASPathValue().toString(), RelativePath::projectFolder);
-
- for (int i = 0; i < numElementsInArray (p); ++i)
- addToExtraSearchPaths (rtasFolder.getChildFile (p[i]));
- }
-
- JUCE_DECLARE_NON_COPYABLE (XCodeProjectExporter)
-};
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2015 - ROLI Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#include "../Application/jucer_Application.h"
+#include "jucer_TextWithDefaultPropertyComponent.h"
+
+namespace
+{
+ const char* const osxVersionDefault = "default";
+ const int oldestSDKVersion = 5;
+ const int currentSDKVersion = 11;
+
+ const char* const osxArch_Default = "default";
+ const char* const osxArch_Native = "Native";
+ const char* const osxArch_32BitUniversal = "32BitUniversal";
+ const char* const osxArch_64BitUniversal = "64BitUniversal";
+ const char* const osxArch_64Bit = "64BitIntel";
+}
+
+//==============================================================================
+class XCodeProjectExporter : public ProjectExporter
+{
+public:
+ //==============================================================================
+ static const char* getNameMac() { return "Xcode (MacOSX)"; }
+ static const char* getNameiOS() { return "Xcode (iOS)"; }
+ static const char* getValueTreeTypeName (bool iOS) { return iOS ? "XCODE_IPHONE" : "XCODE_MAC"; }
+
+ //==============================================================================
+ XCodeProjectExporter (Project& p, const ValueTree& t, const bool isIOS)
+ : ProjectExporter (p, t),
+ xcodeCanUseDwarf (true),
+ iOS (isIOS)
+ {
+ name = iOS ? getNameiOS() : getNameMac();
+
+ if (getTargetLocationString().isEmpty())
+ getTargetLocationValue() = getDefaultBuildsRootFolder() + (iOS ? "iOS" : "MacOSX");
+
+ initialiseDependencyPathValues();
+
+ if (iOS)
+ {
+ if (getScreenOrientationValue().toString().isEmpty())
+ getScreenOrientationValue() = "portraitlandscape";
+ }
+ }
+
+ static XCodeProjectExporter* createForSettings (Project& project, const ValueTree& settings)
+ {
+ if (settings.hasType (getValueTreeTypeName (false))) return new XCodeProjectExporter (project, settings, false);
+ if (settings.hasType (getValueTreeTypeName (true))) return new XCodeProjectExporter (project, settings, true);
+
+ return nullptr;
+ }
+
+ //==============================================================================
+ Value getPListToMergeValue() { return getSetting ("customPList"); }
+ String getPListToMergeString() const { return settings ["customPList"]; }
+
+ Value getExtraFrameworksValue() { return getSetting (Ids::extraFrameworks); }
+ String getExtraFrameworksString() const { return settings [Ids::extraFrameworks]; }
+
+ Value getPostBuildScriptValue() { return getSetting (Ids::postbuildCommand); }
+ String getPostBuildScript() const { return settings [Ids::postbuildCommand]; }
+
+ Value getPreBuildScriptValue() { return getSetting (Ids::prebuildCommand); }
+ String getPreBuildScript() const { return settings [Ids::prebuildCommand]; }
+
+ Value getScreenOrientationValue() { return getSetting (Ids::iosScreenOrientation); }
+ String getScreenOrientationString() const { return settings [Ids::iosScreenOrientation]; }
+
+ Value getCustomResourceFoldersValue() { return getSetting (Ids::customXcodeResourceFolders); }
+ String getCustomResourceFoldersString() const { return getSettingString (Ids::customXcodeResourceFolders).replaceCharacters ("\r\n", "::"); }
+
+ Value getCustomXcassetsFolderValue() { return getSetting (Ids::customXcassetsFolder); }
+ String getCustomXcassetsFolderString() const { return settings [Ids::customXcassetsFolder]; }
+
+ Value getInAppPurchasesValue() { return getSetting (Ids::iosInAppPurchases); }
+ bool isInAppPurchasesEnabled() const { return settings [Ids::iosInAppPurchases]; }
+ Value getBackgroundAudioValue() { return getSetting (Ids::iosBackgroundAudio); }
+ bool isBackgroundAudioEnabled() const { return settings [Ids::iosBackgroundAudio]; }
+ Value getBackgroundBleValue() { return getSetting (Ids::iosBackgroundBle); }
+ bool isBackgroundBleEnabled() const { return settings [Ids::iosBackgroundBle]; }
+
+ Value getIosDevelopmentTeamIDValue() { return getSetting (Ids::iosDevelopmentTeamID); }
+ String getIosDevelopmentTeamIDString() const { return settings [Ids::iosDevelopmentTeamID]; }
+
+ bool usesMMFiles() const override { return true; }
+ bool canCopeWithDuplicateFiles() override { return true; }
+ bool supportsUserDefinedConfigurations() const override { return true; }
+
+ bool isXcode() const override { return true; }
+ bool isVisualStudio() const override { return false; }
+ bool isCodeBlocks() const override { return false; }
+ bool isMakefile() const override { return false; }
+ bool isAndroidStudio() const override { return false; }
+ bool isAndroidAnt() const override { return false; }
+
+ bool isAndroid() const override { return false; }
+ bool isWindows() const override { return false; }
+ bool isLinux() const override { return false; }
+ bool isOSX() const override { return ! iOS; }
+ bool isiOS() const override { return iOS; }
+
+ bool supportsVST() const override { return ! iOS; }
+ bool supportsVST3() const override { return ! iOS; }
+ bool supportsAAX() const override { return ! iOS; }
+ bool supportsRTAS() const override { return ! iOS; }
+ bool supportsAU() const override { return ! iOS; }
+ bool supportsAUv3() const override { return true; }
+ bool supportsStandalone() const override { return true; }
+
+ void createExporterProperties (PropertyListBuilder& props) override
+ {
+ if (iOS)
+ {
+ props.add (new TextPropertyComponent (getCustomXcassetsFolderValue(), "Custom Xcassets folder", 128, false),
+ "If this field is not empty, your Xcode project will use the custom xcassets folder specified here "
+ "for the app icons and launchimages, and will ignore the Icon files specified above.");
+ }
+
+ props.add (new TextPropertyComponent (getCustomResourceFoldersValue(), "Custom Xcode Resource folders", 8192, true),
+ "You can specify a list of custom resource folders here (separated by newlines or whitespace). "
+ "References to these folders will then be added to the Xcode resources. "
+ "This way you can specify them for OS X and iOS separately, and modify the content of the resource folders "
+ "without re-saving the Projucer project.");
+
+ if (iOS)
+ {
+ static const char* orientations[] = { "Portrait and Landscape", "Portrait", "Landscape", nullptr };
+ static const char* orientationValues[] = { "portraitlandscape", "portrait", "landscape", nullptr };
+
+ props.add (new ChoicePropertyComponent (getScreenOrientationValue(), "Screen orientation",StringArray (orientations), Array (orientationValues)),
+ "The screen orientations that this app should support");
+
+ props.add (new BooleanPropertyComponent (getSetting ("UIFileSharingEnabled"), "File Sharing Enabled", "Enabled"),
+ "Enable this to expose your app's files to iTunes.");
+
+ props.add (new BooleanPropertyComponent (getSetting ("UIStatusBarHidden"), "Status Bar Hidden", "Enabled"),
+ "Enable this to disable the status bar in your app.");
+
+ props.add (new BooleanPropertyComponent (getInAppPurchasesValue(), "In-App purchases capability", "Enabled"),
+ "Enable this to grant your app the capability for in-app purchases. "
+ "This option requires that you specify a valid Development Team ID.");
+
+ props.add (new BooleanPropertyComponent (getBackgroundAudioValue(), "Audio background capability", "Enabled"),
+ "Enable this to grant your app the capability to access audio when in background mode.");
+
+ props.add (new BooleanPropertyComponent (getBackgroundBleValue(), "Bluetooth MIDI background capability", "Enabled"),
+ "Enable this to grant your app the capability to connect to Bluetooth LE devices when in background mode.");
+ }
+ else if (projectType.isGUIApplication())
+ {
+ props.add (new TextPropertyComponent (getSetting ("documentExtensions"), "Document file extensions", 128, false),
+ "A comma-separated list of file extensions for documents that your app can open. "
+ "Using a leading '.' is optional, and the extensions are not case-sensitive.");
+ }
+
+ props.add (new TextPropertyComponent (getPListToMergeValue(), "Custom PList", 8192, true),
+ "You can paste the contents of an XML PList file in here, and the settings that it contains will override any "
+ "settings that the Projucer creates. BEWARE! When doing this, be careful to remove from the XML any "
+ "values that you DO want the Projucer to change!");
+
+ props.add (new TextPropertyComponent (getExtraFrameworksValue(), "Extra Frameworks", 2048, false),
+ "A comma-separated list of extra frameworks that should be added to the build. "
+ "(Don't include the .framework extension in the name)");
+
+ props.add (new TextPropertyComponent (getPreBuildScriptValue(), "Pre-build shell script", 32768, true),
+ "Some shell-script that will be run before a build starts.");
+
+ props.add (new TextPropertyComponent (getPostBuildScriptValue(), "Post-build shell script", 32768, true),
+ "Some shell-script that will be run after a build completes.");
+
+ props.add (new TextPropertyComponent (getIosDevelopmentTeamIDValue(), "Development Team ID", 10, false),
+ "The Development Team ID to be used for setting up code-signing your iOS app. This is a ten-character "
+ "string (for example, \"S7B6T5XJ2Q\") that describes the distribution certificate Apple issued to you. "
+ "You can find this string in the OS X app Keychain Access under \"Certificates\".");
+ }
+
+ bool launchProject() override
+ {
+ #if JUCE_MAC
+ return getProjectBundle().startAsProcess();
+ #else
+ return false;
+ #endif
+ }
+
+ bool canLaunchProject() override
+ {
+ #if JUCE_MAC
+ return true;
+ #else
+ return false;
+ #endif
+ }
+
+ //==============================================================================
+ void create (const OwnedArray&) const override
+ {
+ for (auto& target : targets)
+ if (target->xcodeCreatePList)
+ target->infoPlistFile = getTargetFolder().getChildFile (target->getInfoPlistName());
+
+ menuNibFile = getTargetFolder().getChildFile ("RecentFilesMenuTemplate.nib");
+
+ createIconFile();
+
+ File projectBundle (getProjectBundle());
+ createDirectoryOrThrow (projectBundle);
+
+ createObjects();
+
+ File projectFile (projectBundle.getChildFile ("project.pbxproj"));
+
+ {
+ MemoryOutputStream mo;
+ writeProjectFile (mo);
+ overwriteFileIfDifferentOrThrow (projectFile, mo);
+ }
+
+ writeInfoPlistFiles();
+
+ // Deleting the .rsrc files can be needed to force Xcode to update the version number.
+ deleteRsrcFiles();
+
+ if (! ProjucerApplication::getApp().isRunningCommandLine)
+ {
+ // Workaround for a bug where Xcode thinks the project is invalid if opened immedietely
+ // after writing
+ Thread::sleep (2000);
+ }
+ }
+
+ //==============================================================================
+ void addPlatformSpecificSettingsForProjectType (const ProjectType& type) override
+ {
+ if (type.isGUIApplication())
+ targets.add (new Target (Target::GUIApp, *this));
+
+ else if (type.isCommandLineApp())
+ targets.add (new Target (Target::ConsoleApp, *this));
+
+ else if (type.isStaticLibrary())
+ targets.add (new Target (Target::StaticLibrary, *this));
+
+ else if (type.isDynamicLibrary())
+ targets.add (new Target (Target::DynamicLibrary, *this));
+
+ else if (type.isAudioPlugin())
+ {
+ if (project.shouldBuildVST().getValue() && supportsVST())
+ targets.add (new Target (Target::VSTPlugIn, *this));
+
+ if (project.shouldBuildVST3().getValue() && supportsVST3())
+ targets.add (new Target (Target::VST3PlugIn, *this));
+
+ if (project.shouldBuildAU().getValue() && supportsAU())
+ targets.add (new Target (Target::AudioUnitPlugIn, *this));
+
+ if (project.shouldBuildAUv3().getValue())
+ targets.add (new Target (Target::AudioUnitv3PlugIn, *this));
+
+ if (project.shouldBuildAAX().getValue() && supportsAAX())
+ targets.add (new Target (Target::AAXPlugIn, *this));
+
+ if (project.shouldBuildStandalone().getValue())
+ targets.add (new Target (Target::StandalonePlugIn, *this));
+
+ if (project.shouldBuildRTAS().getValue() && supportsRTAS())
+ {
+ targets.add (new Target (Target::RTASPlugIn, *this));
+ addRTASPluginSettings();
+ }
+
+ if (targets.size() > 0)
+ targets.add (new Target (Target::SharedCodeTarget, *this));
+ }
+
+ if (targets.size() > 1)
+ targets.insert (0, new Target (Target::AggregateTarget, *this));
+
+ // If you hit this assert, you tried to generate a project for an exporter
+ // that does not support any of your targets!
+ jassert (targets.size() > 0);
+ }
+
+ void updateDeprecatedProjectSettingsInteractively() override
+ {
+ if (hasInvalidPostBuildScript())
+ {
+ String alertWindowText = iOS ? "Your Xcode (iOS) Exporter settings use an invalid post-build script. Click 'Update' to remove it."
+ : "Your Xcode (OSX) Exporter settings use a pre-JUCE 4.2 post-build script to move the plug-in binaries to their plug-in install folders.\n\n"
+ "Since JUCE 4.2, this is instead done using \"AU/VST/VST2/AAX/RTAS Binary Location\" in the Xcode (OS X) configuration settings.\n\n"
+ "Click 'Update' to remove the script (otherwise your plug-in may not compile correctly).";
+
+ if (AlertWindow::showOkCancelBox (AlertWindow::WarningIcon,
+ "Project settings: " + project.getDocumentTitle(),
+ alertWindowText, "Update", "Cancel", nullptr, nullptr))
+ getPostBuildScriptValue() = var();
+ }
+ }
+
+ bool hasInvalidPostBuildScript() const
+ {
+ // check whether the script is identical to the old one that the Introjucer used to auto-generate
+ return (MD5 (getPostBuildScript().toUTF8()).toHexString() == "265ac212a7e734c5bbd6150e1eae18a1");
+ }
+
+protected:
+ //==============================================================================
+ class XcodeBuildConfiguration : public BuildConfiguration
+ {
+ public:
+ XcodeBuildConfiguration (Project& p, const ValueTree& t, const bool isIOS, const ProjectExporter& e)
+ : BuildConfiguration (p, t, e),
+ iOS (isIOS),
+ osxSDKVersion (config, Ids::osxSDK, nullptr, "default"),
+ osxDeploymentTarget (config, Ids::osxCompatibility, nullptr, "default"),
+ iosDeploymentTarget (config, Ids::iosCompatibility, nullptr, "default"),
+ osxArchitecture (config, Ids::osxArchitecture, nullptr, "default"),
+ customXcodeFlags (config, Ids::customXcodeFlags, nullptr),
+ cppLanguageStandard (config, Ids::cppLanguageStandard, nullptr),
+ cppStandardLibrary (config, Ids::cppLibType, nullptr),
+ codeSignIdentity (config, Ids::codeSigningIdentity, nullptr, iOS ? "iPhone Developer" : "Mac Developer"),
+ fastMathEnabled (config, Ids::fastMath, nullptr),
+ linkTimeOptimisationEnabled (config, Ids::linkTimeOptimisation, nullptr),
+ vstBinaryLocation (config, Ids::xcodeVstBinaryLocation, nullptr, "$(HOME)/Library/Audio/Plug-Ins/VST/"),
+ vst3BinaryLocation (config, Ids::xcodeVst3BinaryLocation, nullptr, "$(HOME)/Library/Audio/Plug-Ins/VST3/"),
+ auBinaryLocation (config, Ids::xcodeAudioUnitBinaryLocation, nullptr, "$(HOME)/Library/Audio/Plug-Ins/Components/"),
+ rtasBinaryLocation (config, Ids::xcodeRtasBinaryLocation, nullptr, "/Library/Application Support/Avid/Audio/Plug-Ins/"),
+ aaxBinaryLocation (config, Ids::xcodeAaxBinaryLocation, nullptr, "/Library/Application Support/Digidesign/Plug-Ins/")
+ {
+ }
+
+ //==========================================================================
+ bool iOS;
+
+ CachedValue osxSDKVersion, osxDeploymentTarget, iosDeploymentTarget, osxArchitecture,
+ customXcodeFlags, cppLanguageStandard, cppStandardLibrary, codeSignIdentity;
+ CachedValue fastMathEnabled, linkTimeOptimisationEnabled;
+ CachedValue vstBinaryLocation, vst3BinaryLocation, auBinaryLocation, rtasBinaryLocation, aaxBinaryLocation;
+
+ //==========================================================================
+ var getDefaultOptimisationLevel() const override { return var ((int) (isDebug() ? gccO0 : gccO3)); }
+
+ void createConfigProperties (PropertyListBuilder& props) override
+ {
+ addXcodePluginInstallPathProperties (props);
+ addGCCOptimisationProperty (props);
+
+ if (iOS)
+ {
+ const char* iosVersions[] = { "Use Default", "7.0", "7.1", "8.0", "8.1", "8.2", "8.3", "8.4", "9.0", "9.1", "9.2", "9.3", 0 };
+ const char* iosVersionValues[] = { osxVersionDefault, "7.0", "7.1", "8.0", "8.1", "8.2", "8.3", "8.4", "9.0", "9.1", "9.2", "9.3", 0 };
+
+ props.add (new ChoicePropertyComponent (iosDeploymentTarget.getPropertyAsValue(), "iOS Deployment Target",
+ StringArray (iosVersions), Array (iosVersionValues)),
+ "The minimum version of iOS that the target binary will run on.");
+ }
+ else
+ {
+ StringArray sdkVersionNames, osxVersionNames;
+ Array versionValues;
+
+ sdkVersionNames.add ("Use Default");
+ osxVersionNames.add ("Use Default");
+ versionValues.add (osxVersionDefault);
+
+ for (int ver = oldestSDKVersion; ver <= currentSDKVersion; ++ver)
+ {
+ sdkVersionNames.add (getSDKName (ver));
+ osxVersionNames.add (getOSXVersionName (ver));
+ versionValues.add (getSDKName (ver));
+ }
+
+ props.add (new ChoicePropertyComponent (osxSDKVersion.getPropertyAsValue(), "OSX Base SDK Version", sdkVersionNames, versionValues),
+ "The version of OSX to link against in the XCode build.");
+
+ props.add (new ChoicePropertyComponent (osxDeploymentTarget.getPropertyAsValue(), "OSX Deployment Target", osxVersionNames, versionValues),
+ "The minimum version of OSX that the target binary will be compatible with.");
+
+ const char* osxArch[] = { "Use Default", "Native architecture of build machine",
+ "Universal Binary (32-bit)", "Universal Binary (32/64-bit)", "64-bit Intel", 0 };
+ const char* osxArchValues[] = { osxArch_Default, osxArch_Native, osxArch_32BitUniversal,
+ osxArch_64BitUniversal, osxArch_64Bit, 0 };
+
+ props.add (new ChoicePropertyComponent (osxArchitecture.getPropertyAsValue(), "OSX Architecture",
+ StringArray (osxArch), Array (osxArchValues)),
+ "The type of OSX binary that will be produced.");
+ }
+
+ props.add (new TextPropertyComponent (customXcodeFlags.getPropertyAsValue(), "Custom Xcode flags", 8192, false),
+ "A comma-separated list of custom Xcode setting flags which will be appended to the list of generated flags, "
+ "e.g. MACOSX_DEPLOYMENT_TARGET_i386 = 10.5, VALID_ARCHS = \"ppc i386 x86_64\"");
+
+ const char* cppLanguageStandardNames[] = { "Use Default", "C++98", "GNU++98", "C++11", "GNU++11", "C++14", "GNU++14", nullptr };
+ Array cppLanguageStandardValues;
+ cppLanguageStandardValues.add (var::null);
+ cppLanguageStandardValues.add ("c++98");
+ cppLanguageStandardValues.add ("gnu++98");
+ cppLanguageStandardValues.add ("c++11");
+ cppLanguageStandardValues.add ("gnu++11");
+ cppLanguageStandardValues.add ("c++14");
+ cppLanguageStandardValues.add ("gnu++14");
+
+ props.add (new ChoicePropertyComponent (cppLanguageStandard.getPropertyAsValue(), "C++ Language Standard",
+ StringArray (cppLanguageStandardNames), cppLanguageStandardValues),
+ "The standard of the C++ language that will be used for compilation.");
+
+ const char* cppLibNames[] = { "Use Default", "LLVM libc++", "GNU libstdc++", nullptr };
+ Array cppLibValues;
+ cppLibValues.add (var::null);
+ cppLibValues.add ("libc++");
+ cppLibValues.add ("libstdc++");
+
+ props.add (new ChoicePropertyComponent (cppStandardLibrary.getPropertyAsValue(), "C++ Library", StringArray (cppLibNames), cppLibValues),
+ "The type of C++ std lib that will be linked.");
+
+ props.add (new TextWithDefaultPropertyComponent (codeSignIdentity, "Code-signing Identity", 1024),
+ "The name of a code-signing identity for Xcode to apply.");
+
+ props.add (new BooleanPropertyComponent (fastMathEnabled.getPropertyAsValue(), "Relax IEEE compliance", "Enabled"),
+ "Enable this to use FAST_MATH non-IEEE mode. (Warning: this can have unexpected results!)");
+
+ props.add (new BooleanPropertyComponent (linkTimeOptimisationEnabled.getPropertyAsValue(), "Link-Time Optimisation", "Enabled"),
+ "Enable this to perform link-time code generation. This is recommended for release builds.");
+ }
+
+ private:
+ //==========================================================================
+ void addXcodePluginInstallPathProperties (PropertyListBuilder& props)
+ {
+ if (project.shouldBuildVST().getValue())
+ props.add (new TextWithDefaultPropertyComponent (vstBinaryLocation, "VST Binary location", 1024),
+ "The folder in which the compiled VST binary should be placed.");
+
+ if (project.shouldBuildVST3().getValue())
+ props.add (new TextWithDefaultPropertyComponent (vst3BinaryLocation, "VST3 Binary location", 1024),
+ "The folder in which the compiled VST3 binary should be placed.");
+
+ if (project.shouldBuildAU().getValue())
+ props.add (new TextWithDefaultPropertyComponent (auBinaryLocation, "AU Binary location", 1024),
+ "The folder in which the compiled AU binary should be placed.");
+
+ if (project.shouldBuildRTAS().getValue())
+ props.add (new TextWithDefaultPropertyComponent (rtasBinaryLocation, "RTAS Binary location", 1024),
+ "The folder in which the compiled RTAS binary should be placed.");
+
+ if (project.shouldBuildAAX().getValue())
+ props.add (new TextWithDefaultPropertyComponent (aaxBinaryLocation, "AAX Binary location", 1024),
+ "The folder in which the compiled AAX binary should be placed.");
+ }
+ };
+
+ BuildConfiguration::Ptr createBuildConfig (const ValueTree& v) const override
+ {
+ return new XcodeBuildConfiguration (project, v, iOS, *this);
+ }
+
+public:
+ //==============================================================================
+ /* The numbers for these enum values are defined by Xcode for the different
+ possible destinations of a "copy files" post-build step.
+ */
+ enum XcodeCopyFilesDestinationIDs
+ {
+ kWrapperFolder = 1,
+ kExecutablesFolder = 6,
+ kResourcesFolder = 7,
+ kFrameworksFolder = 10,
+ kSharedFrameworksFolder = 11,
+ kSharedSupportFolder = 12,
+ kPluginsFolder = 13,
+ kJavaResourcesFolder = 15,
+ kXPCServicesFolder = 16
+ };
+
+ //==============================================================================
+ struct Target
+ {
+ enum Type
+ {
+ GUIApp = 0,
+ ConsoleApp = 1,
+ StaticLibrary = 2,
+ DynamicLibrary = 3,
+
+ VSTPlugIn = 10,
+ VST3PlugIn = 11,
+ AAXPlugIn = 12,
+ RTASPlugIn = 13,
+ AudioUnitPlugIn = 14,
+ AudioUnitv3PlugIn = 15,
+ StandalonePlugIn = 16,
+
+ SharedCodeTarget = 20, // internal
+ AggregateTarget = 21,
+
+ unspecified = 30
+ };
+
+ //==============================================================================
+ Target (Type targetType, const XCodeProjectExporter& exporter)
+ : type (targetType),
+ owner (exporter)
+ {
+ switch (type)
+ {
+ case GUIApp:
+ xcodeIsBundle = false;
+ xcodeIsExecutable = true;
+ xcodeCreatePList = true;
+ xcodePackageType = "APPL";
+ xcodeBundleSignature = "????";
+ xcodeFileType = "wrapper.application";
+ xcodeBundleExtension = ".app";
+ xcodeProductType = "com.apple.product-type.application";
+ xcodeCopyToProductInstallPathAfterBuild = false;
+ break;
+
+ case ConsoleApp:
+ xcodeIsBundle = false;
+ xcodeIsExecutable = true;
+ xcodeCreatePList = false;
+ xcodeFileType = "compiled.mach-o.executable";
+ xcodeBundleExtension = String::empty;
+ xcodeProductType = "com.apple.product-type.tool";
+ xcodeCopyToProductInstallPathAfterBuild = false;
+ break;
+
+ case StaticLibrary:
+ xcodeIsBundle = false;
+ xcodeIsExecutable = false;
+ xcodeCreatePList = false;
+ xcodeFileType = "archive.ar";
+ xcodeProductType = "com.apple.product-type.library.static";
+ xcodeCopyToProductInstallPathAfterBuild = false;
+ break;
+
+ case DynamicLibrary:
+ xcodeIsBundle = false;
+ xcodeIsExecutable = false;
+ xcodeCreatePList = false;
+ xcodeFileType = "compiled.mach-o.dylib";
+ xcodeProductType = "com.apple.product-type.library.dynamic";
+ xcodeBundleExtension = ".dylib";
+ xcodeCopyToProductInstallPathAfterBuild = false;
+
+ break;
+
+ case VSTPlugIn:
+ xcodeIsBundle = true;
+ xcodeIsExecutable = false;
+ xcodeCreatePList = true;
+ xcodePackageType = "BNDL";
+ xcodeBundleSignature = "????";
+ xcodeFileType = "wrapper.cfbundle";
+ xcodeBundleExtension = ".vst";
+ xcodeProductType = "com.apple.product-type.bundle";
+ xcodeCopyToProductInstallPathAfterBuild = true;
+
+ break;
+
+ case VST3PlugIn:
+ xcodeIsBundle = true;
+ xcodeIsExecutable = false;
+ xcodeCreatePList = true;
+ xcodePackageType = "BNDL";
+ xcodeBundleSignature = "????";
+ xcodeFileType = "wrapper.cfbundle";
+ xcodeBundleExtension = ".vst3";
+ xcodeProductType = "com.apple.product-type.bundle";
+ xcodeCopyToProductInstallPathAfterBuild = true;
+
+ break;
+
+ case AudioUnitPlugIn:
+ xcodeIsBundle = true;
+ xcodeIsExecutable = false;
+ xcodeCreatePList = true;
+ xcodePackageType = "BNDL";
+ xcodeBundleSignature = "????";
+ xcodeFileType = "wrapper.cfbundle";
+ xcodeBundleExtension = ".component";
+ xcodeProductType = "com.apple.product-type.bundle";
+ xcodeCopyToProductInstallPathAfterBuild = true;
+
+ addExtraAudioUnitTargetSettings();
+ break;
+
+ case StandalonePlugIn:
+ xcodeIsBundle = false;
+ xcodeIsExecutable = true;
+ xcodeCreatePList = true;
+ xcodePackageType = "APPL";
+ xcodeBundleSignature = "????";
+ xcodeCreatePList = true;
+ xcodeFileType = "wrapper.application";
+ xcodeBundleExtension = ".app";
+ xcodeProductType = "com.apple.product-type.application";
+ xcodeCopyToProductInstallPathAfterBuild = false;
+ break;
+
+ case AudioUnitv3PlugIn:
+ xcodeIsBundle = false;
+ xcodeIsExecutable = false;
+ xcodeCreatePList = true;
+ xcodePackageType = "XPC!";
+ xcodeBundleSignature = "????";
+ xcodeFileType = "wrapper.app-extension";
+ xcodeBundleExtension = ".appex";
+ xcodeBundleIDSubPath = "AUv3";
+ xcodeProductType = "com.apple.product-type.app-extension";
+ xcodeCopyToProductInstallPathAfterBuild = false;
+
+ addExtraAudioUnitv3PlugInTargetSettings();
+ break;
+
+ case AAXPlugIn:
+ xcodeIsBundle = true;
+ xcodeIsExecutable = false;
+ xcodeCreatePList = true;
+ xcodePackageType = "TDMw";
+ xcodeBundleSignature = "PTul";
+ xcodeFileType = "wrapper.cfbundle";
+ xcodeBundleExtension = ".aaxplugin";
+ xcodeProductType = "com.apple.product-type.bundle";
+ xcodeCopyToProductInstallPathAfterBuild = true;
+
+ addExtraAAXTargetSettings();
+ break;
+
+ case RTASPlugIn:
+ xcodeIsBundle = true;
+ xcodeIsExecutable = false;
+ xcodeCreatePList = true;
+ xcodePackageType = "TDMw";
+ xcodeBundleSignature = "PTul";
+ xcodeFileType = "wrapper.cfbundle";
+ xcodeBundleExtension = ".dpm";
+ xcodeProductType = "com.apple.product-type.bundle";
+ xcodeCopyToProductInstallPathAfterBuild = true;
+
+ addExtraRTASTargetSettings();
+ break;
+
+ case SharedCodeTarget:
+ xcodeIsBundle = false;
+ xcodeIsExecutable = false;
+ xcodeCreatePList = false;
+ xcodeFileType = "archive.ar";
+ xcodeProductType = "com.apple.product-type.library.static";
+ xcodeCopyToProductInstallPathAfterBuild = false;
+ break;
+
+ case AggregateTarget:
+ xcodeIsBundle = false;
+ xcodeIsExecutable = false;
+ xcodeCreatePList = false;
+ xcodeCopyToProductInstallPathAfterBuild = false;
+ break;
+
+ default:
+ // unknown target type!
+ jassertfalse;
+ break;
+ }
+ }
+
+ const char* getName() const noexcept
+ {
+ switch (type)
+ {
+ case GUIApp: return "App";
+ case ConsoleApp: return "ConsoleApp";
+ case StaticLibrary: return "Static Library";
+ case DynamicLibrary: return "Dynamic Library";
+ case VSTPlugIn: return "VST";
+ case VST3PlugIn: return "VST3";
+ case AudioUnitPlugIn: return "AU";
+ case StandalonePlugIn: return "AUv3 Standalone";
+ case AudioUnitv3PlugIn: return "AUv3 AppExtension";
+ case AAXPlugIn: return "AAX";
+ case RTASPlugIn: return "RTAS";
+ case SharedCodeTarget: return "Shared Code";
+ case AggregateTarget: return "All";
+ default: return "undefined";
+ }
+ }
+
+ String getXCodeSchemeName() const
+ {
+ return owner.projectName + " (" + getName() + ")";
+ }
+
+ bool shouldBuildVST() const { return owner.supportsVST() && owner.project.shouldBuildVST().getValue() && (type == SharedCodeTarget || type == VSTPlugIn); }
+ bool shouldBuildVST3() const { return owner.supportsVST3() && owner.project.shouldBuildVST3().getValue() && (type == SharedCodeTarget || type == VST3PlugIn); }
+ bool shouldBuildAAX() const { return owner.supportsAAX() && owner.project.shouldBuildAAX().getValue() && (type == SharedCodeTarget || type == AAXPlugIn); }
+ bool shouldBuildRTAS() const { return owner.supportsRTAS() && owner.project.shouldBuildRTAS().getValue() && (type == SharedCodeTarget || type == RTASPlugIn); }
+ bool shouldBuildAU() const { return owner.supportsAU() && owner.project.shouldBuildAU().getValue() && (type == SharedCodeTarget || type == AudioUnitPlugIn); }
+ bool shouldBuildAUv3() const { return owner.supportsAUv3() && owner.project.shouldBuildAUv3().getValue() && (type == SharedCodeTarget || type == AudioUnitv3PlugIn); }
+ bool shouldBuildStandalone() const { return owner.project.shouldBuildStandalone().getValue() && (type == SharedCodeTarget || type == StandalonePlugIn); }
+
+ String getID() const
+ {
+ return owner.createID (String ("__target") + getName());
+ }
+
+ String getInfoPlistName() const
+ {
+ return String ("Info-") + String (getName()).replace (" ", "_") + String (".plist");
+ }
+
+ String xcodePackageType, xcodeBundleSignature, xcodeBundleExtension;
+ String xcodeProductType, xcodeFileType;
+ String xcodeOtherRezFlags, xcodeExcludedFiles64Bit, xcodeBundleIDSubPath;
+ bool xcodeIsBundle, xcodeCreatePList, xcodeIsExecutable, xcodeCopyToProductInstallPathAfterBuild;
+ StringArray xcodeFrameworks, xcodeLibs;
+ Type type;
+ Array xcodeExtraPListEntries;
+ Array xcodeExtraLibrariesDebug, xcodeExtraLibrariesRelease;
+
+ StringArray frameworkIDs, buildPhaseIDs, configIDs, sourceIDs, rezFileIDs;
+ String dependencyID, mainBuildProductID;
+ File infoPlistFile;
+
+ //==============================================================================
+ void addMainBuildProduct() const
+ {
+ jassert (xcodeFileType.isNotEmpty());
+ jassert (xcodeBundleExtension.isEmpty() || xcodeBundleExtension.startsWithChar ('.'));
+
+ if (ProjectExporter::BuildConfiguration::Ptr config = owner.getConfiguration(0))
+ {
+ String productName (owner.replacePreprocessorTokens (*config, config->getTargetBinaryNameString()));
+
+ if (xcodeFileType == "archive.ar")
+ productName = getLibbedFilename (productName);
+ else
+ productName += xcodeBundleExtension;
+
+ addBuildProduct (xcodeFileType, productName);
+ }
+ }
+
+ //==============================================================================
+ void addBuildProduct (const String& fileType, const String& binaryName) const
+ {
+ ValueTree* v = new ValueTree (owner.createID (String ("__productFileID") + getName()));
+ v->setProperty ("isa", "PBXFileReference", nullptr);
+ v->setProperty ("explicitFileType", fileType, nullptr);
+ v->setProperty ("includeInIndex", (int) 0, nullptr);
+ v->setProperty ("path", sanitisePath (binaryName), nullptr);
+ v->setProperty ("sourceTree", "BUILT_PRODUCTS_DIR", nullptr);
+ owner.pbxFileReferences.add (v);
+ }
+
+ //==============================================================================
+ void addDependency()
+ {
+ jassert (dependencyID.isEmpty());
+
+ dependencyID = owner.createID (String ("__dependency") + getName());
+ ValueTree* const v = new ValueTree (dependencyID);
+
+ v->setProperty ("isa", "PBXTargetDependency", nullptr);
+ v->setProperty ("target", getID(), nullptr);
+
+ owner.misc.add (v);
+ }
+
+ String getDependencyID() const
+ {
+ jassert (dependencyID.isNotEmpty());
+
+ return dependencyID;
+ }
+
+ //==============================================================================
+ void addTargetConfig (const String& configName, const StringArray& buildSettings)
+ {
+ String configID = owner.createID (String ("targetconfigid_") + getName() + String ("_") + configName);
+
+ ValueTree* v = new ValueTree (configID);
+ v->setProperty ("isa", "XCBuildConfiguration", nullptr);
+ v->setProperty ("buildSettings", indentBracedList (buildSettings), nullptr);
+ v->setProperty (Ids::name, configName, nullptr);
+
+ configIDs.add (configID);
+ owner.targetConfigs.add (v);
+ }
+
+ //==============================================================================
+ String getTargetAttributes() const
+ {
+ String attributes;
+
+ attributes << getID() << " = { ";
+
+ String developmentTeamID = owner.getIosDevelopmentTeamIDString();
+ if (developmentTeamID.isNotEmpty())
+ attributes << "DevelopmentTeam = " << developmentTeamID << "; ";
+
+ const int inAppPurchasesEnabled = (owner.iOS && owner.isInAppPurchasesEnabled()) ? 1 : 0;
+ const int sandboxEnabled = (type == Target::AudioUnitv3PlugIn ? 1 : 0);
+
+ attributes << "SystemCapabilities = {";
+ attributes << "com.apple.InAppPurchase = { enabled = " << inAppPurchasesEnabled << "; }; ";
+ attributes << "com.apple.Sandbox = { enabled = " << sandboxEnabled << "; }; ";
+ attributes << "}; };";
+
+ return attributes;
+ }
+
+ //==============================================================================
+ ValueTree& addBuildPhase (const String& buildPhaseType, const StringArray& fileIds, const StringRef humanReadableName = StringRef())
+ {
+ String buildPhaseName = buildPhaseType + String ("_") + getName() + String ("_") + (humanReadableName.isNotEmpty() ? String (humanReadableName) : String ("resbuildphase"));
+ String buildPhaseId (owner.createID (buildPhaseName));
+
+ int n = 0;
+ while (buildPhaseIDs.contains (buildPhaseId))
+ buildPhaseId = owner.createID (buildPhaseName + String (++n));
+
+ buildPhaseIDs.add (buildPhaseId);
+
+ ValueTree* v = new ValueTree (buildPhaseId);
+ v->setProperty ("isa", buildPhaseType, nullptr);
+ v->setProperty ("buildActionMask", "2147483647", nullptr);
+ v->setProperty ("files", indentParenthesisedList (fileIds), nullptr);
+ v->setProperty ("runOnlyForDeploymentPostprocessing", (int) 0, nullptr);
+
+ if (humanReadableName.isNotEmpty())
+ v->setProperty ("name", String (humanReadableName), nullptr);
+
+ owner.misc.add (v);
+ return *v;
+ }
+
+ //==============================================================================
+ StringArray getTargetSettings (const XcodeBuildConfiguration& config) const
+ {
+ if (type == AggregateTarget)
+ // the aggregate target should not specify any settings at all!
+ // it just defines dependencies on the other targets.
+ return StringArray();
+
+ StringArray s;
+
+ String bundleIdentifier = owner.project.getBundleIdentifier().toString();
+ if (xcodeBundleIDSubPath.isNotEmpty())
+ {
+ StringArray bundleIdSegments = StringArray::fromTokens (bundleIdentifier, ".", StringRef());
+
+ jassert (bundleIdSegments.size() > 0);
+ bundleIdentifier += String (".") + bundleIdSegments[bundleIdSegments.size() - 1] + xcodeBundleIDSubPath;
+ }
+
+ s.add ("PRODUCT_BUNDLE_IDENTIFIER = " + bundleIdentifier);
+
+ const String arch ((! owner.isiOS() && type == Target::AudioUnitv3PlugIn) ? osxArch_64Bit : config.osxArchitecture.get());
+ if (arch == osxArch_Native) s.add ("ARCHS = \"$(NATIVE_ARCH_ACTUAL)\"");
+ else if (arch == osxArch_32BitUniversal) s.add ("ARCHS = \"$(ARCHS_STANDARD_32_BIT)\"");
+ else if (arch == osxArch_64BitUniversal) s.add ("ARCHS = \"$(ARCHS_STANDARD_32_64_BIT)\"");
+ else if (arch == osxArch_64Bit) s.add ("ARCHS = \"$(ARCHS_STANDARD_64_BIT)\"");
+
+ s.add ("HEADER_SEARCH_PATHS = " + owner.getHeaderSearchPaths (config));
+ s.add ("GCC_OPTIMIZATION_LEVEL = " + config.getGCCOptimisationFlag());
+
+ if (xcodeCreatePList)
+ s.add ("INFOPLIST_FILE = " + infoPlistFile.getFileName());
+
+ if (config.linkTimeOptimisationEnabled.get())
+ s.add ("LLVM_LTO = YES");
+
+ if (config.fastMathEnabled.get())
+ s.add ("GCC_FAST_MATH = YES");
+
+ const String extraFlags (owner.replacePreprocessorTokens (config, owner.getExtraCompilerFlagsString()).trim());
+ if (extraFlags.isNotEmpty())
+ s.add ("OTHER_CPLUSPLUSFLAGS = \"" + extraFlags + "\"");
+
+ String installPath = getInstallPathForConfiguration (config);
+
+ if (installPath.isNotEmpty())
+ {
+ s.add ("INSTALL_PATH = \"" + installPath + "\"");
+
+ if (xcodeCopyToProductInstallPathAfterBuild)
+ {
+ s.add ("DEPLOYMENT_LOCATION = YES");
+ s.add ("DSTROOT = /");
+ }
+ }
+
+ if (xcodeIsBundle)
+ {
+ s.add ("LIBRARY_STYLE = Bundle");
+ s.add ("WRAPPER_EXTENSION = " + xcodeBundleExtension.substring (1));
+ s.add ("GENERATE_PKGINFO_FILE = YES");
+ }
+
+ if (xcodeOtherRezFlags.isNotEmpty())
+ s.add ("OTHER_REZFLAGS = \"" + xcodeOtherRezFlags + "\"");
+
+ String configurationBuildDir = "$(PROJECT_DIR)/build/$(CONFIGURATION)";
+
+ if (config.getTargetBinaryRelativePathString().isNotEmpty())
+ {
+ // a target's position can either be defined via installPath + xcodeCopyToProductInstallPathAfterBuild
+ // (= for audio plug-ins) or using a custom binary path (for everything else), but not both (= conflict!)
+ jassert (! xcodeCopyToProductInstallPathAfterBuild);
+
+ RelativePath binaryPath (config.getTargetBinaryRelativePathString(), RelativePath::projectFolder);
+ configurationBuildDir = sanitisePath (binaryPath.rebased (owner.projectFolder, owner.getTargetFolder(), RelativePath::buildTargetFolder)
+ .toUnixStyle());
+ }
+
+ s.add ("CONFIGURATION_BUILD_DIR = " + addQuotesIfRequired (configurationBuildDir));
+
+ String gccVersion ("com.apple.compilers.llvm.clang.1_0");
+
+ if (owner.iOS)
+ {
+ s.add ("ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon");
+ s.add ("ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage");
+ }
+ else
+ {
+ const String sdk (config.osxSDKVersion.get());
+ const String sdkCompat (config.osxDeploymentTarget.get());
+
+ for (int ver = oldestSDKVersion; ver <= currentSDKVersion; ++ver)
+ {
+ if (sdk == getSDKName (ver)) s.add ("SDKROOT = macosx10." + String (ver));
+ if (sdkCompat == getSDKName (ver)) s.add ("MACOSX_DEPLOYMENT_TARGET = 10." + String (ver));
+ }
+
+ s.add ("MACOSX_DEPLOYMENT_TARGET_ppc = 10.4");
+ s.add ("SDKROOT_ppc = macosx10.5");
+
+ if (xcodeExcludedFiles64Bit.isNotEmpty())
+ {
+ s.add ("EXCLUDED_SOURCE_FILE_NAMES = \"$(EXCLUDED_SOURCE_FILE_NAMES_$(CURRENT_ARCH))\"");
+ s.add ("EXCLUDED_SOURCE_FILE_NAMES_x86_64 = " + xcodeExcludedFiles64Bit);
+ }
+ }
+
+ s.add ("GCC_VERSION = " + gccVersion);
+ s.add ("CLANG_CXX_LANGUAGE_STANDARD = \"c++0x\"");
+ s.add ("CLANG_LINK_OBJC_RUNTIME = NO");
+
+ if (! config.codeSignIdentity.isUsingDefault())
+ s.add ("CODE_SIGN_IDENTITY = " + config.codeSignIdentity.get().quoted());
+
+ if (config.cppLanguageStandard.get().isNotEmpty())
+ s.add ("CLANG_CXX_LANGUAGE_STANDARD = " + config.cppLanguageStandard.get().quoted());
+
+ if (config.cppStandardLibrary.get().isNotEmpty())
+ s.add ("CLANG_CXX_LIBRARY = " + config.cppStandardLibrary.get().quoted());
+
+ s.add ("COMBINE_HIDPI_IMAGES = YES");
+
+ {
+ StringArray linkerFlags, librarySearchPaths;
+ getLinkerFlags (config, linkerFlags, librarySearchPaths);
+
+ if (linkerFlags.size() > 0)
+ s.add ("OTHER_LDFLAGS = \"" + linkerFlags.joinIntoString (" ") + "\"");
+
+ librarySearchPaths.addArray (config.getLibrarySearchPaths());
+ librarySearchPaths = getCleanedStringArray (librarySearchPaths);
+
+ if (librarySearchPaths.size() > 0)
+ {
+ String libPaths ("LIBRARY_SEARCH_PATHS = (\"$(inherited)\"");
+
+ for (int i = 0; i < librarySearchPaths.size(); ++i)
+ libPaths += ", \"\\\"" + librarySearchPaths[i] + "\\\"\"";
+
+ s.add (libPaths + ")");
+ }
+ }
+
+ StringPairArray defines;
+
+ if (config.isDebug())
+ {
+ defines.set ("_DEBUG", "1");
+ defines.set ("DEBUG", "1");
+ s.add ("COPY_PHASE_STRIP = NO");
+ s.add ("GCC_DYNAMIC_NO_PIC = NO");
+ }
+ else
+ {
+ defines.set ("_NDEBUG", "1");
+ defines.set ("NDEBUG", "1");
+ s.add ("GCC_GENERATE_DEBUGGING_SYMBOLS = NO");
+ s.add ("GCC_SYMBOLS_PRIVATE_EXTERN = YES");
+ s.add ("DEAD_CODE_STRIPPING = YES");
+ }
+
+ if (type == Target::SharedCodeTarget)
+ defines.set ("JUCE_SHARED_CODE", "1");
+
+ if (owner.project.getProjectType().isAudioPlugin() && type == Target::AudioUnitv3PlugIn && owner.isOSX())
+ s.add (String ("CODE_SIGN_ENTITLEMENTS = \"") + owner.getEntitlementsFileName() + String ("\""));
+
+ if (owner.project.getProjectType().isAudioPlugin())
+ {
+ defines.set ("JucePlugin_Build_VST", (shouldBuildVST() ? "1" : "0"));
+ defines.set ("JucePlugin_Build_VST3", (shouldBuildVST3() ? "1" : "0"));
+ defines.set ("JucePlugin_Build_AU", (shouldBuildAU() ? "1" : "0"));
+ defines.set ("JucePlugin_Build_AUv3", (shouldBuildAUv3() ? "1" : "0"));
+ defines.set ("JucePlugin_Build_RTAS", (shouldBuildRTAS() ? "1" : "0"));
+ defines.set ("JucePlugin_Build_AAX", (shouldBuildAAX() ? "1" : "0"));
+ defines.set ("JucePlugin_Build_Standalone", (shouldBuildStandalone() ? "1" : "0"));
+ }
+
+ defines = mergePreprocessorDefs (defines, owner.getAllPreprocessorDefs (config));
+
+ StringArray defsList;
+
+ for (int i = 0; i < defines.size(); ++i)
+ {
+ String def (defines.getAllKeys()[i]);
+ const String value (defines.getAllValues()[i]);
+ if (value.isNotEmpty())
+ def << "=" << value.replace ("\"", "\\\"");
+
+ defsList.add ("\"" + def + "\"");
+ }
+
+ s.add ("GCC_PREPROCESSOR_DEFINITIONS = " + indentParenthesisedList (defsList));
+
+ s.addTokens (config.customXcodeFlags.get(), ",", "\"'");
+
+ return getCleanedStringArray (s);
+ }
+
+ String getInstallPathForConfiguration (const XcodeBuildConfiguration& config) const
+ {
+ switch (type)
+ {
+ case GUIApp: return "$(HOME)/Applications";
+ case ConsoleApp: return "/usr/bin";
+ case VSTPlugIn: return config.vstBinaryLocation.get();
+ case VST3PlugIn: return config.vst3BinaryLocation.get();
+ case AudioUnitPlugIn: return config.auBinaryLocation.get();
+ case RTASPlugIn: return config.rtasBinaryLocation.get();
+ case AAXPlugIn: return config.aaxBinaryLocation.get();
+ case SharedCodeTarget: return owner.isiOS() ? "@executable_path/Frameworks" : "@executable_path/../Frameworks";
+ default: return String();
+ }
+ }
+
+ //==============================================================================
+ void getLinkerFlags (const BuildConfiguration& config, StringArray& flags, StringArray& librarySearchPaths) const
+ {
+ if (xcodeIsBundle)
+ flags.add ("-bundle");
+
+ const Array& extraLibs = config.isDebug() ? xcodeExtraLibrariesDebug
+ : xcodeExtraLibrariesRelease;
+
+ for (int i = 0; i < extraLibs.size(); ++i)
+ owner.getLinkerFlagsForStaticLibrary (extraLibs.getReference(i), flags, &librarySearchPaths);
+
+ if (owner.project.getProjectType().isAudioPlugin() && type != Target::SharedCodeTarget)
+ {
+ if (owner.getTargetOfType (Target::SharedCodeTarget) != nullptr)
+ {
+ String productName (getLibbedFilename (owner.replacePreprocessorTokens (config, config.getTargetBinaryNameString())));
+
+ RelativePath sharedCodelib (productName, RelativePath::buildTargetFolder);
+ owner.getLinkerFlagsForStaticLibrary (sharedCodelib, flags);
+ }
+ }
+
+
+
+ flags.add (owner.replacePreprocessorTokens (config, owner.getExtraLinkerFlagsString()));
+ flags.add (owner.getExternalLibraryFlags (config));
+
+ StringArray libs (owner.xcodeLibs);
+ libs.addArray (xcodeLibs);
+
+ for (int i = 0; i < libs.size(); ++i)
+ flags.add (getLinkerFlagForLib (libs[i]));
+
+ flags = getCleanedStringArray (flags);
+ }
+
+ //========================================================================== c
+ void writeInfoPlistFile() const
+ {
+ if (! xcodeCreatePList)
+ return;
+
+ ScopedPointer plist (XmlDocument::parse (owner.getPListToMergeString()));
+
+ if (plist == nullptr || ! plist->hasTagName ("plist"))
+ plist = new XmlElement ("plist");
+
+ XmlElement* dict = plist->getChildByName ("dict");
+
+ if (dict == nullptr)
+ dict = plist->createNewChildElement ("dict");
+
+ if (owner.iOS)
+ {
+ addPlistDictionaryKeyBool (dict, "LSRequiresIPhoneOS", true);
+
+ if (type != AudioUnitv3PlugIn)
+ addPlistDictionaryKeyBool (dict, "UIViewControllerBasedStatusBarAppearance", false);
+ }
+
+ addPlistDictionaryKey (dict, "CFBundleExecutable", "${EXECUTABLE_NAME}");
+
+ if (! owner.iOS) // (NB: on iOS this causes error ITMS-90032 during publishing)
+ addPlistDictionaryKey (dict, "CFBundleIconFile", owner.iconFile.exists() ? owner.iconFile.getFileName() : String());
+
+ addPlistDictionaryKey (dict, "CFBundleIdentifier", "$(PRODUCT_BUNDLE_IDENTIFIER)");
+ addPlistDictionaryKey (dict, "CFBundleName", owner.projectName);
+
+ // needed by NSExtension on iOS
+ addPlistDictionaryKey (dict, "CFBundleDisplayName", owner.projectName);
+ addPlistDictionaryKey (dict, "CFBundlePackageType", xcodePackageType);
+ addPlistDictionaryKey (dict, "CFBundleSignature", xcodeBundleSignature);
+ addPlistDictionaryKey (dict, "CFBundleShortVersionString", owner.project.getVersionString());
+ addPlistDictionaryKey (dict, "CFBundleVersion", owner.project.getVersionString());
+ addPlistDictionaryKey (dict, "NSHumanReadableCopyright", owner.project.getCompanyName().toString());
+ addPlistDictionaryKeyBool (dict, "NSHighResolutionCapable", true);
+
+ StringArray documentExtensions;
+ documentExtensions.addTokens (replacePreprocessorDefs (owner.getAllPreprocessorDefs(), owner.settings ["documentExtensions"]),
+ ",", StringRef());
+ documentExtensions.trim();
+ documentExtensions.removeEmptyStrings (true);
+
+ if (documentExtensions.size() > 0 && type != AudioUnitv3PlugIn)
+ {
+ dict->createNewChildElement ("key")->addTextElement ("CFBundleDocumentTypes");
+ XmlElement* dict2 = dict->createNewChildElement ("array")->createNewChildElement ("dict");
+ XmlElement* arrayTag = nullptr;
+
+ for (int i = 0; i < documentExtensions.size(); ++i)
+ {
+ String ex (documentExtensions[i]);
+ if (ex.startsWithChar ('.'))
+ ex = ex.substring (1);
+
+ if (arrayTag == nullptr)
+ {
+ dict2->createNewChildElement ("key")->addTextElement ("CFBundleTypeExtensions");
+ arrayTag = dict2->createNewChildElement ("array");
+
+ addPlistDictionaryKey (dict2, "CFBundleTypeName", ex);
+ addPlistDictionaryKey (dict2, "CFBundleTypeRole", "Editor");
+ addPlistDictionaryKey (dict2, "CFBundleTypeIconFile", "Icon");
+ addPlistDictionaryKey (dict2, "NSPersistentStoreTypeKey", "XML");
+ }
+
+ arrayTag->createNewChildElement ("string")->addTextElement (ex);
+ }
+ }
+
+ if (owner.settings ["UIFileSharingEnabled"] && type != AudioUnitv3PlugIn)
+ addPlistDictionaryKeyBool (dict, "UIFileSharingEnabled", true);
+
+ if (owner.settings ["UIStatusBarHidden"] && type != AudioUnitv3PlugIn)
+ addPlistDictionaryKeyBool (dict, "UIStatusBarHidden", true);
+
+ if (owner.iOS && type != AudioUnitv3PlugIn)
+ {
+ // Forcing full screen disables the split screen feature and prevents error ITMS-90475
+ addPlistDictionaryKeyBool (dict, "UIRequiresFullScreen", true);
+ addPlistDictionaryKeyBool (dict, "UIStatusBarHidden", true);
+
+ addIosScreenOrientations (dict);
+ addIosBackgroundModes (dict);
+ }
+
+ for (int i = 0; i < xcodeExtraPListEntries.size(); ++i)
+ dict->addChildElement (new XmlElement (xcodeExtraPListEntries.getReference(i)));
+
+ MemoryOutputStream mo;
+ plist->writeToStream (mo, "");
+
+ overwriteFileIfDifferentOrThrow (infoPlistFile, mo);
+ }
+
+ //==============================================================================
+ void addIosScreenOrientations (XmlElement* dict) const
+ {
+ String screenOrientation = owner.getScreenOrientationString();
+ StringArray iOSOrientations;
+
+ if (screenOrientation.contains ("portrait")) { iOSOrientations.add ("UIInterfaceOrientationPortrait"); }
+ if (screenOrientation.contains ("landscape")) { iOSOrientations.add ("UIInterfaceOrientationLandscapeLeft"); iOSOrientations.add ("UIInterfaceOrientationLandscapeRight"); }
+
+ addArrayToPlist (dict, "UISupportedInterfaceOrientations", iOSOrientations);
+
+ }
+
+ //==============================================================================
+ void addIosBackgroundModes (XmlElement* dict) const
+ {
+ StringArray iosBackgroundModes;
+ if (owner.isBackgroundAudioEnabled()) iosBackgroundModes.add ("audio");
+ if (owner.isBackgroundBleEnabled()) iosBackgroundModes.add ("bluetooth-central");
+
+ addArrayToPlist (dict, "UIBackgroundModes", iosBackgroundModes);
+ }
+
+ //==============================================================================
+ static void addArrayToPlist (XmlElement* dict, String arrayKey, const StringArray& arrayElements)
+ {
+ dict->createNewChildElement ("key")->addTextElement (arrayKey);
+ XmlElement* plistStringArray = dict->createNewChildElement ("array");
+
+ for (int i = 0; i < arrayElements.size(); ++i)
+ plistStringArray->createNewChildElement ("string")->addTextElement (arrayElements[i]);
+ }
+
+ //==============================================================================
+ void addShellScriptBuildPhase (const String& phaseName, const String& script)
+ {
+ if (script.trim().isNotEmpty())
+ {
+ ValueTree& v = addBuildPhase ("PBXShellScriptBuildPhase", StringArray());
+ v.setProperty (Ids::name, phaseName, nullptr);
+ v.setProperty ("shellPath", "/bin/sh", nullptr);
+ v.setProperty ("shellScript", script.replace ("\\", "\\\\")
+ .replace ("\"", "\\\"")
+ .replace ("\r\n", "\\n")
+ .replace ("\n", "\\n"), nullptr);
+ }
+ }
+
+ void addCopyFilesPhase (const String& phaseName, const StringArray& files, XcodeCopyFilesDestinationIDs dst)
+ {
+ ValueTree& v = addBuildPhase ("PBXCopyFilesBuildPhase", files, phaseName);
+ v.setProperty ("dstPath", "", nullptr);
+ v.setProperty ("dstSubfolderSpec", (int) dst, nullptr);
+ }
+
+ private:
+ //==============================================================================
+ void addExtraAudioUnitTargetSettings()
+ {
+ xcodeOtherRezFlags = "-d ppc_$ppc -d i386_$i386 -d ppc64_$ppc64 -d x86_64_$x86_64"
+ " -I /System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework/Versions/A/Headers"
+ " -I \\\"$(DEVELOPER_DIR)/Extras/CoreAudio/AudioUnits/AUPublic/AUBase\\\"";
+
+ xcodeFrameworks.addTokens ("AudioUnit CoreAudioKit", false);
+
+ XmlElement plistKey ("key");
+ plistKey.addTextElement ("AudioComponents");
+
+ XmlElement plistEntry ("array");
+ XmlElement* dict = plistEntry.createNewChildElement ("dict");
+
+ addPlistDictionaryKey (dict, "name", owner.project.getPluginManufacturer().toString()
+ + ": " + owner.project.getPluginName().toString());
+ addPlistDictionaryKey (dict, "description", owner.project.getPluginDesc().toString());
+ addPlistDictionaryKey (dict, "factoryFunction", owner.project.getPluginAUExportPrefix().toString() + "Factory");
+ addPlistDictionaryKey (dict, "manufacturer", owner.project.getPluginManufacturerCode().toString().trim().substring (0, 4));
+ addPlistDictionaryKey (dict, "type", owner.project.getAUMainTypeCode());
+ addPlistDictionaryKey (dict, "subtype", owner.project.getPluginCode().toString().trim().substring (0, 4));
+ addPlistDictionaryKeyInt (dict, "version", owner.project.getVersionAsHexInteger());
+
+ xcodeExtraPListEntries.add (plistKey);
+ xcodeExtraPListEntries.add (plistEntry);
+ }
+
+ void addExtraAudioUnitv3PlugInTargetSettings()
+ {
+ if (owner.isiOS())
+ xcodeFrameworks.addTokens ("CoreAudioKit AVFoundation", false);
+ else
+ xcodeFrameworks.addTokens ("AudioUnit CoreAudioKit AVFoundation", false);
+
+ XmlElement plistKey ("key");
+ plistKey.addTextElement ("NSExtension");
+
+ XmlElement plistEntry ("dict");
+
+ addPlistDictionaryKey (&plistEntry, "NSExtensionPrincipalClass", owner.project.getPluginAUExportPrefix().toString() + "FactoryAUv3");
+ addPlistDictionaryKey (&plistEntry, "NSExtensionPointIdentifier", "com.apple.AudioUnit-UI");
+ plistEntry.createNewChildElement ("key")->addTextElement ("NSExtensionAttributes");
+
+ XmlElement* dict = plistEntry.createNewChildElement ("dict");
+ dict->createNewChildElement ("key")->addTextElement ("AudioComponents");
+ XmlElement* componentArray = dict->createNewChildElement ("array");
+
+ XmlElement* componentDict = componentArray->createNewChildElement ("dict");
+
+ addPlistDictionaryKey (componentDict, "name", owner.project.getPluginManufacturer().toString()
+ + ": " + owner.project.getPluginName().toString());
+ addPlistDictionaryKey (componentDict, "description", owner.project.getPluginDesc().toString());
+ addPlistDictionaryKey (componentDict, "factoryFunction",owner.project. getPluginAUExportPrefix().toString() + "FactoryAUv3");
+ addPlistDictionaryKey (componentDict, "manufacturer", owner.project.getPluginManufacturerCode().toString().trim().substring (0, 4));
+ addPlistDictionaryKey (componentDict, "type", owner.project.getAUMainTypeCode());
+ addPlistDictionaryKey (componentDict, "subtype", owner.project.getPluginCode().toString().trim().substring (0, 4));
+ addPlistDictionaryKeyInt (componentDict, "version", owner.project.getVersionAsHexInteger());
+ addPlistDictionaryKeyBool (componentDict, "sandboxSafe", true);
+
+ componentDict->createNewChildElement ("key")->addTextElement ("tags");
+ XmlElement* tagsArray = componentDict->createNewChildElement ("array");
+
+ tagsArray->createNewChildElement ("string")->addTextElement (static_cast (owner.project.getPluginIsSynth().getValue()) ? "Synth" : "Effects");
+
+ xcodeExtraPListEntries.add (plistKey);
+ xcodeExtraPListEntries.add (plistEntry);
+ }
+
+ void addExtraAAXTargetSettings()
+ {
+ const RelativePath aaxLibsFolder = RelativePath (owner.getAAXPathValue().toString(), RelativePath::projectFolder).getChildFile ("Libs");
+
+ xcodeExtraLibrariesDebug.add (aaxLibsFolder.getChildFile ("Debug/libAAXLibrary.a"));
+ xcodeExtraLibrariesRelease.add (aaxLibsFolder.getChildFile ("Release/libAAXLibrary.a"));
+ }
+
+ void addExtraRTASTargetSettings()
+ {
+ RelativePath rtasFolder (owner.getRTASPathValue().toString(), RelativePath::projectFolder);
+
+ xcodeExtraLibrariesDebug.add (rtasFolder.getChildFile ("MacBag/Libs/Debug/libPluginLibrary.a"));
+ xcodeExtraLibrariesRelease.add (rtasFolder.getChildFile ("MacBag/Libs/Release/libPluginLibrary.a"));
+ }
+
+ //==============================================================================
+ const XCodeProjectExporter& owner;
+
+ Target& operator= (const Target&) JUCE_DELETED_FUNCTION;
+ };
+
+ mutable StringArray xcodeFrameworks;
+ StringArray xcodeLibs;
+
+private:
+ //==============================================================================
+ bool xcodeCanUseDwarf;
+ OwnedArray targets;
+
+ mutable OwnedArray pbxBuildFiles, pbxFileReferences, pbxGroups, misc, projectConfigs, targetConfigs;
+ mutable StringArray resourceIDs, sourceIDs, targetIDs;
+ mutable StringArray frameworkFileIDs, rezFileIDs, resourceFileRefs;
+ mutable File menuNibFile, iconFile;
+ mutable StringArray buildProducts;
+
+ const bool iOS;
+
+ static String sanitisePath (const String& path)
+ {
+ if (path.startsWithChar ('~'))
+ return "$(HOME)" + path.substring (1);
+
+ return path;
+ }
+
+ static String addQuotesIfRequired (const String& s)
+ {
+ return s.containsAnyOf (" $") ? s.quoted() : s;
+ }
+
+ File getProjectBundle() const { return getTargetFolder().getChildFile (project.getProjectFilenameRoot()).withFileExtension (".xcodeproj"); }
+
+ //==============================================================================
+ void createObjects() const
+ {
+ prepareTargets();
+
+ addFrameworks();
+ addCustomResourceFolders();
+ addPlistFileReferences();
+
+ if (iOS && ! projectType.isStaticLibrary())
+ addXcassets();
+ else
+ addNibFiles();
+
+ addIcons();
+ addBuildConfigurations();
+
+ addProjectConfigList (projectConfigs, createID ("__projList"));
+
+ {
+ StringArray topLevelGroupIDs;
+
+ addFilesAndGroupsToProject (topLevelGroupIDs);
+ addBuildPhases();
+ addExtraGroupsToProject (topLevelGroupIDs);
+
+ addGroup (createID ("__mainsourcegroup"), "Source", topLevelGroupIDs);
+ }
+
+ addProjectObject();
+ removeMismatchedXcuserdata();
+ }
+
+ void prepareTargets() const
+ {
+ for (int targetIdx = 0; targetIdx < targets.size(); ++targetIdx)
+ {
+ Target& target = *targets[targetIdx];
+
+ if (target.type == Target::AggregateTarget)
+ continue;
+
+ target.addMainBuildProduct();
+
+ String targetName = target.getName();
+ String fileID (createID (targetName + String ("__targetbuildref")));
+ String fileRefID (createID (String ("__productFileID") + targetName));
+
+ ValueTree* v = new ValueTree (fileID);
+ v->setProperty ("isa", "PBXBuildFile", nullptr);
+ v->setProperty ("fileRef", fileRefID, nullptr);
+
+ target.mainBuildProductID = fileID;
+
+ pbxBuildFiles.add (v);
+ target.addDependency();
+ }
+ }
+
+ void addPlistFileReferences() const
+ {
+ for (int targetIdx = 0; targetIdx < targets.size(); ++targetIdx)
+ {
+ Target& target = *targets[targetIdx];
+
+ if (target.type == Target::AggregateTarget)
+ continue;
+
+ if (target.xcodeCreatePList)
+ {
+ RelativePath plistPath (target.infoPlistFile, getTargetFolder(), RelativePath::buildTargetFolder);
+ addFileReference (plistPath.toUnixStyle());
+ resourceFileRefs.add (createFileRefID (plistPath));
+ }
+ }
+
+ }
+
+ void addNibFiles() const
+ {
+ MemoryOutputStream nib;
+ nib.write (BinaryData::RecentFilesMenuTemplate_nib, BinaryData::RecentFilesMenuTemplate_nibSize);
+ overwriteFileIfDifferentOrThrow (menuNibFile, nib);
+
+ RelativePath menuNibPath (menuNibFile, getTargetFolder(), RelativePath::buildTargetFolder);
+ addFileReference (menuNibPath.toUnixStyle());
+ resourceIDs.add (addBuildFile (menuNibPath, false, false));
+ resourceFileRefs.add (createFileRefID (menuNibPath));
+ }
+
+ void addIcons() const
+ {
+ if (iconFile.exists())
+ {
+ RelativePath iconPath (iconFile, getTargetFolder(), RelativePath::buildTargetFolder);
+ addFileReference (iconPath.toUnixStyle());
+ resourceIDs.add (addBuildFile (iconPath, false, false));
+ resourceFileRefs.add (createFileRefID (iconPath));
+ }
+ }
+
+ void addBuildConfigurations() const
+ {
+ // add build configurations
+ for (ConstConfigIterator config (*this); config.next();)
+ {
+ const XcodeBuildConfiguration& xcodeConfig = dynamic_cast (*config);
+ addProjectConfig (config->getName(), getProjectSettings (xcodeConfig));
+ }
+ }
+
+ void addFilesAndGroupsToProject (StringArray& topLevelGroupIDs) const
+ {
+ if (! isiOS() && project.getProjectType().isAudioPlugin())
+ topLevelGroupIDs.add (addEntitlementsFile());
+
+ for (int i = 0; i < getAllGroups().size(); ++i)
+ {
+ const Project::Item& group = getAllGroups().getReference(i);
+
+ if (group.getNumChildren() > 0)
+ topLevelGroupIDs.add (addProjectItem (group));
+ }
+ }
+
+ void addExtraGroupsToProject (StringArray& topLevelGroupIDs) const
+ {
+ { // Add 'resources' group
+ String resourcesGroupID (createID ("__resources"));
+ addGroup (resourcesGroupID, "Resources", resourceFileRefs);
+ topLevelGroupIDs.add (resourcesGroupID);
+ }
+
+ { // Add 'frameworks' group
+ String frameworksGroupID (createID ("__frameworks"));
+ addGroup (frameworksGroupID, "Frameworks", frameworkFileIDs);
+ topLevelGroupIDs.add (frameworksGroupID);
+ }
+
+ { // Add 'products' group
+ String productsGroupID (createID ("__products"));
+ addGroup (productsGroupID, "Products", buildProducts);
+ topLevelGroupIDs.add (productsGroupID);
+ }
+ }
+
+ void addBuildPhases() const
+ {
+ // add build phases
+ for (int i = 0; i < targets.size(); ++i)
+ {
+ Target& target = *targets[i];
+
+ if (target.type != Target::AggregateTarget)
+ buildProducts.add (createID (String ("__productFileID") + String (target.getName())));
+
+ for (ConstConfigIterator config (*this); config.next();)
+ {
+ const XcodeBuildConfiguration& xcodeConfig = dynamic_cast (*config);
+ target.addTargetConfig (config->getName(), target.getTargetSettings (xcodeConfig));
+ }
+
+ addConfigList (target, targetConfigs, createID (String ("__configList") + target.getName()));
+
+ target.addShellScriptBuildPhase ("Pre-build script", getPreBuildScript());
+
+ if (target.type != Target::AggregateTarget)
+ {
+ // TODO: ideally resources wouldn't be copied into the AUv3 bundle as well.
+ // However, fixing this requires supporting App groups -> TODO: add app groups
+ if (! projectType.isStaticLibrary() && target.type != Target::SharedCodeTarget)
+ target.addBuildPhase ("PBXResourcesBuildPhase", resourceIDs);
+
+ StringArray rezFiles (rezFileIDs);
+ rezFiles.addArray (target.rezFileIDs);
+
+ if (rezFiles.size() > 0)
+ target.addBuildPhase ("PBXRezBuildPhase", rezFiles);
+
+
+ StringArray sourceFiles (target.sourceIDs);
+
+ if (target.type == Target::SharedCodeTarget
+ || (! project.getProjectType().isAudioPlugin()))
+ sourceFiles.addArray (sourceIDs);
+
+ target.addBuildPhase ("PBXSourcesBuildPhase", sourceFiles);
+
+ if (! projectType.isStaticLibrary() && target.type != Target::SharedCodeTarget)
+ target.addBuildPhase ("PBXFrameworksBuildPhase", target.frameworkIDs);
+
+ target.addShellScriptBuildPhase ("Post-build script", getPostBuildScript());
+ }
+
+ if (project.getProjectType().isAudioPlugin() && project.shouldBuildAUv3().getValue()
+ && project.shouldBuildStandalone().getValue() && target.type == Target::StandalonePlugIn)
+ embedAppExtension();
+
+ addTargetObject (target);
+ }
+ }
+
+ void embedAppExtension() const
+ {
+ if (Target* standaloneTarget = getTargetOfType (Target::StandalonePlugIn))
+ {
+ if (Target* auv3Target = getTargetOfType (Target::AudioUnitv3PlugIn))
+ {
+ StringArray files;
+ files.add (auv3Target->mainBuildProductID);
+ standaloneTarget->addCopyFilesPhase ("Embed App Extensions", files, kPluginsFolder);
+ }
+ }
+ }
+
+ static Image fixMacIconImageSize (Drawable& image)
+ {
+ const int validSizes[] = { 16, 32, 48, 128, 256, 512, 1024 };
+
+ const int w = image.getWidth();
+ const int h = image.getHeight();
+
+ int bestSize = 16;
+
+ for (int i = 0; i < numElementsInArray (validSizes); ++i)
+ {
+ if (w == h && w == validSizes[i])
+ {
+ bestSize = w;
+ break;
+ }
+
+ if (jmax (w, h) > validSizes[i])
+ bestSize = validSizes[i];
+ }
+
+ return rescaleImageForIcon (image, bestSize);
+ }
+
+ //==============================================================================
+ Target* getTargetOfType (Target::Type type) const
+ {
+ for (auto& target : targets)
+ if (target->type == type)
+ return target;
+
+ return nullptr;
+ }
+
+ void addTargetObject (Target& target) const
+ {
+ String targetName = target.getName();
+
+ String targetID = target.getID();
+ ValueTree* const v = new ValueTree (targetID);
+ v->setProperty ("isa", target.type == Target::AggregateTarget ? "PBXAggregateTarget" : "PBXNativeTarget", nullptr);
+ v->setProperty ("buildConfigurationList", createID (String ("__configList") + targetName), nullptr);
+
+ if (target.type != Target::AggregateTarget)
+ {
+ v->setProperty ("buildPhases", indentParenthesisedList (target.buildPhaseIDs), nullptr);
+ v->setProperty ("buildRules", "( )", nullptr);
+ }
+
+ v->setProperty ("dependencies", indentParenthesisedList (getTargetDependencies (target)), nullptr);
+ v->setProperty (Ids::name, target.getXCodeSchemeName(), nullptr);
+ v->setProperty ("productName", projectName, nullptr);
+
+ if (target.type != Target::AggregateTarget)
+ {
+ v->setProperty ("productReference", createID (String ("__productFileID") + targetName), nullptr);
+
+ jassert (target.xcodeProductType.isNotEmpty());
+ v->setProperty ("productType", target.xcodeProductType, nullptr);
+ }
+
+ targetIDs.add (targetID);
+ misc.add (v);
+ }
+
+ StringArray getTargetDependencies (const Target& target) const
+ {
+ StringArray dependencies;
+
+ if (project.getProjectType().isAudioPlugin())
+ {
+ if (target.type == Target::StandalonePlugIn) // depends on AUv3 and shared code
+ {
+ if (Target* auv3Target = getTargetOfType (Target::AudioUnitv3PlugIn))
+ dependencies.add (auv3Target->getDependencyID());
+
+ if (Target* sharedCodeTarget = getTargetOfType (Target::SharedCodeTarget))
+ dependencies.add (sharedCodeTarget->getDependencyID());
+ }
+ else if (target.type == Target::AggregateTarget) // depends on all other targets
+ {
+ for (int i = 1; i < targets.size(); ++i)
+ dependencies.add (targets[i]->getDependencyID());
+ }
+ else if (target.type != Target::SharedCodeTarget) // shared code doesn't depend on anything; all other targets depend only on the shared code
+ {
+ if (Target* sharedCodeTarget = getTargetOfType (Target::SharedCodeTarget))
+ dependencies.add (sharedCodeTarget->getDependencyID());
+ }
+ }
+
+ return dependencies;
+ }
+
+ static void writeOldIconFormat (MemoryOutputStream& out, const Image& image, const char* type, const char* maskType)
+ {
+ const int w = image.getWidth();
+ const int h = image.getHeight();
+
+ out.write (type, 4);
+ out.writeIntBigEndian (8 + 4 * w * h);
+
+ const Image::BitmapData bitmap (image, Image::BitmapData::readOnly);
+
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ const Colour pixel (bitmap.getPixelColour (x, y));
+ out.writeByte ((char) pixel.getAlpha());
+ out.writeByte ((char) pixel.getRed());
+ out.writeByte ((char) pixel.getGreen());
+ out.writeByte ((char) pixel.getBlue());
+ }
+ }
+
+ out.write (maskType, 4);
+ out.writeIntBigEndian (8 + w * h);
+
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ const Colour pixel (bitmap.getPixelColour (x, y));
+ out.writeByte ((char) pixel.getAlpha());
+ }
+ }
+ }
+
+ static void writeNewIconFormat (MemoryOutputStream& out, const Image& image, const char* type)
+ {
+ MemoryOutputStream pngData;
+ PNGImageFormat pngFormat;
+ pngFormat.writeImageToStream (image, pngData);
+
+ out.write (type, 4);
+ out.writeIntBigEndian (8 + (int) pngData.getDataSize());
+ out << pngData;
+ }
+
+ void writeIcnsFile (const OwnedArray& images, OutputStream& out) const
+ {
+ MemoryOutputStream data;
+ int smallest = 0x7fffffff;
+ Drawable* smallestImage = nullptr;
+
+ for (int i = 0; i < images.size(); ++i)
+ {
+ const Image image (fixMacIconImageSize (*images.getUnchecked(i)));
+ jassert (image.getWidth() == image.getHeight());
+
+ if (image.getWidth() < smallest)
+ {
+ smallest = image.getWidth();
+ smallestImage = images.getUnchecked(i);
+ }
+
+ switch (image.getWidth())
+ {
+ case 16: writeOldIconFormat (data, image, "is32", "s8mk"); break;
+ case 32: writeOldIconFormat (data, image, "il32", "l8mk"); break;
+ case 48: writeOldIconFormat (data, image, "ih32", "h8mk"); break;
+ case 128: writeOldIconFormat (data, image, "it32", "t8mk"); break;
+ case 256: writeNewIconFormat (data, image, "ic08"); break;
+ case 512: writeNewIconFormat (data, image, "ic09"); break;
+ case 1024: writeNewIconFormat (data, image, "ic10"); break;
+ default: break;
+ }
+ }
+
+ jassert (data.getDataSize() > 0); // no suitable sized images?
+
+ // If you only supply a 1024 image, the file doesn't work on 10.8, so we need
+ // to force a smaller one in there too..
+ if (smallest > 512 && smallestImage != nullptr)
+ writeNewIconFormat (data, rescaleImageForIcon (*smallestImage, 512), "ic09");
+
+ out.write ("icns", 4);
+ out.writeIntBigEndian ((int) data.getDataSize() + 8);
+ out << data;
+ }
+
+ void getIconImages (OwnedArray& images) const
+ {
+ ScopedPointer bigIcon (getBigIcon());
+ if (bigIcon != nullptr)
+ images.add (bigIcon.release());
+
+ ScopedPointer smallIcon (getSmallIcon());
+ if (smallIcon != nullptr)
+ images.add (smallIcon.release());
+ }
+
+ void createiOSIconFiles (File appIconSet) const
+ {
+ const Array types (getiOSAppIconTypes());
+
+ OwnedArray images;
+ getIconImages (images);
+
+ if (images.size() > 0)
+ {
+ for (int i = 0; i < types.size(); ++i)
+ {
+ const AppIconType type = types.getUnchecked(i);
+ const Image image (rescaleImageForIcon (*images.getFirst(), type.size));
+
+ MemoryOutputStream pngData;
+ PNGImageFormat pngFormat;
+ pngFormat.writeImageToStream (image, pngData);
+
+ overwriteFileIfDifferentOrThrow (appIconSet.getChildFile (type.filename), pngData);
+ }
+ }
+ }
+
+ void createIconFile() const
+ {
+ OwnedArray images;
+ getIconImages (images);
+
+ if (images.size() > 0)
+ {
+ MemoryOutputStream mo;
+ writeIcnsFile (images, mo);
+
+ iconFile = getTargetFolder().getChildFile ("Icon.icns");
+ overwriteFileIfDifferentOrThrow (iconFile, mo);
+ }
+ }
+
+ void writeInfoPlistFiles() const
+ {
+ for (auto& target : targets)
+ target->writeInfoPlistFile();
+ }
+
+ void deleteRsrcFiles() const
+ {
+ for (DirectoryIterator di (getTargetFolder().getChildFile ("build"), true, "*.rsrc", File::findFiles); di.next();)
+ di.getFile().deleteFile();
+ }
+
+ String getHeaderSearchPaths (const BuildConfiguration& config) const
+ {
+ StringArray paths (extraSearchPaths);
+ paths.addArray (config.getHeaderSearchPaths());
+ paths.add ("$(inherited)");
+
+ paths = getCleanedStringArray (paths);
+
+ for (int i = 0; i < paths.size(); ++i)
+ {
+ String& s = paths.getReference(i);
+
+ s = replacePreprocessorTokens (config, s);
+
+ if (s.containsChar (' '))
+ s = "\"\\\"" + s + "\\\"\""; // crazy double quotes required when there are spaces..
+ else
+ s = "\"" + s + "\"";
+ }
+
+ return "(" + paths.joinIntoString (", ") + ")";
+ }
+
+ static String getLinkerFlagForLib (String library)
+ {
+ if (library.substring (0, 3) == "lib")
+ library = library.substring (3);
+
+ return "-l" + library.upToLastOccurrenceOf (".", false, false);
+ }
+
+ void getLinkerFlagsForStaticLibrary (const RelativePath& library, StringArray& flags, StringArray* librarySearchPaths = nullptr) const
+ {
+ flags.add (getLinkerFlagForLib (library.getFileNameWithoutExtension()));
+
+ String searchPath (library.toUnixStyle().upToLastOccurrenceOf ("/", false, false));
+
+ if (! library.isAbsolute())
+ {
+ String srcRoot (rebaseFromProjectFolderToBuildTarget (RelativePath (".", RelativePath::projectFolder)).toUnixStyle());
+
+ if (srcRoot.endsWith ("/.")) srcRoot = srcRoot.dropLastCharacters (2);
+ if (! srcRoot.endsWithChar ('/')) srcRoot << '/';
+
+ searchPath = srcRoot + searchPath;
+ }
+
+ if (librarySearchPaths != nullptr)
+ librarySearchPaths->add (sanitisePath (searchPath));
+ }
+
+ StringArray getProjectSettings (const XcodeBuildConfiguration& config) const
+ {
+ StringArray s;
+ s.add ("ALWAYS_SEARCH_USER_PATHS = NO");
+ s.add ("GCC_C_LANGUAGE_STANDARD = c99");
+ s.add ("GCC_WARN_ABOUT_RETURN_TYPE = YES");
+ s.add ("GCC_WARN_CHECK_SWITCH_STATEMENTS = YES");
+ s.add ("GCC_WARN_UNUSED_VARIABLE = YES");
+ s.add ("GCC_WARN_MISSING_PARENTHESES = YES");
+ s.add ("GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES");
+ s.add ("GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES");
+ s.add ("WARNING_CFLAGS = -Wreorder");
+ s.add ("GCC_MODEL_TUNING = G5");
+
+ if (projectType.isStaticLibrary())
+ {
+ s.add ("GCC_INLINES_ARE_PRIVATE_EXTERN = NO");
+ s.add ("GCC_SYMBOLS_PRIVATE_EXTERN = NO");
+ }
+ else
+ {
+ s.add ("GCC_INLINES_ARE_PRIVATE_EXTERN = YES");
+ }
+
+ if (config.isDebug())
+ {
+ s.add ("ENABLE_TESTABILITY = YES");
+
+ if (config.osxArchitecture.get() == osxArch_Default || config.osxArchitecture.get().isEmpty())
+ s.add ("ONLY_ACTIVE_ARCH = YES");
+ }
+
+ if (iOS)
+ {
+ s.add ("\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = " + config.codeSignIdentity.get().quoted());
+ s.add ("SDKROOT = iphoneos");
+ s.add ("TARGETED_DEVICE_FAMILY = \"1,2\"");
+
+ const String iosVersion (config.iosDeploymentTarget.get());
+ if (iosVersion.isNotEmpty() && iosVersion != osxVersionDefault)
+ s.add ("IPHONEOS_DEPLOYMENT_TARGET = " + iosVersion);
+ }
+ else
+ {
+ if (! config.codeSignIdentity.isUsingDefault() || getIosDevelopmentTeamIDString().isNotEmpty())
+ s.add ("\"CODE_SIGN_IDENTITY\" = " + config.codeSignIdentity.get().quoted());
+ }
+
+ s.add ("ZERO_LINK = NO");
+
+ if (xcodeCanUseDwarf)
+ s.add ("DEBUG_INFORMATION_FORMAT = \"dwarf\"");
+
+ s.add ("PRODUCT_NAME = \"" + replacePreprocessorTokens (config, config.getTargetBinaryNameString()) + "\"");
+ return s;
+ }
+
+ void addFrameworks() const
+ {
+ if (! projectType.isStaticLibrary())
+ {
+ if (iOS && isInAppPurchasesEnabled())
+ xcodeFrameworks.addIfNotAlreadyThere ("StoreKit");
+
+ xcodeFrameworks.addTokens (getExtraFrameworksString(), ",;", "\"'");
+ xcodeFrameworks.trim();
+
+ StringArray s (xcodeFrameworks);
+
+ for (auto& target : targets)
+ s.addArray (target->xcodeFrameworks);
+
+ if (project.getConfigFlag ("JUCE_QUICKTIME") == Project::configFlagDisabled)
+ s.removeString ("QuickTime");
+
+ s.trim();
+ s.removeDuplicates (true);
+ s.sort (true);
+
+ for (int i = 0; i < s.size(); ++i)
+ {
+ String frameworkID = addFramework (s[i]);
+
+ // find all the targets that are referring to this object
+ for (auto& target : targets)
+ {
+ if (xcodeFrameworks.contains (s[i]) || target->xcodeFrameworks.contains (s[i]))
+ target->frameworkIDs.add (frameworkID);
+ }
+ }
+ }
+ }
+
+ void addCustomResourceFolders() const
+ {
+ StringArray crf;
+
+ crf.addTokens (getCustomResourceFoldersString(), ":", "");
+ crf.trim();
+
+ for (int i = 0; i < crf.size(); ++i)
+ addCustomResourceFolder (crf[i]);
+ }
+
+ void addXcassets() const
+ {
+ String customXcassetsPath = getCustomXcassetsFolderString();
+
+ if (customXcassetsPath.isEmpty())
+ createXcassetsFolderFromIcons();
+ else
+ addCustomResourceFolder (customXcassetsPath, "folder.assetcatalog");
+ }
+
+ void addCustomResourceFolder (String folderPathRelativeToProjectFolder, const String fileType = "folder") const
+ {
+ String folderPath = RelativePath (folderPathRelativeToProjectFolder, RelativePath::projectFolder)
+ .rebased (projectFolder, getTargetFolder(), RelativePath::buildTargetFolder)
+ .toUnixStyle();
+
+ const String fileRefID (createFileRefID (folderPath));
+
+ addFileOrFolderReference (folderPath, "", fileType);
+
+ resourceIDs.add (addBuildFile (folderPath, fileRefID, false, false));
+ resourceFileRefs.add (createFileRefID (folderPath));
+ }
+
+ //==============================================================================
+ void writeProjectFile (OutputStream& output) const
+ {
+ output << "// !$*UTF8*$!\n{\n"
+ "\tarchiveVersion = 1;\n"
+ "\tclasses = {\n\t};\n"
+ "\tobjectVersion = 46;\n"
+ "\tobjects = {\n\n";
+
+ Array objects;
+ objects.addArray (pbxBuildFiles);
+ objects.addArray (pbxFileReferences);
+ objects.addArray (pbxGroups);
+ objects.addArray (targetConfigs);
+ objects.addArray (projectConfigs);
+ objects.addArray (misc);
+
+ for (int i = 0; i < objects.size(); ++i)
+ {
+ ValueTree& o = *objects.getUnchecked(i);
+ output << "\t\t" << o.getType().toString() << " = {";
+
+ for (int j = 0; j < o.getNumProperties(); ++j)
+ {
+ const Identifier propertyName (o.getPropertyName(j));
+ String val (o.getProperty (propertyName).toString());
+
+ if (val.isEmpty() || (val.containsAnyOf (" \t;<>()=,&+-_@~\r\n\\#%^`*")
+ && ! (val.trimStart().startsWithChar ('(')
+ || val.trimStart().startsWithChar ('{'))))
+ val = "\"" + val + "\"";
+
+ output << propertyName.toString() << " = " << val << "; ";
+ }
+
+ output << "};\n";
+ }
+
+ output << "\t};\n\trootObject = " << createID ("__root") << ";\n}\n";
+ }
+
+ String addBuildFile (const String& path, const String& fileRefID, bool addToSourceBuildPhase, bool inhibitWarnings, Target* xcodeTarget = nullptr) const
+ {
+ String fileID (createID (path + "buildref"));
+
+ if (addToSourceBuildPhase)
+ {
+ if (xcodeTarget != nullptr) xcodeTarget->sourceIDs.add (fileID);
+ else sourceIDs.add (fileID);
+ }
+
+ ValueTree* v = new ValueTree (fileID);
+ v->setProperty ("isa", "PBXBuildFile", nullptr);
+ v->setProperty ("fileRef", fileRefID, nullptr);
+
+ if (inhibitWarnings)
+ v->setProperty ("settings", "{COMPILER_FLAGS = \"-w\"; }", nullptr);
+
+ pbxBuildFiles.add (v);
+ return fileID;
+ }
+
+ String addBuildFile (const RelativePath& path, bool addToSourceBuildPhase, bool inhibitWarnings, Target* xcodeTarget = nullptr) const
+ {
+ return addBuildFile (path.toUnixStyle(), createFileRefID (path), addToSourceBuildPhase, inhibitWarnings, xcodeTarget);
+ }
+
+ String addFileReference (String pathString) const
+ {
+ String sourceTree ("SOURCE_ROOT");
+ RelativePath path (pathString, RelativePath::unknown);
+
+ if (pathString.startsWith ("${"))
+ {
+ sourceTree = pathString.substring (2).upToFirstOccurrenceOf ("}", false, false);
+ pathString = pathString.fromFirstOccurrenceOf ("}/", false, false);
+ }
+ else if (path.isAbsolute())
+ {
+ sourceTree = "";
+ }
+
+ String fileType = getFileType (path);
+
+ return addFileOrFolderReference (pathString, sourceTree, fileType);
+ }
+
+ String addFileOrFolderReference (String pathString, String sourceTree, String fileType) const
+ {
+ const String fileRefID (createFileRefID (pathString));
+
+ ScopedPointer v (new ValueTree (fileRefID));
+ v->setProperty ("isa", "PBXFileReference", nullptr);
+ v->setProperty ("lastKnownFileType", fileType, nullptr);
+ v->setProperty (Ids::name, pathString.fromLastOccurrenceOf ("/", false, false), nullptr);
+ v->setProperty ("path", sanitisePath (pathString), nullptr);
+ v->setProperty ("sourceTree", sourceTree, nullptr);
+
+ const int existing = pbxFileReferences.indexOfSorted (*this, v);
+
+ if (existing >= 0)
+ {
+ // If this fails, there's either a string hash collision, or the same file is being added twice (incorrectly)
+ jassert (pbxFileReferences.getUnchecked (existing)->isEquivalentTo (*v));
+ }
+ else
+ {
+ pbxFileReferences.addSorted (*this, v.release());
+ }
+
+ return fileRefID;
+ }
+
+public:
+ static int compareElements (const ValueTree* first, const ValueTree* second)
+ {
+ return first->getType().getCharPointer().compare (second->getType().getCharPointer());
+ }
+
+private:
+ static String getFileType (const RelativePath& file)
+ {
+ if (file.hasFileExtension (cppFileExtensions)) return "sourcecode.cpp.cpp";
+ if (file.hasFileExtension (".mm")) return "sourcecode.cpp.objcpp";
+ if (file.hasFileExtension (".m")) return "sourcecode.c.objc";
+ if (file.hasFileExtension (".c")) return "sourcecode.c.c";
+ if (file.hasFileExtension (headerFileExtensions)) return "sourcecode.c.h";
+ if (file.hasFileExtension (asmFileExtensions)) return "sourcecode.c.asm";
+ if (file.hasFileExtension (".framework")) return "wrapper.framework";
+ if (file.hasFileExtension (".jpeg;.jpg")) return "image.jpeg";
+ if (file.hasFileExtension ("png;gif")) return "image" + file.getFileExtension();
+ if (file.hasFileExtension ("html;htm")) return "text.html";
+ if (file.hasFileExtension ("xml;zip;wav")) return "file" + file.getFileExtension();
+ if (file.hasFileExtension ("txt;rtf")) return "text" + file.getFileExtension();
+ if (file.hasFileExtension ("plist")) return "text.plist.xml";
+ if (file.hasFileExtension ("entitlements")) return "text.plist.xml";
+ if (file.hasFileExtension ("app")) return "wrapper.application";
+ if (file.hasFileExtension ("component;vst;plugin")) return "wrapper.cfbundle";
+ if (file.hasFileExtension ("xcodeproj")) return "wrapper.pb-project";
+ if (file.hasFileExtension ("a")) return "archive.ar";
+ if (file.hasFileExtension ("xcassets")) return "folder.assetcatalog";
+
+ return "file" + file.getFileExtension();
+ }
+
+ String addFile (const RelativePath& path, bool shouldBeCompiled, bool shouldBeAddedToBinaryResources,
+ bool shouldBeAddedToXcodeResources, bool inhibitWarnings, Target* xcodeTarget) const
+ {
+ const String pathAsString (path.toUnixStyle());
+ const String refID (addFileReference (path.toUnixStyle()));
+
+ if (shouldBeCompiled)
+ {
+ addBuildFile (pathAsString, refID, true, inhibitWarnings, xcodeTarget);
+ }
+ else if (! shouldBeAddedToBinaryResources || shouldBeAddedToXcodeResources)
+ {
+ const String fileType (getFileType (path));
+
+ if (shouldBeAddedToXcodeResources || fileType.startsWith ("image.") || fileType.startsWith ("text.") || fileType.startsWith ("file."))
+ {
+ resourceIDs.add (addBuildFile (pathAsString, refID, false, false));
+ resourceFileRefs.add (refID);
+ }
+ }
+
+ return refID;
+ }
+
+ String addRezFile (const RelativePath& path) const
+ {
+ const String pathAsString (path.toUnixStyle());
+ const String refID (addFileReference (path.toUnixStyle()));
+
+ Target* auTarget = getTargetOfType (Target::AudioUnitPlugIn);
+
+ if (auTarget == nullptr)
+ return String();
+
+ String rezFileID = addBuildFile (pathAsString, refID, false, false, auTarget);
+ auTarget->rezFileIDs.add (rezFileID);
+
+ return refID;
+ }
+
+ String getEntitlementsFileName() const
+ {
+ return project.getProjectFilenameRoot() + String (".entitlements");
+ }
+
+ String addEntitlementsFile() const
+ {
+ const char* sandboxEntitlement =
+ ""
+ ""
+ ""
+ ""
+ " com.apple.security.app-sandbox"
+ " "
+ ""
+ "";
+
+ File entitlementsFile = getTargetFolder().getChildFile (getEntitlementsFileName());
+ overwriteFileIfDifferentOrThrow (entitlementsFile, sandboxEntitlement);
+
+ RelativePath plistPath (entitlementsFile, getTargetFolder(), RelativePath::buildTargetFolder);
+ return addFile (plistPath, false, false, false, false, nullptr);
+ }
+
+ String addProjectItem (const Project::Item& projectItem) const
+ {
+ if (projectItem.isGroup())
+ {
+ StringArray childIDs;
+ for (int i = 0; i < projectItem.getNumChildren(); ++i)
+ {
+ const String childID (addProjectItem (projectItem.getChild(i)));
+
+ if (childID.isNotEmpty())
+ childIDs.add (childID);
+ }
+
+ return addGroup (projectItem, childIDs);
+ }
+
+ if (projectItem.shouldBeAddedToTargetProject())
+ {
+ const String itemPath (projectItem.getFilePath());
+ RelativePath path;
+
+ if (itemPath.startsWith ("${"))
+ path = RelativePath (itemPath, RelativePath::unknown);
+ else
+ path = RelativePath (projectItem.getFile(), getTargetFolder(), RelativePath::buildTargetFolder);
+
+ if (path.hasFileExtension (".r") && LibraryModule::CompileUnit::hasSuffix (projectItem.getFile(), "_AU"))
+ return addRezFile (path);
+
+ Target* xcodeTarget = nullptr;
+ if (projectItem.isModuleCode() && projectItem.shouldBeCompiled())
+ {
+ const File& file = projectItem.getFile();
+
+ Target::Type targetType = Target::unspecified;
+ if (LibraryModule::CompileUnit::hasSuffix (file, "_AU")) targetType = Target::AudioUnitPlugIn;
+ else if (LibraryModule::CompileUnit::hasSuffix (file, "_AUv3")) targetType = Target::AudioUnitv3PlugIn;
+ else if (LibraryModule::CompileUnit::hasSuffix (file, "_AAX")) targetType = Target::AAXPlugIn;
+ else if (LibraryModule::CompileUnit::hasSuffix (file, "_RTAS")) targetType = Target::RTASPlugIn;
+ else if (LibraryModule::CompileUnit::hasSuffix (file, "_VST2")) targetType = Target::VSTPlugIn;
+ else if (LibraryModule::CompileUnit::hasSuffix (file, "_VST3")) targetType = Target::VST3PlugIn;
+ else if (LibraryModule::CompileUnit::hasSuffix (file, "_Standalone")) targetType = Target::StandalonePlugIn;
+
+ xcodeTarget = getTargetOfType (targetType);
+ }
+
+ return addFile (path, projectItem.shouldBeCompiled(),
+ projectItem.shouldBeAddedToBinaryResources(),
+ projectItem.shouldBeAddedToXcodeResources(),
+ projectItem.shouldInhibitWarnings(),
+ xcodeTarget);
+ }
+
+ return String();
+ }
+
+ String addFramework (const String& frameworkName) const
+ {
+ String path (frameworkName);
+ if (! File::isAbsolutePath (path))
+ path = "System/Library/Frameworks/" + path;
+
+ if (! path.endsWithIgnoreCase (".framework"))
+ path << ".framework";
+
+ const String fileRefID (createFileRefID (path));
+
+ addFileReference ((File::isAbsolutePath (frameworkName) ? "" : "${SDKROOT}/") + path);
+ frameworkFileIDs.add (fileRefID);
+
+ return addBuildFile (path, fileRefID, false, false);
+ }
+
+ void addGroup (const String& groupID, const String& groupName, const StringArray& childIDs) const
+ {
+ ValueTree* v = new ValueTree (groupID);
+ v->setProperty ("isa", "PBXGroup", nullptr);
+ v->setProperty ("children", indentParenthesisedList (childIDs), nullptr);
+ v->setProperty (Ids::name, groupName, nullptr);
+ v->setProperty ("sourceTree", "", nullptr);
+ pbxGroups.add (v);
+ }
+
+ String addGroup (const Project::Item& item, StringArray& childIDs) const
+ {
+ const String groupName (item.getName());
+ const String groupID (getIDForGroup (item));
+ addGroup (groupID, groupName, childIDs);
+ return groupID;
+ }
+
+ void addProjectConfig (const String& configName, const StringArray& buildSettings) const
+ {
+ ValueTree* v = new ValueTree (createID ("projectconfigid_" + configName));
+ v->setProperty ("isa", "XCBuildConfiguration", nullptr);
+ v->setProperty ("buildSettings", indentBracedList (buildSettings), nullptr);
+ v->setProperty (Ids::name, configName, nullptr);
+ projectConfigs.add (v);
+ }
+
+ void addConfigList (Target& target, const OwnedArray & configsToUse, const String& listID) const
+ {
+ ValueTree* v = new ValueTree (listID);
+ v->setProperty ("isa", "XCConfigurationList", nullptr);
+ v->setProperty ("buildConfigurations", indentParenthesisedList (target.configIDs), nullptr);
+ v->setProperty ("defaultConfigurationIsVisible", (int) 0, nullptr);
+
+ if (configsToUse[0] != nullptr)
+ v->setProperty ("defaultConfigurationName", configsToUse[0]->getProperty (Ids::name), nullptr);
+
+ misc.add (v);
+ }
+
+ void addProjectConfigList (const OwnedArray & configsToUse, const String& listID) const
+ {
+ StringArray configIDs;
+
+ for (int i = 0; i < configsToUse.size(); ++i)
+ configIDs.add (configsToUse[i]->getType().toString());
+
+ ValueTree* v = new ValueTree (listID);
+ v->setProperty ("isa", "XCConfigurationList", nullptr);
+ v->setProperty ("buildConfigurations", indentParenthesisedList (configIDs), nullptr);
+ v->setProperty ("defaultConfigurationIsVisible", (int) 0, nullptr);
+
+ if (configsToUse[0] != nullptr)
+ v->setProperty ("defaultConfigurationName", configsToUse[0]->getProperty (Ids::name), nullptr);
+
+ misc.add (v);
+ }
+
+ void addProjectObject() const
+ {
+ ValueTree* const v = new ValueTree (createID ("__root"));
+ v->setProperty ("isa", "PBXProject", nullptr);
+ v->setProperty ("buildConfigurationList", createID ("__projList"), nullptr);
+ v->setProperty ("attributes", getProjectObjectAttributes(), nullptr);
+ v->setProperty ("compatibilityVersion", "Xcode 3.2", nullptr);
+ v->setProperty ("hasScannedForEncodings", (int) 0, nullptr);
+ v->setProperty ("mainGroup", createID ("__mainsourcegroup"), nullptr);
+ v->setProperty ("projectDirPath", "\"\"", nullptr);
+ v->setProperty ("projectRoot", "\"\"", nullptr);
+
+ String targetString = "(" + targetIDs.joinIntoString (", ") + ")";
+ v->setProperty ("targets", targetString, nullptr);
+ misc.add (v);
+ }
+
+ //==============================================================================
+ void removeMismatchedXcuserdata() const
+ {
+ File xcuserdata = getProjectBundle().getChildFile ("xcuserdata");
+
+ if (! xcuserdata.exists())
+ return;
+
+ if (! xcuserdataMatchesTargets (xcuserdata))
+ {
+ xcuserdata.deleteRecursively();
+ getProjectBundle().getChildFile ("project.xcworkspace").deleteRecursively();
+ }
+ }
+
+ bool xcuserdataMatchesTargets (const File& xcuserdata) const
+ {
+ Array xcschemeManagementPlists;
+ xcuserdata.findChildFiles (xcschemeManagementPlists, File::findFiles, true, "xcschememanagement.plist");
+
+ for (auto& plist : xcschemeManagementPlists)
+ if (! xcschemeManagementPlistMatchesTargets (plist))
+ return false;
+
+ return true;
+ }
+
+ static StringArray parseNamesOfTargetsFromPlist (const XmlElement& dictXML)
+ {
+ forEachXmlChildElementWithTagName (dictXML, schemesKey, "key")
+ {
+ if (schemesKey->getAllSubText().trim().equalsIgnoreCase ("SchemeUserState"))
+ {
+ if (auto* dict = schemesKey->getNextElement())
+ {
+ if (dict->hasTagName ("dict"))
+ {
+ StringArray names;
+
+ forEachXmlChildElementWithTagName (*dict, key, "key")
+ names.add (key->getAllSubText().upToLastOccurrenceOf (".xcscheme", false, false).trim());
+
+ names.sort (false);
+ return names;
+ }
+ }
+ }
+ }
+
+ return StringArray();
+ }
+
+ StringArray getNamesOfTargets() const
+ {
+ StringArray names;
+
+ for (auto& target : targets)
+ names.add (target->getXCodeSchemeName());
+
+ names.sort (false);
+ return names;
+ }
+
+ bool xcschemeManagementPlistMatchesTargets (const File& plist) const
+ {
+ ScopedPointer xml (XmlDocument::parse (plist));
+
+ if (xml != nullptr)
+ if (auto* dict = xml->getChildByName ("dict"))
+ return parseNamesOfTargetsFromPlist (*dict) == getNamesOfTargets();
+
+ return false;
+ }
+
+ //==============================================================================
+ struct AppIconType
+ {
+ const char* idiom;
+ const char* sizeString;
+ const char* filename;
+ const char* scale;
+ int size;
+ };
+
+ static Array getiOSAppIconTypes()
+ {
+ AppIconType types[] =
+ {
+ { "iphone", "29x29", "Icon-29.png", "1x", 29 },
+ { "iphone", "29x29", "Icon-29@2x.png", "2x", 58 },
+ { "iphone", "29x29", "Icon-29@3x.png", "3x", 87 },
+ { "iphone", "40x40", "Icon-Spotlight-40@2x.png", "2x", 80 },
+ { "iphone", "40x40", "Icon-Spotlight-40@3x.png", "3x", 120 },
+ { "iphone", "57x57", "Icon.png", "1x", 57 },
+ { "iphone", "57x57", "Icon@2x.png", "2x", 114 },
+ { "iphone", "60x60", "Icon-60@2x.png", "2x", 120 },
+ { "iphone", "60x60", "Icon-@3x.png", "3x", 180 },
+ { "ipad", "29x29", "Icon-Small-1.png", "1x", 29 },
+ { "ipad", "29x29", "Icon-Small@2x-1.png", "2x", 58 },
+ { "ipad", "40x40", "Icon-Spotlight-40.png", "1x", 40 },
+ { "ipad", "40x40", "Icon-Spotlight-40@2x-1.png", "2x", 80 },
+ { "ipad", "50x50", "Icon-Small-50.png", "1x", 50 },
+ { "ipad", "50x50", "Icon-Small-50@2x.png", "2x", 100 },
+ { "ipad", "72x72", "Icon-72.png", "1x", 72 },
+ { "ipad", "72x72", "Icon-72@2x.png", "2x", 144 },
+ { "ipad", "76x76", "Icon-76.png", "1x", 76 },
+ { "ipad", "76x76", "Icon-76@2x.png", "2x", 152 },
+ { "ipad", "83.5x83.5", "Icon-83.5@2x.png", "2x", 167 }
+ };
+
+ return Array (types, numElementsInArray (types));
+ }
+
+ static String getiOSAppIconContents()
+ {
+ const Array types (getiOSAppIconTypes());
+ var images;
+
+ for (int i = 0; i < types.size(); ++i)
+ {
+ AppIconType type = types.getUnchecked(i);
+
+ DynamicObject::Ptr d = new DynamicObject();
+ d->setProperty ("idiom", type.idiom);
+ d->setProperty ("size", type.sizeString);
+ d->setProperty ("filename", type.filename);
+ d->setProperty ("scale", type.scale);
+ images.append (var (d));
+ }
+
+ return getiOSAssetContents (images);
+ }
+
+ String getProjectObjectAttributes() const
+ {
+ String attributes;
+
+ attributes << "{ LastUpgradeCheck = 0440; ";
+
+ if (projectType.isGUIApplication() || projectType.isAudioPlugin())
+ {
+ attributes << "TargetAttributes = { ";
+
+ for (auto& target : targets)
+ attributes << target->getTargetAttributes();
+
+ attributes << " }; ";
+ }
+
+ attributes << "}";
+
+ return attributes;
+ }
+
+ //==============================================================================
+ struct ImageType
+ {
+ const char* orientation;
+ const char* idiom;
+ const char* subtype;
+ const char* extent;
+ const char* scale;
+ const char* filename;
+ int width;
+ int height;
+ };
+
+ static Array getiOSLaunchImageTypes()
+ {
+ ImageType types[] =
+ {
+ { "portrait", "iphone", nullptr, "full-screen", "2x", "LaunchImage-iphone-2x.png", 640, 960 },
+ { "portrait", "iphone", "retina4", "full-screen", "2x", "LaunchImage-iphone-retina4.png", 640, 1136 },
+ { "portrait", "ipad", nullptr, "full-screen", "1x", "LaunchImage-ipad-portrait-1x.png", 768, 1024 },
+ { "landscape","ipad", nullptr, "full-screen", "1x", "LaunchImage-ipad-landscape-1x.png", 1024, 768 },
+ { "portrait", "ipad", nullptr, "full-screen", "2x", "LaunchImage-ipad-portrait-2x.png", 1536, 2048 },
+ { "landscape","ipad", nullptr, "full-screen", "2x", "LaunchImage-ipad-landscape-2x.png", 2048, 1536 }
+ };
+
+ return Array (types, numElementsInArray (types));
+ }
+
+ static String getiOSLaunchImageContents()
+ {
+ const Array types (getiOSLaunchImageTypes());
+ var images;
+
+ for (int i = 0; i < types.size(); ++i)
+ {
+ const ImageType& type = types.getReference(i);
+
+ DynamicObject::Ptr d = new DynamicObject();
+ d->setProperty ("orientation", type.orientation);
+ d->setProperty ("idiom", type.idiom);
+ d->setProperty ("extent", type.extent);
+ d->setProperty ("minimum-system-version", "7.0");
+ d->setProperty ("scale", type.scale);
+ d->setProperty ("filename", type.filename);
+
+ if (type.subtype != nullptr)
+ d->setProperty ("subtype", type.subtype);
+
+ images.append (var (d));
+ }
+
+ return getiOSAssetContents (images);
+ }
+
+ static void createiOSLaunchImageFiles (const File& launchImageSet)
+ {
+ const Array types (getiOSLaunchImageTypes());
+
+ for (int i = 0; i < types.size(); ++i)
+ {
+ const ImageType& type = types.getReference(i);
+
+ Image image (Image::ARGB, type.width, type.height, true); // (empty black image)
+ image.clear (image.getBounds(), Colours::black);
+
+ MemoryOutputStream pngData;
+ PNGImageFormat pngFormat;
+ pngFormat.writeImageToStream (image, pngData);
+ overwriteFileIfDifferentOrThrow (launchImageSet.getChildFile (type.filename), pngData);
+ }
+ }
+
+ //==============================================================================
+ static String getiOSAssetContents (var images)
+ {
+ DynamicObject::Ptr v (new DynamicObject());
+
+ var info (new DynamicObject());
+ info.getDynamicObject()->setProperty ("version", 1);
+ info.getDynamicObject()->setProperty ("author", "xcode");
+
+ v->setProperty ("images", images);
+ v->setProperty ("info", info);
+
+ return JSON::toString (var (v));
+ }
+
+ void createXcassetsFolderFromIcons() const
+ {
+ const File assets (getTargetFolder().getChildFile (project.getProjectFilenameRoot())
+ .getChildFile ("Images.xcassets"));
+ const File iconSet (assets.getChildFile ("AppIcon.appiconset"));
+ const File launchImage (assets.getChildFile ("LaunchImage.launchimage"));
+
+ overwriteFileIfDifferentOrThrow (iconSet.getChildFile ("Contents.json"), getiOSAppIconContents());
+ createiOSIconFiles (iconSet);
+
+ overwriteFileIfDifferentOrThrow (launchImage.getChildFile ("Contents.json"), getiOSLaunchImageContents());
+ createiOSLaunchImageFiles (launchImage);
+
+ RelativePath assetsPath (assets, getTargetFolder(), RelativePath::buildTargetFolder);
+ addFileReference (assetsPath.toUnixStyle());
+ resourceIDs.add (addBuildFile (assetsPath, false, false));
+ resourceFileRefs.add (createFileRefID (assetsPath));
+ }
+
+ //==============================================================================
+ static String indentBracedList (const StringArray& list) { return "{" + indentList (list, ";", 0, true) + " }"; }
+ static String indentParenthesisedList (const StringArray& list) { return "(" + indentList (list, ",", 1, false) + " )"; }
+
+ static String indentList (const StringArray& list, const String& separator, int extraTabs, bool shouldSort)
+ {
+ if (list.size() == 0)
+ return " ";
+
+ const String tabs ("\n" + String::repeatedString ("\t", extraTabs + 4));
+
+ if (shouldSort)
+ {
+ StringArray sorted (list);
+ sorted.sort (true);
+
+ return tabs + sorted.joinIntoString (separator + tabs) + separator;
+ }
+
+ return tabs + list.joinIntoString (separator + tabs) + separator;
+ }
+
+ String createID (String rootString) const
+ {
+ if (rootString.startsWith ("${"))
+ rootString = rootString.fromFirstOccurrenceOf ("}/", false, false);
+
+ rootString += project.getProjectUID();
+
+ return MD5 (rootString.toUTF8()).toHexString().substring (0, 24).toUpperCase();
+ }
+
+ String createFileRefID (const RelativePath& path) const { return createFileRefID (path.toUnixStyle()); }
+ String createFileRefID (const String& path) const { return createID ("__fileref_" + path); }
+ String getIDForGroup (const Project::Item& item) const { return createID (item.getID()); }
+
+ bool shouldFileBeCompiledByDefault (const RelativePath& file) const override
+ {
+ return file.hasFileExtension (sourceFileExtensions);
+ }
+
+ static String getOSXVersionName (int version)
+ {
+ jassert (version >= 4);
+ return "10." + String (version);
+ }
+
+ static String getSDKName (int version)
+ {
+ return getOSXVersionName (version) + " SDK";
+ }
+
+ void initialiseDependencyPathValues()
+ {
+ vst2Path.referTo (Value (new DependencyPathValueSource (getSetting (Ids::vstFolder), Ids::vst2Path, TargetOS::osx)));
+ vst3Path.referTo (Value (new DependencyPathValueSource (getSetting (Ids::vst3Folder), Ids::vst3Path, TargetOS::osx)));
+ aaxPath. referTo (Value (new DependencyPathValueSource (getSetting (Ids::aaxFolder), Ids::aaxPath, TargetOS::osx)));
+ rtasPath.referTo (Value (new DependencyPathValueSource (getSetting (Ids::rtasFolder), Ids::rtasPath, TargetOS::osx)));
+ }
+
+ void addRTASPluginSettings()
+ {
+ xcodeCanUseDwarf = false;
+
+ extraSearchPaths.add ("$(DEVELOPER_DIR)/Headers/FlatCarbon");
+ extraSearchPaths.add ("$(SDKROOT)/Developer/Headers/FlatCarbon");
+
+ static const char* p[] = { "AlturaPorts/TDMPlugIns/PlugInLibrary/Controls",
+ "AlturaPorts/TDMPlugIns/PlugInLibrary/CoreClasses",
+ "AlturaPorts/TDMPlugIns/PlugInLibrary/DSPClasses",
+ "AlturaPorts/TDMPlugIns/PlugInLibrary/EffectClasses",
+ "AlturaPorts/TDMPlugIns/PlugInLibrary/MacBuild",
+ "AlturaPorts/TDMPlugIns/PlugInLibrary/Meters",
+ "AlturaPorts/TDMPlugIns/PlugInLibrary/ProcessClasses",
+ "AlturaPorts/TDMPlugIns/PlugInLibrary/ProcessClasses/Interfaces",
+ "AlturaPorts/TDMPlugIns/PlugInLibrary/RTASP_Adapt",
+ "AlturaPorts/TDMPlugIns/PlugInLibrary/Utilities",
+ "AlturaPorts/TDMPlugIns/PlugInLibrary/ViewClasses",
+ "AlturaPorts/TDMPlugIns/DSPManager/**",
+ "AlturaPorts/TDMPlugIns/SupplementalPlugInLib/Encryption",
+ "AlturaPorts/TDMPlugIns/SupplementalPlugInLib/GraphicsExtensions",
+ "AlturaPorts/TDMPlugIns/common/**",
+ "AlturaPorts/TDMPlugIns/common/PI_LibInterface",
+ "AlturaPorts/TDMPlugIns/PACEProtection/**",
+ "AlturaPorts/TDMPlugIns/SignalProcessing/**",
+ "AlturaPorts/OMS/Headers",
+ "AlturaPorts/Fic/Interfaces/**",
+ "AlturaPorts/Fic/Source/SignalNets",
+ "AlturaPorts/DSIPublicInterface/PublicHeaders",
+ "DAEWin/Include",
+ "AlturaPorts/DigiPublic/Interfaces",
+ "AlturaPorts/DigiPublic",
+ "AlturaPorts/NewFileLibs/DOA",
+ "AlturaPorts/NewFileLibs/Cmn",
+ "xplat/AVX/avx2/avx2sdk/inc",
+ "xplat/AVX/avx2/avx2sdk/utils" };
+
+ RelativePath rtasFolder (getRTASPathValue().toString(), RelativePath::projectFolder);
+
+ for (int i = 0; i < numElementsInArray (p); ++i)
+ addToExtraSearchPaths (rtasFolder.getChildFile (p[i]));
+ }
+
+ JUCE_DECLARE_NON_COPYABLE (XCodeProjectExporter)
+};