The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2766 lines
122KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. #include "../Application/jucer_Application.h"
  18. #include "jucer_TextWithDefaultPropertyComponent.h"
  19. namespace
  20. {
  21. const char* const osxVersionDefault = "default";
  22. const int oldestSDKVersion = 5;
  23. const int currentSDKVersion = 12;
  24. const char* const osxArch_Default = "default";
  25. const char* const osxArch_Native = "Native";
  26. const char* const osxArch_32BitUniversal = "32BitUniversal";
  27. const char* const osxArch_64BitUniversal = "64BitUniversal";
  28. const char* const osxArch_64Bit = "64BitIntel";
  29. }
  30. //==============================================================================
  31. class XCodeProjectExporter : public ProjectExporter
  32. {
  33. public:
  34. //==============================================================================
  35. static const char* getNameMac() { return "Xcode (MacOSX)"; }
  36. static const char* getNameiOS() { return "Xcode (iOS)"; }
  37. static const char* getValueTreeTypeName (bool iOS) { return iOS ? "XCODE_IPHONE" : "XCODE_MAC"; }
  38. //==============================================================================
  39. XCodeProjectExporter (Project& p, const ValueTree& t, const bool isIOS)
  40. : ProjectExporter (p, t),
  41. xcodeCanUseDwarf (true),
  42. iOS (isIOS)
  43. {
  44. name = iOS ? getNameiOS() : getNameMac();
  45. if (getTargetLocationString().isEmpty())
  46. getTargetLocationValue() = getDefaultBuildsRootFolder() + (iOS ? "iOS" : "MacOSX");
  47. initialiseDependencyPathValues();
  48. if (iOS)
  49. {
  50. if (getScreenOrientationValue().toString().isEmpty())
  51. getScreenOrientationValue() = "portraitlandscape";
  52. }
  53. }
  54. static XCodeProjectExporter* createForSettings (Project& project, const ValueTree& settings)
  55. {
  56. if (settings.hasType (getValueTreeTypeName (false))) return new XCodeProjectExporter (project, settings, false);
  57. if (settings.hasType (getValueTreeTypeName (true))) return new XCodeProjectExporter (project, settings, true);
  58. return nullptr;
  59. }
  60. //==============================================================================
  61. Value getPListToMergeValue() { return getSetting ("customPList"); }
  62. String getPListToMergeString() const { return settings ["customPList"]; }
  63. Value getExtraFrameworksValue() { return getSetting (Ids::extraFrameworks); }
  64. String getExtraFrameworksString() const { return settings [Ids::extraFrameworks]; }
  65. Value getPostBuildScriptValue() { return getSetting (Ids::postbuildCommand); }
  66. String getPostBuildScript() const { return settings [Ids::postbuildCommand]; }
  67. Value getPreBuildScriptValue() { return getSetting (Ids::prebuildCommand); }
  68. String getPreBuildScript() const { return settings [Ids::prebuildCommand]; }
  69. Value getScreenOrientationValue() { return getSetting (Ids::iosScreenOrientation); }
  70. String getScreenOrientationString() const { return settings [Ids::iosScreenOrientation]; }
  71. Value getCustomResourceFoldersValue() { return getSetting (Ids::customXcodeResourceFolders); }
  72. String getCustomResourceFoldersString() const { return getSettingString (Ids::customXcodeResourceFolders).replaceCharacters ("\r\n", "::"); }
  73. Value getCustomXcassetsFolderValue() { return getSetting (Ids::customXcassetsFolder); }
  74. String getCustomXcassetsFolderString() const { return settings [Ids::customXcassetsFolder]; }
  75. Value getInAppPurchasesValue() { return getSetting (Ids::iosInAppPurchases); }
  76. bool isInAppPurchasesEnabled() const { return settings [Ids::iosInAppPurchases]; }
  77. Value getBackgroundAudioValue() { return getSetting (Ids::iosBackgroundAudio); }
  78. bool isBackgroundAudioEnabled() const { return settings [Ids::iosBackgroundAudio]; }
  79. Value getBackgroundBleValue() { return getSetting (Ids::iosBackgroundBle); }
  80. bool isBackgroundBleEnabled() const { return settings [Ids::iosBackgroundBle]; }
  81. Value getIosDevelopmentTeamIDValue() { return getSetting (Ids::iosDevelopmentTeamID); }
  82. String getIosDevelopmentTeamIDString() const { return settings [Ids::iosDevelopmentTeamID]; }
  83. bool usesMMFiles() const override { return true; }
  84. bool canCopeWithDuplicateFiles() override { return true; }
  85. bool supportsUserDefinedConfigurations() const override { return true; }
  86. bool isXcode() const override { return true; }
  87. bool isVisualStudio() const override { return false; }
  88. bool isCodeBlocks() const override { return false; }
  89. bool isMakefile() const override { return false; }
  90. bool isAndroidStudio() const override { return false; }
  91. bool isAndroidAnt() const override { return false; }
  92. bool isAndroid() const override { return false; }
  93. bool isWindows() const override { return false; }
  94. bool isLinux() const override { return false; }
  95. bool isOSX() const override { return ! iOS; }
  96. bool isiOS() const override { return iOS; }
  97. bool supportsVST() const override { return ! iOS; }
  98. bool supportsVST3() const override { return ! iOS; }
  99. bool supportsAAX() const override { return ! iOS; }
  100. bool supportsRTAS() const override { return ! iOS; }
  101. bool supportsAU() const override { return ! iOS; }
  102. bool supportsAUv3() const override { return true; }
  103. bool supportsStandalone() const override { return true; }
  104. void createExporterProperties (PropertyListBuilder& props) override
  105. {
  106. if (iOS)
  107. {
  108. props.add (new TextPropertyComponent (getCustomXcassetsFolderValue(), "Custom Xcassets folder", 128, false),
  109. "If this field is not empty, your Xcode project will use the custom xcassets folder specified here "
  110. "for the app icons and launchimages, and will ignore the Icon files specified above.");
  111. }
  112. props.add (new TextPropertyComponent (getCustomResourceFoldersValue(), "Custom Xcode Resource folders", 8192, true),
  113. "You can specify a list of custom resource folders here (separated by newlines or whitespace). "
  114. "References to these folders will then be added to the Xcode resources. "
  115. "This way you can specify them for OS X and iOS separately, and modify the content of the resource folders "
  116. "without re-saving the Projucer project.");
  117. if (iOS)
  118. {
  119. static const char* orientations[] = { "Portrait and Landscape", "Portrait", "Landscape", nullptr };
  120. static const char* orientationValues[] = { "portraitlandscape", "portrait", "landscape", nullptr };
  121. props.add (new ChoicePropertyComponent (getScreenOrientationValue(), "Screen orientation",StringArray (orientations), Array<var> (orientationValues)),
  122. "The screen orientations that this app should support");
  123. props.add (new BooleanPropertyComponent (getSetting ("UIFileSharingEnabled"), "File Sharing Enabled", "Enabled"),
  124. "Enable this to expose your app's files to iTunes.");
  125. props.add (new BooleanPropertyComponent (getSetting ("UIStatusBarHidden"), "Status Bar Hidden", "Enabled"),
  126. "Enable this to disable the status bar in your app.");
  127. props.add (new BooleanPropertyComponent (getInAppPurchasesValue(), "In-App purchases capability", "Enabled"),
  128. "Enable this to grant your app the capability for in-app purchases. "
  129. "This option requires that you specify a valid Development Team ID.");
  130. props.add (new BooleanPropertyComponent (getBackgroundAudioValue(), "Audio background capability", "Enabled"),
  131. "Enable this to grant your app the capability to access audio when in background mode.");
  132. props.add (new BooleanPropertyComponent (getBackgroundBleValue(), "Bluetooth MIDI background capability", "Enabled"),
  133. "Enable this to grant your app the capability to connect to Bluetooth LE devices when in background mode.");
  134. }
  135. else if (projectType.isGUIApplication())
  136. {
  137. props.add (new TextPropertyComponent (getSetting ("documentExtensions"), "Document file extensions", 128, false),
  138. "A comma-separated list of file extensions for documents that your app can open. "
  139. "Using a leading '.' is optional, and the extensions are not case-sensitive.");
  140. }
  141. props.add (new TextPropertyComponent (getPListToMergeValue(), "Custom PList", 8192, true),
  142. "You can paste the contents of an XML PList file in here, and the settings that it contains will override any "
  143. "settings that the Projucer creates. BEWARE! When doing this, be careful to remove from the XML any "
  144. "values that you DO want the Projucer to change!");
  145. props.add (new TextPropertyComponent (getExtraFrameworksValue(), "Extra Frameworks", 2048, false),
  146. "A comma-separated list of extra frameworks that should be added to the build. "
  147. "(Don't include the .framework extension in the name)");
  148. props.add (new TextPropertyComponent (getPreBuildScriptValue(), "Pre-build shell script", 32768, true),
  149. "Some shell-script that will be run before a build starts.");
  150. props.add (new TextPropertyComponent (getPostBuildScriptValue(), "Post-build shell script", 32768, true),
  151. "Some shell-script that will be run after a build completes.");
  152. props.add (new TextPropertyComponent (getIosDevelopmentTeamIDValue(), "Development Team ID", 10, false),
  153. "The Development Team ID to be used for setting up code-signing your iOS app. This is a ten-character "
  154. "string (for example, \"S7B6T5XJ2Q\") that describes the distribution certificate Apple issued to you. "
  155. "You can find this string in the OS X app Keychain Access under \"Certificates\".");
  156. }
  157. bool launchProject() override
  158. {
  159. #if JUCE_MAC
  160. return getProjectBundle().startAsProcess();
  161. #else
  162. return false;
  163. #endif
  164. }
  165. bool canLaunchProject() override
  166. {
  167. #if JUCE_MAC
  168. return true;
  169. #else
  170. return false;
  171. #endif
  172. }
  173. //==============================================================================
  174. void create (const OwnedArray<LibraryModule>&) const override
  175. {
  176. for (auto& target : targets)
  177. if (target->xcodeCreatePList)
  178. target->infoPlistFile = getTargetFolder().getChildFile (target->getInfoPlistName());
  179. menuNibFile = getTargetFolder().getChildFile ("RecentFilesMenuTemplate.nib");
  180. createIconFile();
  181. File projectBundle (getProjectBundle());
  182. createDirectoryOrThrow (projectBundle);
  183. createObjects();
  184. File projectFile (projectBundle.getChildFile ("project.pbxproj"));
  185. {
  186. MemoryOutputStream mo;
  187. writeProjectFile (mo);
  188. overwriteFileIfDifferentOrThrow (projectFile, mo);
  189. }
  190. writeInfoPlistFiles();
  191. // Deleting the .rsrc files can be needed to force Xcode to update the version number.
  192. deleteRsrcFiles();
  193. if (! ProjucerApplication::getApp().isRunningCommandLine)
  194. {
  195. // Workaround for a bug where Xcode thinks the project is invalid if opened immedietely
  196. // after writing
  197. Thread::sleep (2000);
  198. }
  199. }
  200. //==============================================================================
  201. void addPlatformSpecificSettingsForProjectType (const ProjectType& type) override
  202. {
  203. if (type.isGUIApplication())
  204. targets.add (new Target (Target::GUIApp, *this));
  205. else if (type.isCommandLineApp())
  206. targets.add (new Target (Target::ConsoleApp, *this));
  207. else if (type.isStaticLibrary())
  208. targets.add (new Target (Target::StaticLibrary, *this));
  209. else if (type.isDynamicLibrary())
  210. targets.add (new Target (Target::DynamicLibrary, *this));
  211. else if (type.isAudioPlugin())
  212. {
  213. if (project.shouldBuildVST().getValue() && supportsVST())
  214. targets.add (new Target (Target::VSTPlugIn, *this));
  215. if (project.shouldBuildVST3().getValue() && supportsVST3())
  216. targets.add (new Target (Target::VST3PlugIn, *this));
  217. if (project.shouldBuildAU().getValue() && supportsAU())
  218. targets.add (new Target (Target::AudioUnitPlugIn, *this));
  219. if (project.shouldBuildAUv3().getValue())
  220. targets.add (new Target (Target::AudioUnitv3PlugIn, *this));
  221. if (project.shouldBuildAAX().getValue() && supportsAAX())
  222. targets.add (new Target (Target::AAXPlugIn, *this));
  223. if (project.shouldBuildStandalone().getValue())
  224. targets.add (new Target (Target::StandalonePlugIn, *this));
  225. if (project.shouldBuildRTAS().getValue() && supportsRTAS())
  226. {
  227. targets.add (new Target (Target::RTASPlugIn, *this));
  228. addRTASPluginSettings();
  229. }
  230. if (targets.size() > 0)
  231. targets.add (new Target (Target::SharedCodeTarget, *this));
  232. }
  233. if (targets.size() > 1)
  234. targets.insert (0, new Target (Target::AggregateTarget, *this));
  235. // If you hit this assert, you tried to generate a project for an exporter
  236. // that does not support any of your targets!
  237. jassert (targets.size() > 0);
  238. }
  239. void updateDeprecatedProjectSettingsInteractively() override
  240. {
  241. if (hasInvalidPostBuildScript())
  242. {
  243. String alertWindowText = iOS ? "Your Xcode (iOS) Exporter settings use an invalid post-build script. Click 'Update' to remove it."
  244. : "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"
  245. "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"
  246. "Click 'Update' to remove the script (otherwise your plug-in may not compile correctly).";
  247. if (AlertWindow::showOkCancelBox (AlertWindow::WarningIcon,
  248. "Project settings: " + project.getDocumentTitle(),
  249. alertWindowText, "Update", "Cancel", nullptr, nullptr))
  250. getPostBuildScriptValue() = var();
  251. }
  252. }
  253. bool hasInvalidPostBuildScript() const
  254. {
  255. // check whether the script is identical to the old one that the Introjucer used to auto-generate
  256. return (MD5 (getPostBuildScript().toUTF8()).toHexString() == "265ac212a7e734c5bbd6150e1eae18a1");
  257. }
  258. protected:
  259. //==============================================================================
  260. class XcodeBuildConfiguration : public BuildConfiguration
  261. {
  262. public:
  263. XcodeBuildConfiguration (Project& p, const ValueTree& t, const bool isIOS, const ProjectExporter& e)
  264. : BuildConfiguration (p, t, e),
  265. iOS (isIOS),
  266. osxSDKVersion (config, Ids::osxSDK, nullptr, "default"),
  267. osxDeploymentTarget (config, Ids::osxCompatibility, nullptr, "default"),
  268. iosDeploymentTarget (config, Ids::iosCompatibility, nullptr, "default"),
  269. osxArchitecture (config, Ids::osxArchitecture, nullptr, "default"),
  270. customXcodeFlags (config, Ids::customXcodeFlags, nullptr),
  271. cppLanguageStandard (config, Ids::cppLanguageStandard, nullptr),
  272. cppStandardLibrary (config, Ids::cppLibType, nullptr),
  273. codeSignIdentity (config, Ids::codeSigningIdentity, nullptr, iOS ? "iPhone Developer" : "Mac Developer"),
  274. fastMathEnabled (config, Ids::fastMath, nullptr),
  275. linkTimeOptimisationEnabled (config, Ids::linkTimeOptimisation, nullptr),
  276. stripLocalSymbolsEnabled (config, Ids::stripLocalSymbols, nullptr),
  277. vstBinaryLocation (config, Ids::xcodeVstBinaryLocation, nullptr, "$(HOME)/Library/Audio/Plug-Ins/VST/"),
  278. vst3BinaryLocation (config, Ids::xcodeVst3BinaryLocation, nullptr, "$(HOME)/Library/Audio/Plug-Ins/VST3/"),
  279. auBinaryLocation (config, Ids::xcodeAudioUnitBinaryLocation, nullptr, "$(HOME)/Library/Audio/Plug-Ins/Components/"),
  280. rtasBinaryLocation (config, Ids::xcodeRtasBinaryLocation, nullptr, "/Library/Application Support/Digidesign/Plug-Ins/"),
  281. aaxBinaryLocation (config, Ids::xcodeAaxBinaryLocation, nullptr, "/Library/Application Support/Avid/Audio/Plug-Ins/")
  282. {
  283. }
  284. //==========================================================================
  285. bool iOS;
  286. CachedValue<String> osxSDKVersion, osxDeploymentTarget, iosDeploymentTarget, osxArchitecture,
  287. customXcodeFlags, cppLanguageStandard, cppStandardLibrary, codeSignIdentity;
  288. CachedValue<bool> fastMathEnabled, linkTimeOptimisationEnabled, stripLocalSymbolsEnabled;
  289. CachedValue<String> vstBinaryLocation, vst3BinaryLocation, auBinaryLocation, rtasBinaryLocation, aaxBinaryLocation;
  290. //==========================================================================
  291. var getDefaultOptimisationLevel() const override { return var ((int) (isDebug() ? gccO0 : gccO3)); }
  292. void createConfigProperties (PropertyListBuilder& props) override
  293. {
  294. addXcodePluginInstallPathProperties (props);
  295. addGCCOptimisationProperty (props);
  296. if (iOS)
  297. {
  298. 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", "10.0", 0 };
  299. 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", "10.0", 0 };
  300. props.add (new ChoicePropertyComponent (iosDeploymentTarget.getPropertyAsValue(), "iOS Deployment Target",
  301. StringArray (iosVersions), Array<var> (iosVersionValues)),
  302. "The minimum version of iOS that the target binary will run on.");
  303. }
  304. else
  305. {
  306. StringArray sdkVersionNames, osxVersionNames;
  307. Array<var> versionValues;
  308. sdkVersionNames.add ("Use Default");
  309. osxVersionNames.add ("Use Default");
  310. versionValues.add (osxVersionDefault);
  311. for (int ver = oldestSDKVersion; ver <= currentSDKVersion; ++ver)
  312. {
  313. sdkVersionNames.add (getSDKName (ver));
  314. osxVersionNames.add (getOSXVersionName (ver));
  315. versionValues.add (getSDKName (ver));
  316. }
  317. props.add (new ChoicePropertyComponent (osxSDKVersion.getPropertyAsValue(), "OSX Base SDK Version", sdkVersionNames, versionValues),
  318. "The version of OSX to link against in the XCode build.");
  319. props.add (new ChoicePropertyComponent (osxDeploymentTarget.getPropertyAsValue(), "OSX Deployment Target", osxVersionNames, versionValues),
  320. "The minimum version of OSX that the target binary will be compatible with.");
  321. const char* osxArch[] = { "Use Default", "Native architecture of build machine",
  322. "Universal Binary (32-bit)", "Universal Binary (32/64-bit)", "64-bit Intel", 0 };
  323. const char* osxArchValues[] = { osxArch_Default, osxArch_Native, osxArch_32BitUniversal,
  324. osxArch_64BitUniversal, osxArch_64Bit, 0 };
  325. props.add (new ChoicePropertyComponent (osxArchitecture.getPropertyAsValue(), "OSX Architecture",
  326. StringArray (osxArch), Array<var> (osxArchValues)),
  327. "The type of OSX binary that will be produced.");
  328. }
  329. props.add (new TextPropertyComponent (customXcodeFlags.getPropertyAsValue(), "Custom Xcode flags", 8192, false),
  330. "A comma-separated list of custom Xcode setting flags which will be appended to the list of generated flags, "
  331. "e.g. MACOSX_DEPLOYMENT_TARGET_i386 = 10.5, VALID_ARCHS = \"ppc i386 x86_64\"");
  332. const char* cppLanguageStandardNames[] = { "Use Default", "C++98", "GNU++98", "C++11", "GNU++11", "C++14", "GNU++14", nullptr };
  333. Array<var> cppLanguageStandardValues;
  334. cppLanguageStandardValues.add (var());
  335. cppLanguageStandardValues.add ("c++98");
  336. cppLanguageStandardValues.add ("gnu++98");
  337. cppLanguageStandardValues.add ("c++11");
  338. cppLanguageStandardValues.add ("gnu++11");
  339. cppLanguageStandardValues.add ("c++14");
  340. cppLanguageStandardValues.add ("gnu++14");
  341. props.add (new ChoicePropertyComponent (cppLanguageStandard.getPropertyAsValue(), "C++ Language Standard",
  342. StringArray (cppLanguageStandardNames), cppLanguageStandardValues),
  343. "The standard of the C++ language that will be used for compilation.");
  344. const char* cppLibNames[] = { "Use Default", "LLVM libc++", "GNU libstdc++", nullptr };
  345. Array<var> cppLibValues;
  346. cppLibValues.add (var());
  347. cppLibValues.add ("libc++");
  348. cppLibValues.add ("libstdc++");
  349. props.add (new ChoicePropertyComponent (cppStandardLibrary.getPropertyAsValue(), "C++ Library", StringArray (cppLibNames), cppLibValues),
  350. "The type of C++ std lib that will be linked.");
  351. props.add (new TextWithDefaultPropertyComponent<String> (codeSignIdentity, "Code-signing Identity", 1024),
  352. "The name of a code-signing identity for Xcode to apply.");
  353. props.add (new BooleanPropertyComponent (fastMathEnabled.getPropertyAsValue(), "Relax IEEE compliance", "Enabled"),
  354. "Enable this to use FAST_MATH non-IEEE mode. (Warning: this can have unexpected results!)");
  355. props.add (new BooleanPropertyComponent (linkTimeOptimisationEnabled.getPropertyAsValue(), "Link-Time Optimisation", "Enabled"),
  356. "Enable this to perform link-time code generation. This is recommended for release builds.");
  357. props.add (new BooleanPropertyComponent (stripLocalSymbolsEnabled.getPropertyAsValue(), "Strip local symbols", "Enabled"),
  358. "Enable this to strip any locally defined symbols resulting in a smaller binary size. Enabling this will also remove any function names from crash logs. Must be disabled for static library projects.");
  359. }
  360. private:
  361. //==========================================================================
  362. void addXcodePluginInstallPathProperties (PropertyListBuilder& props)
  363. {
  364. if (project.shouldBuildVST().getValue())
  365. props.add (new TextWithDefaultPropertyComponent<String> (vstBinaryLocation, "VST Binary location", 1024),
  366. "The folder in which the compiled VST binary should be placed.");
  367. if (project.shouldBuildVST3().getValue())
  368. props.add (new TextWithDefaultPropertyComponent<String> (vst3BinaryLocation, "VST3 Binary location", 1024),
  369. "The folder in which the compiled VST3 binary should be placed.");
  370. if (project.shouldBuildAU().getValue())
  371. props.add (new TextWithDefaultPropertyComponent<String> (auBinaryLocation, "AU Binary location", 1024),
  372. "The folder in which the compiled AU binary should be placed.");
  373. if (project.shouldBuildRTAS().getValue())
  374. props.add (new TextWithDefaultPropertyComponent<String> (rtasBinaryLocation, "RTAS Binary location", 1024),
  375. "The folder in which the compiled RTAS binary should be placed.");
  376. if (project.shouldBuildAAX().getValue())
  377. props.add (new TextWithDefaultPropertyComponent<String> (aaxBinaryLocation, "AAX Binary location", 1024),
  378. "The folder in which the compiled AAX binary should be placed.");
  379. }
  380. };
  381. BuildConfiguration::Ptr createBuildConfig (const ValueTree& v) const override
  382. {
  383. return new XcodeBuildConfiguration (project, v, iOS, *this);
  384. }
  385. public:
  386. //==============================================================================
  387. /* The numbers for these enum values are defined by Xcode for the different
  388. possible destinations of a "copy files" post-build step.
  389. */
  390. enum XcodeCopyFilesDestinationIDs
  391. {
  392. kWrapperFolder = 1,
  393. kExecutablesFolder = 6,
  394. kResourcesFolder = 7,
  395. kFrameworksFolder = 10,
  396. kSharedFrameworksFolder = 11,
  397. kSharedSupportFolder = 12,
  398. kPluginsFolder = 13,
  399. kJavaResourcesFolder = 15,
  400. kXPCServicesFolder = 16
  401. };
  402. //==============================================================================
  403. struct Target
  404. {
  405. enum Type
  406. {
  407. GUIApp = 0,
  408. ConsoleApp = 1,
  409. StaticLibrary = 2,
  410. DynamicLibrary = 3,
  411. VSTPlugIn = 10,
  412. VST3PlugIn = 11,
  413. AAXPlugIn = 12,
  414. RTASPlugIn = 13,
  415. AudioUnitPlugIn = 14,
  416. AudioUnitv3PlugIn = 15,
  417. StandalonePlugIn = 16,
  418. SharedCodeTarget = 20, // internal
  419. AggregateTarget = 21,
  420. unspecified = 30
  421. };
  422. //==============================================================================
  423. Target (Type targetType, const XCodeProjectExporter& exporter)
  424. : type (targetType),
  425. owner (exporter)
  426. {
  427. switch (type)
  428. {
  429. case GUIApp:
  430. xcodeIsBundle = false;
  431. xcodeIsExecutable = true;
  432. xcodeCreatePList = true;
  433. xcodePackageType = "APPL";
  434. xcodeBundleSignature = "????";
  435. xcodeFileType = "wrapper.application";
  436. xcodeBundleExtension = ".app";
  437. xcodeProductType = "com.apple.product-type.application";
  438. xcodeCopyToProductInstallPathAfterBuild = false;
  439. break;
  440. case ConsoleApp:
  441. xcodeIsBundle = false;
  442. xcodeIsExecutable = true;
  443. xcodeCreatePList = false;
  444. xcodeFileType = "compiled.mach-o.executable";
  445. xcodeBundleExtension = String();
  446. xcodeProductType = "com.apple.product-type.tool";
  447. xcodeCopyToProductInstallPathAfterBuild = false;
  448. break;
  449. case StaticLibrary:
  450. xcodeIsBundle = false;
  451. xcodeIsExecutable = false;
  452. xcodeCreatePList = false;
  453. xcodeFileType = "archive.ar";
  454. xcodeProductType = "com.apple.product-type.library.static";
  455. xcodeCopyToProductInstallPathAfterBuild = false;
  456. break;
  457. case DynamicLibrary:
  458. xcodeIsBundle = false;
  459. xcodeIsExecutable = false;
  460. xcodeCreatePList = false;
  461. xcodeFileType = "compiled.mach-o.dylib";
  462. xcodeProductType = "com.apple.product-type.library.dynamic";
  463. xcodeBundleExtension = ".dylib";
  464. xcodeCopyToProductInstallPathAfterBuild = false;
  465. break;
  466. case VSTPlugIn:
  467. xcodeIsBundle = true;
  468. xcodeIsExecutable = false;
  469. xcodeCreatePList = true;
  470. xcodePackageType = "BNDL";
  471. xcodeBundleSignature = "????";
  472. xcodeFileType = "wrapper.cfbundle";
  473. xcodeBundleExtension = ".vst";
  474. xcodeProductType = "com.apple.product-type.bundle";
  475. xcodeCopyToProductInstallPathAfterBuild = true;
  476. break;
  477. case VST3PlugIn:
  478. xcodeIsBundle = true;
  479. xcodeIsExecutable = false;
  480. xcodeCreatePList = true;
  481. xcodePackageType = "BNDL";
  482. xcodeBundleSignature = "????";
  483. xcodeFileType = "wrapper.cfbundle";
  484. xcodeBundleExtension = ".vst3";
  485. xcodeProductType = "com.apple.product-type.bundle";
  486. xcodeCopyToProductInstallPathAfterBuild = true;
  487. break;
  488. case AudioUnitPlugIn:
  489. xcodeIsBundle = true;
  490. xcodeIsExecutable = false;
  491. xcodeCreatePList = true;
  492. xcodePackageType = "BNDL";
  493. xcodeBundleSignature = "????";
  494. xcodeFileType = "wrapper.cfbundle";
  495. xcodeBundleExtension = ".component";
  496. xcodeProductType = "com.apple.product-type.bundle";
  497. xcodeCopyToProductInstallPathAfterBuild = true;
  498. addExtraAudioUnitTargetSettings();
  499. break;
  500. case StandalonePlugIn:
  501. xcodeIsBundle = false;
  502. xcodeIsExecutable = true;
  503. xcodeCreatePList = true;
  504. xcodePackageType = "APPL";
  505. xcodeBundleSignature = "????";
  506. xcodeCreatePList = true;
  507. xcodeFileType = "wrapper.application";
  508. xcodeBundleExtension = ".app";
  509. xcodeProductType = "com.apple.product-type.application";
  510. xcodeCopyToProductInstallPathAfterBuild = false;
  511. break;
  512. case AudioUnitv3PlugIn:
  513. xcodeIsBundle = false;
  514. xcodeIsExecutable = false;
  515. xcodeCreatePList = true;
  516. xcodePackageType = "XPC!";
  517. xcodeBundleSignature = "????";
  518. xcodeFileType = "wrapper.app-extension";
  519. xcodeBundleExtension = ".appex";
  520. xcodeBundleIDSubPath = "AUv3";
  521. xcodeProductType = "com.apple.product-type.app-extension";
  522. xcodeCopyToProductInstallPathAfterBuild = false;
  523. addExtraAudioUnitv3PlugInTargetSettings();
  524. break;
  525. case AAXPlugIn:
  526. xcodeIsBundle = true;
  527. xcodeIsExecutable = false;
  528. xcodeCreatePList = true;
  529. xcodePackageType = "TDMw";
  530. xcodeBundleSignature = "PTul";
  531. xcodeFileType = "wrapper.cfbundle";
  532. xcodeBundleExtension = ".aaxplugin";
  533. xcodeProductType = "com.apple.product-type.bundle";
  534. xcodeCopyToProductInstallPathAfterBuild = true;
  535. addExtraAAXTargetSettings();
  536. break;
  537. case RTASPlugIn:
  538. xcodeIsBundle = true;
  539. xcodeIsExecutable = false;
  540. xcodeCreatePList = true;
  541. xcodePackageType = "TDMw";
  542. xcodeBundleSignature = "PTul";
  543. xcodeFileType = "wrapper.cfbundle";
  544. xcodeBundleExtension = ".dpm";
  545. xcodeProductType = "com.apple.product-type.bundle";
  546. xcodeCopyToProductInstallPathAfterBuild = true;
  547. addExtraRTASTargetSettings();
  548. break;
  549. case SharedCodeTarget:
  550. xcodeIsBundle = false;
  551. xcodeIsExecutable = false;
  552. xcodeCreatePList = false;
  553. xcodeFileType = "archive.ar";
  554. xcodeProductType = "com.apple.product-type.library.static";
  555. xcodeCopyToProductInstallPathAfterBuild = false;
  556. break;
  557. case AggregateTarget:
  558. xcodeIsBundle = false;
  559. xcodeIsExecutable = false;
  560. xcodeCreatePList = false;
  561. xcodeCopyToProductInstallPathAfterBuild = false;
  562. break;
  563. default:
  564. // unknown target type!
  565. jassertfalse;
  566. break;
  567. }
  568. }
  569. const char* getName() const noexcept
  570. {
  571. switch (type)
  572. {
  573. case GUIApp: return "App";
  574. case ConsoleApp: return "ConsoleApp";
  575. case StaticLibrary: return "Static Library";
  576. case DynamicLibrary: return "Dynamic Library";
  577. case VSTPlugIn: return "VST";
  578. case VST3PlugIn: return "VST3";
  579. case AudioUnitPlugIn: return "AU";
  580. case StandalonePlugIn: return "AUv3 Standalone";
  581. case AudioUnitv3PlugIn: return "AUv3 AppExtension";
  582. case AAXPlugIn: return "AAX";
  583. case RTASPlugIn: return "RTAS";
  584. case SharedCodeTarget: return "Shared Code";
  585. case AggregateTarget: return "All";
  586. default: return "undefined";
  587. }
  588. }
  589. String getXCodeSchemeName() const
  590. {
  591. return owner.projectName + " (" + getName() + ")";
  592. }
  593. bool shouldBuildVST() const { return owner.supportsVST() && owner.project.shouldBuildVST().getValue() && (type == SharedCodeTarget || type == VSTPlugIn); }
  594. bool shouldBuildVST3() const { return owner.supportsVST3() && owner.project.shouldBuildVST3().getValue() && (type == SharedCodeTarget || type == VST3PlugIn); }
  595. bool shouldBuildAAX() const { return owner.supportsAAX() && owner.project.shouldBuildAAX().getValue() && (type == SharedCodeTarget || type == AAXPlugIn); }
  596. bool shouldBuildRTAS() const { return owner.supportsRTAS() && owner.project.shouldBuildRTAS().getValue() && (type == SharedCodeTarget || type == RTASPlugIn); }
  597. bool shouldBuildAU() const { return owner.supportsAU() && owner.project.shouldBuildAU().getValue() && (type == SharedCodeTarget || type == AudioUnitPlugIn); }
  598. bool shouldBuildAUv3() const { return owner.supportsAUv3() && owner.project.shouldBuildAUv3().getValue() && (type == SharedCodeTarget || type == AudioUnitv3PlugIn); }
  599. bool shouldBuildStandalone() const { return owner.project.shouldBuildStandalone().getValue() && (type == SharedCodeTarget || type == StandalonePlugIn); }
  600. String getID() const
  601. {
  602. return owner.createID (String ("__target") + getName());
  603. }
  604. String getInfoPlistName() const
  605. {
  606. return String ("Info-") + String (getName()).replace (" ", "_") + String (".plist");
  607. }
  608. String xcodePackageType, xcodeBundleSignature, xcodeBundleExtension;
  609. String xcodeProductType, xcodeFileType;
  610. String xcodeOtherRezFlags, xcodeExcludedFiles64Bit, xcodeBundleIDSubPath;
  611. bool xcodeIsBundle, xcodeCreatePList, xcodeIsExecutable, xcodeCopyToProductInstallPathAfterBuild;
  612. StringArray xcodeFrameworks, xcodeLibs;
  613. Type type;
  614. Array<XmlElement> xcodeExtraPListEntries;
  615. Array<RelativePath> xcodeExtraLibrariesDebug, xcodeExtraLibrariesRelease;
  616. StringArray frameworkIDs, buildPhaseIDs, configIDs, sourceIDs, rezFileIDs;
  617. String dependencyID, mainBuildProductID;
  618. File infoPlistFile;
  619. //==============================================================================
  620. void addMainBuildProduct() const
  621. {
  622. jassert (xcodeFileType.isNotEmpty());
  623. jassert (xcodeBundleExtension.isEmpty() || xcodeBundleExtension.startsWithChar ('.'));
  624. if (ProjectExporter::BuildConfiguration::Ptr config = owner.getConfiguration(0))
  625. {
  626. String productName (owner.replacePreprocessorTokens (*config, config->getTargetBinaryNameString()));
  627. if (xcodeFileType == "archive.ar")
  628. productName = getLibbedFilename (productName);
  629. else
  630. productName += xcodeBundleExtension;
  631. addBuildProduct (xcodeFileType, productName);
  632. }
  633. }
  634. //==============================================================================
  635. void addBuildProduct (const String& fileType, const String& binaryName) const
  636. {
  637. ValueTree* v = new ValueTree (owner.createID (String ("__productFileID") + getName()));
  638. v->setProperty ("isa", "PBXFileReference", nullptr);
  639. v->setProperty ("explicitFileType", fileType, nullptr);
  640. v->setProperty ("includeInIndex", (int) 0, nullptr);
  641. v->setProperty ("path", sanitisePath (binaryName), nullptr);
  642. v->setProperty ("sourceTree", "BUILT_PRODUCTS_DIR", nullptr);
  643. owner.pbxFileReferences.add (v);
  644. }
  645. //==============================================================================
  646. void addDependency()
  647. {
  648. jassert (dependencyID.isEmpty());
  649. dependencyID = owner.createID (String ("__dependency") + getName());
  650. ValueTree* const v = new ValueTree (dependencyID);
  651. v->setProperty ("isa", "PBXTargetDependency", nullptr);
  652. v->setProperty ("target", getID(), nullptr);
  653. owner.misc.add (v);
  654. }
  655. String getDependencyID() const
  656. {
  657. jassert (dependencyID.isNotEmpty());
  658. return dependencyID;
  659. }
  660. //==============================================================================
  661. void addTargetConfig (const String& configName, const StringArray& buildSettings)
  662. {
  663. String configID = owner.createID (String ("targetconfigid_") + getName() + String ("_") + configName);
  664. ValueTree* v = new ValueTree (configID);
  665. v->setProperty ("isa", "XCBuildConfiguration", nullptr);
  666. v->setProperty ("buildSettings", indentBracedList (buildSettings), nullptr);
  667. v->setProperty (Ids::name, configName, nullptr);
  668. configIDs.add (configID);
  669. owner.targetConfigs.add (v);
  670. }
  671. //==============================================================================
  672. String getTargetAttributes() const
  673. {
  674. String attributes;
  675. attributes << getID() << " = { ";
  676. String developmentTeamID = owner.getIosDevelopmentTeamIDString();
  677. if (developmentTeamID.isNotEmpty())
  678. attributes << "DevelopmentTeam = " << developmentTeamID << "; ";
  679. const int inAppPurchasesEnabled = (owner.iOS && owner.isInAppPurchasesEnabled()) ? 1 : 0;
  680. const int sandboxEnabled = (type == Target::AudioUnitv3PlugIn ? 1 : 0);
  681. attributes << "SystemCapabilities = {";
  682. attributes << "com.apple.InAppPurchase = { enabled = " << inAppPurchasesEnabled << "; }; ";
  683. attributes << "com.apple.Sandbox = { enabled = " << sandboxEnabled << "; }; ";
  684. attributes << "}; };";
  685. return attributes;
  686. }
  687. //==============================================================================
  688. ValueTree& addBuildPhase (const String& buildPhaseType, const StringArray& fileIds, const StringRef humanReadableName = StringRef())
  689. {
  690. String buildPhaseName = buildPhaseType + String ("_") + getName() + String ("_") + (humanReadableName.isNotEmpty() ? String (humanReadableName) : String ("resbuildphase"));
  691. String buildPhaseId (owner.createID (buildPhaseName));
  692. int n = 0;
  693. while (buildPhaseIDs.contains (buildPhaseId))
  694. buildPhaseId = owner.createID (buildPhaseName + String (++n));
  695. buildPhaseIDs.add (buildPhaseId);
  696. ValueTree* v = new ValueTree (buildPhaseId);
  697. v->setProperty ("isa", buildPhaseType, nullptr);
  698. v->setProperty ("buildActionMask", "2147483647", nullptr);
  699. v->setProperty ("files", indentParenthesisedList (fileIds), nullptr);
  700. v->setProperty ("runOnlyForDeploymentPostprocessing", (int) 0, nullptr);
  701. if (humanReadableName.isNotEmpty())
  702. v->setProperty ("name", String (humanReadableName), nullptr);
  703. owner.misc.add (v);
  704. return *v;
  705. }
  706. //==============================================================================
  707. StringArray getTargetSettings (const XcodeBuildConfiguration& config) const
  708. {
  709. if (type == AggregateTarget)
  710. // the aggregate target should not specify any settings at all!
  711. // it just defines dependencies on the other targets.
  712. return StringArray();
  713. StringArray s;
  714. String bundleIdentifier = owner.project.getBundleIdentifier().toString();
  715. if (xcodeBundleIDSubPath.isNotEmpty())
  716. {
  717. StringArray bundleIdSegments = StringArray::fromTokens (bundleIdentifier, ".", StringRef());
  718. jassert (bundleIdSegments.size() > 0);
  719. bundleIdentifier += String (".") + bundleIdSegments[bundleIdSegments.size() - 1] + xcodeBundleIDSubPath;
  720. }
  721. s.add ("PRODUCT_BUNDLE_IDENTIFIER = " + bundleIdentifier);
  722. const String arch ((! owner.isiOS() && type == Target::AudioUnitv3PlugIn) ? osxArch_64Bit : config.osxArchitecture.get());
  723. if (arch == osxArch_Native) s.add ("ARCHS = \"$(NATIVE_ARCH_ACTUAL)\"");
  724. else if (arch == osxArch_32BitUniversal) s.add ("ARCHS = \"$(ARCHS_STANDARD_32_BIT)\"");
  725. else if (arch == osxArch_64BitUniversal) s.add ("ARCHS = \"$(ARCHS_STANDARD_32_64_BIT)\"");
  726. else if (arch == osxArch_64Bit) s.add ("ARCHS = \"$(ARCHS_STANDARD_64_BIT)\"");
  727. s.add ("HEADER_SEARCH_PATHS = " + owner.getHeaderSearchPaths (config));
  728. s.add ("GCC_OPTIMIZATION_LEVEL = " + config.getGCCOptimisationFlag());
  729. if (xcodeCreatePList)
  730. s.add ("INFOPLIST_FILE = " + infoPlistFile.getFileName());
  731. if (config.linkTimeOptimisationEnabled.get())
  732. s.add ("LLVM_LTO = YES");
  733. if (config.fastMathEnabled.get())
  734. s.add ("GCC_FAST_MATH = YES");
  735. const String extraFlags (owner.replacePreprocessorTokens (config, owner.getExtraCompilerFlagsString()).trim());
  736. if (extraFlags.isNotEmpty())
  737. s.add ("OTHER_CPLUSPLUSFLAGS = \"" + extraFlags + "\"");
  738. String installPath = getInstallPathForConfiguration (config);
  739. if (installPath.isNotEmpty())
  740. {
  741. s.add ("INSTALL_PATH = \"" + installPath + "\"");
  742. if (xcodeCopyToProductInstallPathAfterBuild)
  743. {
  744. s.add ("DEPLOYMENT_LOCATION = YES");
  745. s.add ("DSTROOT = /");
  746. }
  747. }
  748. if (xcodeIsBundle)
  749. {
  750. s.add ("LIBRARY_STYLE = Bundle");
  751. s.add ("WRAPPER_EXTENSION = " + xcodeBundleExtension.substring (1));
  752. s.add ("GENERATE_PKGINFO_FILE = YES");
  753. }
  754. if (xcodeOtherRezFlags.isNotEmpty())
  755. s.add ("OTHER_REZFLAGS = \"" + xcodeOtherRezFlags + "\"");
  756. String configurationBuildDir = "$(PROJECT_DIR)/build/$(CONFIGURATION)";
  757. if (config.getTargetBinaryRelativePathString().isNotEmpty())
  758. {
  759. // a target's position can either be defined via installPath + xcodeCopyToProductInstallPathAfterBuild
  760. // (= for audio plug-ins) or using a custom binary path (for everything else), but not both (= conflict!)
  761. jassert (! xcodeCopyToProductInstallPathAfterBuild);
  762. RelativePath binaryPath (config.getTargetBinaryRelativePathString(), RelativePath::projectFolder);
  763. configurationBuildDir = sanitisePath (binaryPath.rebased (owner.projectFolder, owner.getTargetFolder(), RelativePath::buildTargetFolder)
  764. .toUnixStyle());
  765. }
  766. s.add ("CONFIGURATION_BUILD_DIR = " + addQuotesIfRequired (configurationBuildDir));
  767. String gccVersion ("com.apple.compilers.llvm.clang.1_0");
  768. if (owner.iOS)
  769. {
  770. s.add ("ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon");
  771. s.add ("ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage");
  772. }
  773. else
  774. {
  775. const String sdk (config.osxSDKVersion.get());
  776. const String sdkCompat (config.osxDeploymentTarget.get());
  777. // if the user doesn't set it, then use the last known version that works well with JUCE
  778. String deploymentTarget = "10.11";
  779. for (int ver = oldestSDKVersion; ver <= currentSDKVersion; ++ver)
  780. {
  781. if (sdk == getSDKName (ver)) s.add ("SDKROOT = macosx10." + String (ver));
  782. if (sdkCompat == getSDKName (ver)) deploymentTarget = "10." + String (ver);
  783. }
  784. s.add ("MACOSX_DEPLOYMENT_TARGET = " + deploymentTarget);
  785. s.add ("MACOSX_DEPLOYMENT_TARGET_ppc = 10.4");
  786. s.add ("SDKROOT_ppc = macosx10.5");
  787. if (xcodeExcludedFiles64Bit.isNotEmpty())
  788. {
  789. s.add ("EXCLUDED_SOURCE_FILE_NAMES = \"$(EXCLUDED_SOURCE_FILE_NAMES_$(CURRENT_ARCH))\"");
  790. s.add ("EXCLUDED_SOURCE_FILE_NAMES_x86_64 = " + xcodeExcludedFiles64Bit);
  791. }
  792. }
  793. s.add ("GCC_VERSION = " + gccVersion);
  794. s.add ("CLANG_CXX_LANGUAGE_STANDARD = \"c++0x\"");
  795. s.add ("CLANG_LINK_OBJC_RUNTIME = NO");
  796. if (! config.codeSignIdentity.isUsingDefault())
  797. s.add ("CODE_SIGN_IDENTITY = " + config.codeSignIdentity.get().quoted());
  798. if (config.cppLanguageStandard.get().isNotEmpty())
  799. s.add ("CLANG_CXX_LANGUAGE_STANDARD = " + config.cppLanguageStandard.get().quoted());
  800. if (config.cppStandardLibrary.get().isNotEmpty())
  801. s.add ("CLANG_CXX_LIBRARY = " + config.cppStandardLibrary.get().quoted());
  802. s.add ("COMBINE_HIDPI_IMAGES = YES");
  803. {
  804. StringArray linkerFlags, librarySearchPaths;
  805. getLinkerSettings (config, linkerFlags, librarySearchPaths);
  806. if (linkerFlags.size() > 0)
  807. s.add ("OTHER_LDFLAGS = \"" + linkerFlags.joinIntoString (" ") + "\"");
  808. librarySearchPaths.addArray (config.getLibrarySearchPaths());
  809. librarySearchPaths = getCleanedStringArray (librarySearchPaths);
  810. if (librarySearchPaths.size() > 0)
  811. {
  812. String libPaths ("LIBRARY_SEARCH_PATHS = (\"$(inherited)\"");
  813. for (int i = 0; i < librarySearchPaths.size(); ++i)
  814. libPaths += ", \"\\\"" + librarySearchPaths[i] + "\\\"\"";
  815. s.add (libPaths + ")");
  816. }
  817. }
  818. StringPairArray defines;
  819. if (config.isDebug())
  820. {
  821. defines.set ("_DEBUG", "1");
  822. defines.set ("DEBUG", "1");
  823. s.add ("COPY_PHASE_STRIP = NO");
  824. s.add ("GCC_DYNAMIC_NO_PIC = NO");
  825. }
  826. else
  827. {
  828. defines.set ("_NDEBUG", "1");
  829. defines.set ("NDEBUG", "1");
  830. s.add ("GCC_GENERATE_DEBUGGING_SYMBOLS = NO");
  831. s.add ("GCC_SYMBOLS_PRIVATE_EXTERN = YES");
  832. s.add ("DEAD_CODE_STRIPPING = YES");
  833. }
  834. if (type != Target::SharedCodeTarget && type != Target::StaticLibrary && type != Target::DynamicLibrary
  835. && config.stripLocalSymbolsEnabled.get())
  836. {
  837. s.add ("STRIPFLAGS = \"-x\"");
  838. s.add ("DEPLOYMENT_POSTPROCESSING = YES");
  839. s.add ("SEPARATE_STRIP = YES");
  840. }
  841. if (type == Target::SharedCodeTarget)
  842. defines.set ("JUCE_SHARED_CODE", "1");
  843. if (owner.project.getProjectType().isAudioPlugin() && type == Target::AudioUnitv3PlugIn && owner.isOSX())
  844. s.add (String ("CODE_SIGN_ENTITLEMENTS = \"") + owner.getEntitlementsFileName() + String ("\""));
  845. if (owner.project.getProjectType().isAudioPlugin())
  846. {
  847. defines.set ("JucePlugin_Build_VST", (shouldBuildVST() ? "1" : "0"));
  848. defines.set ("JucePlugin_Build_VST3", (shouldBuildVST3() ? "1" : "0"));
  849. defines.set ("JucePlugin_Build_AU", (shouldBuildAU() ? "1" : "0"));
  850. defines.set ("JucePlugin_Build_AUv3", (shouldBuildAUv3() ? "1" : "0"));
  851. defines.set ("JucePlugin_Build_RTAS", (shouldBuildRTAS() ? "1" : "0"));
  852. defines.set ("JucePlugin_Build_AAX", (shouldBuildAAX() ? "1" : "0"));
  853. defines.set ("JucePlugin_Build_Standalone", (shouldBuildStandalone() ? "1" : "0"));
  854. }
  855. defines = mergePreprocessorDefs (defines, owner.getAllPreprocessorDefs (config));
  856. StringArray defsList;
  857. for (int i = 0; i < defines.size(); ++i)
  858. {
  859. String def (defines.getAllKeys()[i]);
  860. const String value (defines.getAllValues()[i]);
  861. if (value.isNotEmpty())
  862. def << "=" << value.replace ("\"", "\\\"");
  863. defsList.add ("\"" + def + "\"");
  864. }
  865. s.add ("GCC_PREPROCESSOR_DEFINITIONS = " + indentParenthesisedList (defsList));
  866. s.addTokens (config.customXcodeFlags.get(), ",", "\"'");
  867. return getCleanedStringArray (s);
  868. }
  869. String getInstallPathForConfiguration (const XcodeBuildConfiguration& config) const
  870. {
  871. switch (type)
  872. {
  873. case GUIApp: return "$(HOME)/Applications";
  874. case ConsoleApp: return "/usr/bin";
  875. case VSTPlugIn: return config.vstBinaryLocation.get();
  876. case VST3PlugIn: return config.vst3BinaryLocation.get();
  877. case AudioUnitPlugIn: return config.auBinaryLocation.get();
  878. case RTASPlugIn: return config.rtasBinaryLocation.get();
  879. case AAXPlugIn: return config.aaxBinaryLocation.get();
  880. case SharedCodeTarget: return owner.isiOS() ? "@executable_path/Frameworks" : "@executable_path/../Frameworks";
  881. default: return String();
  882. }
  883. }
  884. //==============================================================================
  885. void getLinkerSettings (const BuildConfiguration& config, StringArray& flags, StringArray& librarySearchPaths) const
  886. {
  887. if (xcodeIsBundle)
  888. flags.add (owner.isiOS() ? "-bitcode_bundle" : "-bundle");
  889. const Array<RelativePath>& extraLibs = config.isDebug() ? xcodeExtraLibrariesDebug
  890. : xcodeExtraLibrariesRelease;
  891. for (auto& lib : extraLibs)
  892. {
  893. flags.add (getLinkerFlagForLib (lib.getFileNameWithoutExtension()));
  894. librarySearchPaths.add (owner.getSearchPathForStaticLibrary (lib));
  895. }
  896. if (owner.project.getProjectType().isAudioPlugin() && type != Target::SharedCodeTarget)
  897. {
  898. if (owner.getTargetOfType (Target::SharedCodeTarget) != nullptr)
  899. {
  900. String productName (getLibbedFilename (owner.replacePreprocessorTokens (config, config.getTargetBinaryNameString())));
  901. RelativePath sharedCodelib (productName, RelativePath::buildTargetFolder);
  902. flags.add (getLinkerFlagForLib (sharedCodelib.getFileNameWithoutExtension()));
  903. }
  904. }
  905. flags.add (owner.replacePreprocessorTokens (config, owner.getExtraLinkerFlagsString()));
  906. flags.add (owner.getExternalLibraryFlags (config));
  907. StringArray libs (owner.xcodeLibs);
  908. libs.addArray (xcodeLibs);
  909. for (int i = 0; i < libs.size(); ++i)
  910. flags.add (getLinkerFlagForLib (libs[i]));
  911. flags = getCleanedStringArray (flags);
  912. }
  913. //========================================================================== c
  914. void writeInfoPlistFile() const
  915. {
  916. if (! xcodeCreatePList)
  917. return;
  918. ScopedPointer<XmlElement> plist (XmlDocument::parse (owner.getPListToMergeString()));
  919. if (plist == nullptr || ! plist->hasTagName ("plist"))
  920. plist = new XmlElement ("plist");
  921. XmlElement* dict = plist->getChildByName ("dict");
  922. if (dict == nullptr)
  923. dict = plist->createNewChildElement ("dict");
  924. if (owner.iOS)
  925. {
  926. addPlistDictionaryKeyBool (dict, "LSRequiresIPhoneOS", true);
  927. if (type != AudioUnitv3PlugIn)
  928. addPlistDictionaryKeyBool (dict, "UIViewControllerBasedStatusBarAppearance", false);
  929. }
  930. addPlistDictionaryKey (dict, "CFBundleExecutable", "${EXECUTABLE_NAME}");
  931. if (! owner.iOS) // (NB: on iOS this causes error ITMS-90032 during publishing)
  932. addPlistDictionaryKey (dict, "CFBundleIconFile", owner.iconFile.exists() ? owner.iconFile.getFileName() : String());
  933. addPlistDictionaryKey (dict, "CFBundleIdentifier", "$(PRODUCT_BUNDLE_IDENTIFIER)");
  934. addPlistDictionaryKey (dict, "CFBundleName", owner.projectName);
  935. // needed by NSExtension on iOS
  936. addPlistDictionaryKey (dict, "CFBundleDisplayName", owner.projectName);
  937. addPlistDictionaryKey (dict, "CFBundlePackageType", xcodePackageType);
  938. addPlistDictionaryKey (dict, "CFBundleSignature", xcodeBundleSignature);
  939. addPlistDictionaryKey (dict, "CFBundleShortVersionString", owner.project.getVersionString());
  940. addPlistDictionaryKey (dict, "CFBundleVersion", owner.project.getVersionString());
  941. addPlistDictionaryKey (dict, "NSHumanReadableCopyright", owner.project.getCompanyName().toString());
  942. addPlistDictionaryKeyBool (dict, "NSHighResolutionCapable", true);
  943. StringArray documentExtensions;
  944. documentExtensions.addTokens (replacePreprocessorDefs (owner.getAllPreprocessorDefs(), owner.settings ["documentExtensions"]),
  945. ",", StringRef());
  946. documentExtensions.trim();
  947. documentExtensions.removeEmptyStrings (true);
  948. if (documentExtensions.size() > 0 && type != AudioUnitv3PlugIn)
  949. {
  950. dict->createNewChildElement ("key")->addTextElement ("CFBundleDocumentTypes");
  951. XmlElement* dict2 = dict->createNewChildElement ("array")->createNewChildElement ("dict");
  952. XmlElement* arrayTag = nullptr;
  953. for (int i = 0; i < documentExtensions.size(); ++i)
  954. {
  955. String ex (documentExtensions[i]);
  956. if (ex.startsWithChar ('.'))
  957. ex = ex.substring (1);
  958. if (arrayTag == nullptr)
  959. {
  960. dict2->createNewChildElement ("key")->addTextElement ("CFBundleTypeExtensions");
  961. arrayTag = dict2->createNewChildElement ("array");
  962. addPlistDictionaryKey (dict2, "CFBundleTypeName", ex);
  963. addPlistDictionaryKey (dict2, "CFBundleTypeRole", "Editor");
  964. addPlistDictionaryKey (dict2, "CFBundleTypeIconFile", "Icon");
  965. addPlistDictionaryKey (dict2, "NSPersistentStoreTypeKey", "XML");
  966. }
  967. arrayTag->createNewChildElement ("string")->addTextElement (ex);
  968. }
  969. }
  970. if (owner.settings ["UIFileSharingEnabled"] && type != AudioUnitv3PlugIn)
  971. addPlistDictionaryKeyBool (dict, "UIFileSharingEnabled", true);
  972. if (owner.settings ["UIStatusBarHidden"] && type != AudioUnitv3PlugIn)
  973. addPlistDictionaryKeyBool (dict, "UIStatusBarHidden", true);
  974. if (owner.iOS && type != AudioUnitv3PlugIn)
  975. {
  976. // Forcing full screen disables the split screen feature and prevents error ITMS-90475
  977. addPlistDictionaryKeyBool (dict, "UIRequiresFullScreen", true);
  978. addPlistDictionaryKeyBool (dict, "UIStatusBarHidden", true);
  979. addIosScreenOrientations (dict);
  980. addIosBackgroundModes (dict);
  981. }
  982. for (int i = 0; i < xcodeExtraPListEntries.size(); ++i)
  983. dict->addChildElement (new XmlElement (xcodeExtraPListEntries.getReference(i)));
  984. MemoryOutputStream mo;
  985. plist->writeToStream (mo, "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">");
  986. overwriteFileIfDifferentOrThrow (infoPlistFile, mo);
  987. }
  988. //==============================================================================
  989. void addIosScreenOrientations (XmlElement* dict) const
  990. {
  991. String screenOrientation = owner.getScreenOrientationString();
  992. StringArray iOSOrientations;
  993. if (screenOrientation.contains ("portrait")) { iOSOrientations.add ("UIInterfaceOrientationPortrait"); }
  994. if (screenOrientation.contains ("landscape")) { iOSOrientations.add ("UIInterfaceOrientationLandscapeLeft"); iOSOrientations.add ("UIInterfaceOrientationLandscapeRight"); }
  995. addArrayToPlist (dict, "UISupportedInterfaceOrientations", iOSOrientations);
  996. }
  997. //==============================================================================
  998. void addIosBackgroundModes (XmlElement* dict) const
  999. {
  1000. StringArray iosBackgroundModes;
  1001. if (owner.isBackgroundAudioEnabled()) iosBackgroundModes.add ("audio");
  1002. if (owner.isBackgroundBleEnabled()) iosBackgroundModes.add ("bluetooth-central");
  1003. addArrayToPlist (dict, "UIBackgroundModes", iosBackgroundModes);
  1004. }
  1005. //==============================================================================
  1006. static void addArrayToPlist (XmlElement* dict, String arrayKey, const StringArray& arrayElements)
  1007. {
  1008. dict->createNewChildElement ("key")->addTextElement (arrayKey);
  1009. XmlElement* plistStringArray = dict->createNewChildElement ("array");
  1010. for (int i = 0; i < arrayElements.size(); ++i)
  1011. plistStringArray->createNewChildElement ("string")->addTextElement (arrayElements[i]);
  1012. }
  1013. //==============================================================================
  1014. void addShellScriptBuildPhase (const String& phaseName, const String& script)
  1015. {
  1016. if (script.trim().isNotEmpty())
  1017. {
  1018. ValueTree& v = addBuildPhase ("PBXShellScriptBuildPhase", StringArray());
  1019. v.setProperty (Ids::name, phaseName, nullptr);
  1020. v.setProperty ("shellPath", "/bin/sh", nullptr);
  1021. v.setProperty ("shellScript", script.replace ("\\", "\\\\")
  1022. .replace ("\"", "\\\"")
  1023. .replace ("\r\n", "\\n")
  1024. .replace ("\n", "\\n"), nullptr);
  1025. }
  1026. }
  1027. void addCopyFilesPhase (const String& phaseName, const StringArray& files, XcodeCopyFilesDestinationIDs dst)
  1028. {
  1029. ValueTree& v = addBuildPhase ("PBXCopyFilesBuildPhase", files, phaseName);
  1030. v.setProperty ("dstPath", "", nullptr);
  1031. v.setProperty ("dstSubfolderSpec", (int) dst, nullptr);
  1032. }
  1033. private:
  1034. //==============================================================================
  1035. void addExtraAudioUnitTargetSettings()
  1036. {
  1037. xcodeOtherRezFlags = "-d ppc_$ppc -d i386_$i386 -d ppc64_$ppc64 -d x86_64_$x86_64"
  1038. " -I /System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework/Versions/A/Headers"
  1039. " -I \\\"$(DEVELOPER_DIR)/Extras/CoreAudio/AudioUnits/AUPublic/AUBase\\\"";
  1040. xcodeFrameworks.addTokens ("AudioUnit CoreAudioKit", false);
  1041. XmlElement plistKey ("key");
  1042. plistKey.addTextElement ("AudioComponents");
  1043. XmlElement plistEntry ("array");
  1044. XmlElement* dict = plistEntry.createNewChildElement ("dict");
  1045. addPlistDictionaryKey (dict, "name", owner.project.getPluginManufacturer().toString()
  1046. + ": " + owner.project.getPluginName().toString());
  1047. addPlistDictionaryKey (dict, "description", owner.project.getPluginDesc().toString());
  1048. addPlistDictionaryKey (dict, "factoryFunction", owner.project.getPluginAUExportPrefix().toString() + "Factory");
  1049. addPlistDictionaryKey (dict, "manufacturer", owner.project.getPluginManufacturerCode().toString().trim().substring (0, 4));
  1050. addPlistDictionaryKey (dict, "type", owner.project.getAUMainTypeCode());
  1051. addPlistDictionaryKey (dict, "subtype", owner.project.getPluginCode().toString().trim().substring (0, 4));
  1052. addPlistDictionaryKeyInt (dict, "version", owner.project.getVersionAsHexInteger());
  1053. xcodeExtraPListEntries.add (plistKey);
  1054. xcodeExtraPListEntries.add (plistEntry);
  1055. }
  1056. void addExtraAudioUnitv3PlugInTargetSettings()
  1057. {
  1058. if (owner.isiOS())
  1059. xcodeFrameworks.addTokens ("CoreAudioKit AVFoundation", false);
  1060. else
  1061. xcodeFrameworks.addTokens ("AudioUnit CoreAudioKit AVFoundation", false);
  1062. XmlElement plistKey ("key");
  1063. plistKey.addTextElement ("NSExtension");
  1064. XmlElement plistEntry ("dict");
  1065. addPlistDictionaryKey (&plistEntry, "NSExtensionPrincipalClass", owner.project.getPluginAUExportPrefix().toString() + "FactoryAUv3");
  1066. addPlistDictionaryKey (&plistEntry, "NSExtensionPointIdentifier", "com.apple.AudioUnit-UI");
  1067. plistEntry.createNewChildElement ("key")->addTextElement ("NSExtensionAttributes");
  1068. XmlElement* dict = plistEntry.createNewChildElement ("dict");
  1069. dict->createNewChildElement ("key")->addTextElement ("AudioComponents");
  1070. XmlElement* componentArray = dict->createNewChildElement ("array");
  1071. XmlElement* componentDict = componentArray->createNewChildElement ("dict");
  1072. addPlistDictionaryKey (componentDict, "name", owner.project.getPluginManufacturer().toString()
  1073. + ": " + owner.project.getPluginName().toString());
  1074. addPlistDictionaryKey (componentDict, "description", owner.project.getPluginDesc().toString());
  1075. addPlistDictionaryKey (componentDict, "factoryFunction",owner.project. getPluginAUExportPrefix().toString() + "FactoryAUv3");
  1076. addPlistDictionaryKey (componentDict, "manufacturer", owner.project.getPluginManufacturerCode().toString().trim().substring (0, 4));
  1077. addPlistDictionaryKey (componentDict, "type", owner.project.getAUMainTypeCode());
  1078. addPlistDictionaryKey (componentDict, "subtype", owner.project.getPluginCode().toString().trim().substring (0, 4));
  1079. addPlistDictionaryKeyInt (componentDict, "version", owner.project.getVersionAsHexInteger());
  1080. addPlistDictionaryKeyBool (componentDict, "sandboxSafe", true);
  1081. componentDict->createNewChildElement ("key")->addTextElement ("tags");
  1082. XmlElement* tagsArray = componentDict->createNewChildElement ("array");
  1083. tagsArray->createNewChildElement ("string")->addTextElement (static_cast<bool> (owner.project.getPluginIsSynth().getValue()) ? "Synth" : "Effects");
  1084. xcodeExtraPListEntries.add (plistKey);
  1085. xcodeExtraPListEntries.add (plistEntry);
  1086. }
  1087. void addExtraAAXTargetSettings()
  1088. {
  1089. const RelativePath aaxLibsFolder = RelativePath (owner.getAAXPathValue().toString(), RelativePath::projectFolder).getChildFile ("Libs");
  1090. xcodeExtraLibrariesDebug.add (aaxLibsFolder.getChildFile ("Debug/libAAXLibrary.a"));
  1091. xcodeExtraLibrariesRelease.add (aaxLibsFolder.getChildFile ("Release/libAAXLibrary.a"));
  1092. }
  1093. void addExtraRTASTargetSettings()
  1094. {
  1095. RelativePath rtasFolder (owner.getRTASPathValue().toString(), RelativePath::projectFolder);
  1096. xcodeExtraLibrariesDebug.add (rtasFolder.getChildFile ("MacBag/Libs/Debug/libPluginLibrary.a"));
  1097. xcodeExtraLibrariesRelease.add (rtasFolder.getChildFile ("MacBag/Libs/Release/libPluginLibrary.a"));
  1098. }
  1099. //==============================================================================
  1100. const XCodeProjectExporter& owner;
  1101. Target& operator= (const Target&) JUCE_DELETED_FUNCTION;
  1102. };
  1103. mutable StringArray xcodeFrameworks;
  1104. StringArray xcodeLibs;
  1105. private:
  1106. //==============================================================================
  1107. bool xcodeCanUseDwarf;
  1108. OwnedArray<Target> targets;
  1109. mutable OwnedArray<ValueTree> pbxBuildFiles, pbxFileReferences, pbxGroups, misc, projectConfigs, targetConfigs;
  1110. mutable StringArray resourceIDs, sourceIDs, targetIDs;
  1111. mutable StringArray frameworkFileIDs, rezFileIDs, resourceFileRefs;
  1112. mutable File menuNibFile, iconFile;
  1113. mutable StringArray buildProducts;
  1114. const bool iOS;
  1115. static String sanitisePath (const String& path)
  1116. {
  1117. if (path.startsWithChar ('~'))
  1118. return "$(HOME)" + path.substring (1);
  1119. return path;
  1120. }
  1121. static String addQuotesIfRequired (const String& s)
  1122. {
  1123. return s.containsAnyOf (" $") ? s.quoted() : s;
  1124. }
  1125. File getProjectBundle() const { return getTargetFolder().getChildFile (project.getProjectFilenameRoot()).withFileExtension (".xcodeproj"); }
  1126. //==============================================================================
  1127. void createObjects() const
  1128. {
  1129. prepareTargets();
  1130. addFrameworks();
  1131. addCustomResourceFolders();
  1132. addPlistFileReferences();
  1133. if (iOS && ! projectType.isStaticLibrary())
  1134. addXcassets();
  1135. else
  1136. addNibFiles();
  1137. addIcons();
  1138. addBuildConfigurations();
  1139. addProjectConfigList (projectConfigs, createID ("__projList"));
  1140. {
  1141. StringArray topLevelGroupIDs;
  1142. addFilesAndGroupsToProject (topLevelGroupIDs);
  1143. addBuildPhases();
  1144. addExtraGroupsToProject (topLevelGroupIDs);
  1145. addGroup (createID ("__mainsourcegroup"), "Source", topLevelGroupIDs);
  1146. }
  1147. addProjectObject();
  1148. removeMismatchedXcuserdata();
  1149. }
  1150. void prepareTargets() const
  1151. {
  1152. for (int targetIdx = 0; targetIdx < targets.size(); ++targetIdx)
  1153. {
  1154. Target& target = *targets[targetIdx];
  1155. if (target.type == Target::AggregateTarget)
  1156. continue;
  1157. target.addMainBuildProduct();
  1158. String targetName = target.getName();
  1159. String fileID (createID (targetName + String ("__targetbuildref")));
  1160. String fileRefID (createID (String ("__productFileID") + targetName));
  1161. ValueTree* v = new ValueTree (fileID);
  1162. v->setProperty ("isa", "PBXBuildFile", nullptr);
  1163. v->setProperty ("fileRef", fileRefID, nullptr);
  1164. target.mainBuildProductID = fileID;
  1165. pbxBuildFiles.add (v);
  1166. target.addDependency();
  1167. }
  1168. }
  1169. void addPlistFileReferences() const
  1170. {
  1171. for (int targetIdx = 0; targetIdx < targets.size(); ++targetIdx)
  1172. {
  1173. Target& target = *targets[targetIdx];
  1174. if (target.type == Target::AggregateTarget)
  1175. continue;
  1176. if (target.xcodeCreatePList)
  1177. {
  1178. RelativePath plistPath (target.infoPlistFile, getTargetFolder(), RelativePath::buildTargetFolder);
  1179. addFileReference (plistPath.toUnixStyle());
  1180. resourceFileRefs.add (createFileRefID (plistPath));
  1181. }
  1182. }
  1183. }
  1184. void addNibFiles() const
  1185. {
  1186. MemoryOutputStream nib;
  1187. nib.write (BinaryData::RecentFilesMenuTemplate_nib, BinaryData::RecentFilesMenuTemplate_nibSize);
  1188. overwriteFileIfDifferentOrThrow (menuNibFile, nib);
  1189. RelativePath menuNibPath (menuNibFile, getTargetFolder(), RelativePath::buildTargetFolder);
  1190. addFileReference (menuNibPath.toUnixStyle());
  1191. resourceIDs.add (addBuildFile (menuNibPath, false, false));
  1192. resourceFileRefs.add (createFileRefID (menuNibPath));
  1193. }
  1194. void addIcons() const
  1195. {
  1196. if (iconFile.exists())
  1197. {
  1198. RelativePath iconPath (iconFile, getTargetFolder(), RelativePath::buildTargetFolder);
  1199. addFileReference (iconPath.toUnixStyle());
  1200. resourceIDs.add (addBuildFile (iconPath, false, false));
  1201. resourceFileRefs.add (createFileRefID (iconPath));
  1202. }
  1203. }
  1204. void addBuildConfigurations() const
  1205. {
  1206. // add build configurations
  1207. for (ConstConfigIterator config (*this); config.next();)
  1208. {
  1209. const XcodeBuildConfiguration& xcodeConfig = dynamic_cast<const XcodeBuildConfiguration&> (*config);
  1210. addProjectConfig (config->getName(), getProjectSettings (xcodeConfig));
  1211. }
  1212. }
  1213. void addFilesAndGroupsToProject (StringArray& topLevelGroupIDs) const
  1214. {
  1215. if (! isiOS() && project.getProjectType().isAudioPlugin())
  1216. topLevelGroupIDs.add (addEntitlementsFile());
  1217. for (int i = 0; i < getAllGroups().size(); ++i)
  1218. {
  1219. const Project::Item& group = getAllGroups().getReference(i);
  1220. if (group.getNumChildren() > 0)
  1221. topLevelGroupIDs.add (addProjectItem (group));
  1222. }
  1223. }
  1224. void addExtraGroupsToProject (StringArray& topLevelGroupIDs) const
  1225. {
  1226. { // Add 'resources' group
  1227. String resourcesGroupID (createID ("__resources"));
  1228. addGroup (resourcesGroupID, "Resources", resourceFileRefs);
  1229. topLevelGroupIDs.add (resourcesGroupID);
  1230. }
  1231. { // Add 'frameworks' group
  1232. String frameworksGroupID (createID ("__frameworks"));
  1233. addGroup (frameworksGroupID, "Frameworks", frameworkFileIDs);
  1234. topLevelGroupIDs.add (frameworksGroupID);
  1235. }
  1236. { // Add 'products' group
  1237. String productsGroupID (createID ("__products"));
  1238. addGroup (productsGroupID, "Products", buildProducts);
  1239. topLevelGroupIDs.add (productsGroupID);
  1240. }
  1241. }
  1242. void addBuildPhases() const
  1243. {
  1244. // add build phases
  1245. for (int i = 0; i < targets.size(); ++i)
  1246. {
  1247. Target& target = *targets[i];
  1248. if (target.type != Target::AggregateTarget)
  1249. buildProducts.add (createID (String ("__productFileID") + String (target.getName())));
  1250. for (ConstConfigIterator config (*this); config.next();)
  1251. {
  1252. const XcodeBuildConfiguration& xcodeConfig = dynamic_cast<const XcodeBuildConfiguration&> (*config);
  1253. target.addTargetConfig (config->getName(), target.getTargetSettings (xcodeConfig));
  1254. }
  1255. addConfigList (target, targetConfigs, createID (String ("__configList") + target.getName()));
  1256. target.addShellScriptBuildPhase ("Pre-build script", getPreBuildScript());
  1257. if (target.type != Target::AggregateTarget)
  1258. {
  1259. // TODO: ideally resources wouldn't be copied into the AUv3 bundle as well.
  1260. // However, fixing this requires supporting App groups -> TODO: add app groups
  1261. if (! projectType.isStaticLibrary() && target.type != Target::SharedCodeTarget)
  1262. target.addBuildPhase ("PBXResourcesBuildPhase", resourceIDs);
  1263. StringArray rezFiles (rezFileIDs);
  1264. rezFiles.addArray (target.rezFileIDs);
  1265. if (rezFiles.size() > 0)
  1266. target.addBuildPhase ("PBXRezBuildPhase", rezFiles);
  1267. StringArray sourceFiles (target.sourceIDs);
  1268. if (target.type == Target::SharedCodeTarget
  1269. || (! project.getProjectType().isAudioPlugin()))
  1270. sourceFiles.addArray (sourceIDs);
  1271. target.addBuildPhase ("PBXSourcesBuildPhase", sourceFiles);
  1272. if (! projectType.isStaticLibrary() && target.type != Target::SharedCodeTarget)
  1273. target.addBuildPhase ("PBXFrameworksBuildPhase", target.frameworkIDs);
  1274. }
  1275. target.addShellScriptBuildPhase ("Post-build script", getPostBuildScript());
  1276. if (project.getProjectType().isAudioPlugin() && project.shouldBuildAUv3().getValue()
  1277. && project.shouldBuildStandalone().getValue() && target.type == Target::StandalonePlugIn)
  1278. embedAppExtension();
  1279. addTargetObject (target);
  1280. }
  1281. }
  1282. void embedAppExtension() const
  1283. {
  1284. if (Target* standaloneTarget = getTargetOfType (Target::StandalonePlugIn))
  1285. {
  1286. if (Target* auv3Target = getTargetOfType (Target::AudioUnitv3PlugIn))
  1287. {
  1288. StringArray files;
  1289. files.add (auv3Target->mainBuildProductID);
  1290. standaloneTarget->addCopyFilesPhase ("Embed App Extensions", files, kPluginsFolder);
  1291. }
  1292. }
  1293. }
  1294. static Image fixMacIconImageSize (Drawable& image)
  1295. {
  1296. const int validSizes[] = { 16, 32, 48, 128, 256, 512, 1024 };
  1297. const int w = image.getWidth();
  1298. const int h = image.getHeight();
  1299. int bestSize = 16;
  1300. for (int i = 0; i < numElementsInArray (validSizes); ++i)
  1301. {
  1302. if (w == h && w == validSizes[i])
  1303. {
  1304. bestSize = w;
  1305. break;
  1306. }
  1307. if (jmax (w, h) > validSizes[i])
  1308. bestSize = validSizes[i];
  1309. }
  1310. return rescaleImageForIcon (image, bestSize);
  1311. }
  1312. //==============================================================================
  1313. Target* getTargetOfType (Target::Type type) const
  1314. {
  1315. for (auto& target : targets)
  1316. if (target->type == type)
  1317. return target;
  1318. return nullptr;
  1319. }
  1320. void addTargetObject (Target& target) const
  1321. {
  1322. String targetName = target.getName();
  1323. String targetID = target.getID();
  1324. ValueTree* const v = new ValueTree (targetID);
  1325. v->setProperty ("isa", target.type == Target::AggregateTarget ? "PBXAggregateTarget" : "PBXNativeTarget", nullptr);
  1326. v->setProperty ("buildConfigurationList", createID (String ("__configList") + targetName), nullptr);
  1327. v->setProperty ("buildPhases", indentParenthesisedList (target.buildPhaseIDs), nullptr);
  1328. v->setProperty ("buildRules", "( )", nullptr);
  1329. v->setProperty ("dependencies", indentParenthesisedList (getTargetDependencies (target)), nullptr);
  1330. v->setProperty (Ids::name, target.getXCodeSchemeName(), nullptr);
  1331. v->setProperty ("productName", projectName, nullptr);
  1332. if (target.type != Target::AggregateTarget)
  1333. {
  1334. v->setProperty ("productReference", createID (String ("__productFileID") + targetName), nullptr);
  1335. jassert (target.xcodeProductType.isNotEmpty());
  1336. v->setProperty ("productType", target.xcodeProductType, nullptr);
  1337. }
  1338. targetIDs.add (targetID);
  1339. misc.add (v);
  1340. }
  1341. StringArray getTargetDependencies (const Target& target) const
  1342. {
  1343. StringArray dependencies;
  1344. if (project.getProjectType().isAudioPlugin())
  1345. {
  1346. if (target.type == Target::StandalonePlugIn) // depends on AUv3 and shared code
  1347. {
  1348. if (Target* auv3Target = getTargetOfType (Target::AudioUnitv3PlugIn))
  1349. dependencies.add (auv3Target->getDependencyID());
  1350. if (Target* sharedCodeTarget = getTargetOfType (Target::SharedCodeTarget))
  1351. dependencies.add (sharedCodeTarget->getDependencyID());
  1352. }
  1353. else if (target.type == Target::AggregateTarget) // depends on all other targets
  1354. {
  1355. for (int i = 1; i < targets.size(); ++i)
  1356. dependencies.add (targets[i]->getDependencyID());
  1357. }
  1358. else if (target.type != Target::SharedCodeTarget) // shared code doesn't depend on anything; all other targets depend only on the shared code
  1359. {
  1360. if (Target* sharedCodeTarget = getTargetOfType (Target::SharedCodeTarget))
  1361. dependencies.add (sharedCodeTarget->getDependencyID());
  1362. }
  1363. }
  1364. return dependencies;
  1365. }
  1366. static void writeOldIconFormat (MemoryOutputStream& out, const Image& image, const char* type, const char* maskType)
  1367. {
  1368. const int w = image.getWidth();
  1369. const int h = image.getHeight();
  1370. out.write (type, 4);
  1371. out.writeIntBigEndian (8 + 4 * w * h);
  1372. const Image::BitmapData bitmap (image, Image::BitmapData::readOnly);
  1373. for (int y = 0; y < h; ++y)
  1374. {
  1375. for (int x = 0; x < w; ++x)
  1376. {
  1377. const Colour pixel (bitmap.getPixelColour (x, y));
  1378. out.writeByte ((char) pixel.getAlpha());
  1379. out.writeByte ((char) pixel.getRed());
  1380. out.writeByte ((char) pixel.getGreen());
  1381. out.writeByte ((char) pixel.getBlue());
  1382. }
  1383. }
  1384. out.write (maskType, 4);
  1385. out.writeIntBigEndian (8 + w * h);
  1386. for (int y = 0; y < h; ++y)
  1387. {
  1388. for (int x = 0; x < w; ++x)
  1389. {
  1390. const Colour pixel (bitmap.getPixelColour (x, y));
  1391. out.writeByte ((char) pixel.getAlpha());
  1392. }
  1393. }
  1394. }
  1395. static void writeNewIconFormat (MemoryOutputStream& out, const Image& image, const char* type)
  1396. {
  1397. MemoryOutputStream pngData;
  1398. PNGImageFormat pngFormat;
  1399. pngFormat.writeImageToStream (image, pngData);
  1400. out.write (type, 4);
  1401. out.writeIntBigEndian (8 + (int) pngData.getDataSize());
  1402. out << pngData;
  1403. }
  1404. void writeIcnsFile (const OwnedArray<Drawable>& images, OutputStream& out) const
  1405. {
  1406. MemoryOutputStream data;
  1407. int smallest = 0x7fffffff;
  1408. Drawable* smallestImage = nullptr;
  1409. for (int i = 0; i < images.size(); ++i)
  1410. {
  1411. const Image image (fixMacIconImageSize (*images.getUnchecked(i)));
  1412. jassert (image.getWidth() == image.getHeight());
  1413. if (image.getWidth() < smallest)
  1414. {
  1415. smallest = image.getWidth();
  1416. smallestImage = images.getUnchecked(i);
  1417. }
  1418. switch (image.getWidth())
  1419. {
  1420. case 16: writeOldIconFormat (data, image, "is32", "s8mk"); break;
  1421. case 32: writeOldIconFormat (data, image, "il32", "l8mk"); break;
  1422. case 48: writeOldIconFormat (data, image, "ih32", "h8mk"); break;
  1423. case 128: writeOldIconFormat (data, image, "it32", "t8mk"); break;
  1424. case 256: writeNewIconFormat (data, image, "ic08"); break;
  1425. case 512: writeNewIconFormat (data, image, "ic09"); break;
  1426. case 1024: writeNewIconFormat (data, image, "ic10"); break;
  1427. default: break;
  1428. }
  1429. }
  1430. jassert (data.getDataSize() > 0); // no suitable sized images?
  1431. // If you only supply a 1024 image, the file doesn't work on 10.8, so we need
  1432. // to force a smaller one in there too..
  1433. if (smallest > 512 && smallestImage != nullptr)
  1434. writeNewIconFormat (data, rescaleImageForIcon (*smallestImage, 512), "ic09");
  1435. out.write ("icns", 4);
  1436. out.writeIntBigEndian ((int) data.getDataSize() + 8);
  1437. out << data;
  1438. }
  1439. void getIconImages (OwnedArray<Drawable>& images) const
  1440. {
  1441. ScopedPointer<Drawable> bigIcon (getBigIcon());
  1442. if (bigIcon != nullptr)
  1443. images.add (bigIcon.release());
  1444. ScopedPointer<Drawable> smallIcon (getSmallIcon());
  1445. if (smallIcon != nullptr)
  1446. images.add (smallIcon.release());
  1447. }
  1448. void createiOSIconFiles (File appIconSet) const
  1449. {
  1450. const Array<AppIconType> types (getiOSAppIconTypes());
  1451. OwnedArray<Drawable> images;
  1452. getIconImages (images);
  1453. if (images.size() > 0)
  1454. {
  1455. for (int i = 0; i < types.size(); ++i)
  1456. {
  1457. const AppIconType type = types.getUnchecked(i);
  1458. const Image image (rescaleImageForIcon (*images.getFirst(), type.size));
  1459. MemoryOutputStream pngData;
  1460. PNGImageFormat pngFormat;
  1461. pngFormat.writeImageToStream (image, pngData);
  1462. overwriteFileIfDifferentOrThrow (appIconSet.getChildFile (type.filename), pngData);
  1463. }
  1464. }
  1465. }
  1466. void createIconFile() const
  1467. {
  1468. OwnedArray<Drawable> images;
  1469. getIconImages (images);
  1470. if (images.size() > 0)
  1471. {
  1472. MemoryOutputStream mo;
  1473. writeIcnsFile (images, mo);
  1474. iconFile = getTargetFolder().getChildFile ("Icon.icns");
  1475. overwriteFileIfDifferentOrThrow (iconFile, mo);
  1476. }
  1477. }
  1478. void writeInfoPlistFiles() const
  1479. {
  1480. for (auto& target : targets)
  1481. target->writeInfoPlistFile();
  1482. }
  1483. void deleteRsrcFiles() const
  1484. {
  1485. for (DirectoryIterator di (getTargetFolder().getChildFile ("build"), true, "*.rsrc", File::findFiles); di.next();)
  1486. di.getFile().deleteFile();
  1487. }
  1488. String getHeaderSearchPaths (const BuildConfiguration& config) const
  1489. {
  1490. StringArray paths (extraSearchPaths);
  1491. paths.addArray (config.getHeaderSearchPaths());
  1492. paths.add ("$(inherited)");
  1493. paths = getCleanedStringArray (paths);
  1494. for (int i = 0; i < paths.size(); ++i)
  1495. {
  1496. String& s = paths.getReference(i);
  1497. s = replacePreprocessorTokens (config, s);
  1498. if (s.containsChar (' '))
  1499. s = "\"\\\"" + s + "\\\"\""; // crazy double quotes required when there are spaces..
  1500. else
  1501. s = "\"" + s + "\"";
  1502. }
  1503. return "(" + paths.joinIntoString (", ") + ")";
  1504. }
  1505. static String getLinkerFlagForLib (String library)
  1506. {
  1507. if (library.substring (0, 3) == "lib")
  1508. library = library.substring (3);
  1509. return "-l" + library.replace (" ", "\\\\ ").upToLastOccurrenceOf (".", false, false);
  1510. }
  1511. String getSearchPathForStaticLibrary (const RelativePath& library) const
  1512. {
  1513. String searchPath (library.toUnixStyle().upToLastOccurrenceOf ("/", false, false));
  1514. if (! library.isAbsolute())
  1515. {
  1516. String srcRoot (rebaseFromProjectFolderToBuildTarget (RelativePath (".", RelativePath::projectFolder)).toUnixStyle());
  1517. if (srcRoot.endsWith ("/.")) srcRoot = srcRoot.dropLastCharacters (2);
  1518. if (! srcRoot.endsWithChar ('/')) srcRoot << '/';
  1519. searchPath = srcRoot + searchPath;
  1520. }
  1521. return sanitisePath (searchPath);
  1522. }
  1523. StringArray getProjectSettings (const XcodeBuildConfiguration& config) const
  1524. {
  1525. StringArray s;
  1526. s.add ("ALWAYS_SEARCH_USER_PATHS = NO");
  1527. s.add ("GCC_C_LANGUAGE_STANDARD = c99");
  1528. s.add ("GCC_WARN_ABOUT_RETURN_TYPE = YES");
  1529. s.add ("GCC_WARN_CHECK_SWITCH_STATEMENTS = YES");
  1530. s.add ("GCC_WARN_UNUSED_VARIABLE = YES");
  1531. s.add ("GCC_WARN_MISSING_PARENTHESES = YES");
  1532. s.add ("GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES");
  1533. s.add ("GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES");
  1534. s.add ("WARNING_CFLAGS = -Wreorder");
  1535. s.add ("GCC_MODEL_TUNING = G5");
  1536. if (projectType.isStaticLibrary())
  1537. {
  1538. s.add ("GCC_INLINES_ARE_PRIVATE_EXTERN = NO");
  1539. s.add ("GCC_SYMBOLS_PRIVATE_EXTERN = NO");
  1540. }
  1541. else
  1542. {
  1543. s.add ("GCC_INLINES_ARE_PRIVATE_EXTERN = YES");
  1544. }
  1545. if (config.isDebug())
  1546. {
  1547. s.add ("ENABLE_TESTABILITY = YES");
  1548. if (config.osxArchitecture.get() == osxArch_Default || config.osxArchitecture.get().isEmpty())
  1549. s.add ("ONLY_ACTIVE_ARCH = YES");
  1550. }
  1551. if (iOS)
  1552. {
  1553. s.add ("\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = " + config.codeSignIdentity.get().quoted());
  1554. s.add ("SDKROOT = iphoneos");
  1555. s.add ("TARGETED_DEVICE_FAMILY = \"1,2\"");
  1556. const String iosVersion (config.iosDeploymentTarget.get());
  1557. if (iosVersion.isNotEmpty() && iosVersion != osxVersionDefault)
  1558. s.add ("IPHONEOS_DEPLOYMENT_TARGET = " + iosVersion);
  1559. else
  1560. s.add ("IPHONEOS_DEPLOYMENT_TARGET = 9.3");
  1561. }
  1562. else
  1563. {
  1564. if (! config.codeSignIdentity.isUsingDefault() || getIosDevelopmentTeamIDString().isNotEmpty())
  1565. s.add ("\"CODE_SIGN_IDENTITY\" = " + config.codeSignIdentity.get().quoted());
  1566. }
  1567. s.add ("ZERO_LINK = NO");
  1568. if (xcodeCanUseDwarf)
  1569. s.add ("DEBUG_INFORMATION_FORMAT = \"dwarf\"");
  1570. s.add ("PRODUCT_NAME = \"" + replacePreprocessorTokens (config, config.getTargetBinaryNameString()) + "\"");
  1571. return s;
  1572. }
  1573. void addFrameworks() const
  1574. {
  1575. if (! projectType.isStaticLibrary())
  1576. {
  1577. if (iOS && isInAppPurchasesEnabled())
  1578. xcodeFrameworks.addIfNotAlreadyThere ("StoreKit");
  1579. xcodeFrameworks.addTokens (getExtraFrameworksString(), ",;", "\"'");
  1580. xcodeFrameworks.trim();
  1581. StringArray s (xcodeFrameworks);
  1582. for (auto& target : targets)
  1583. s.addArray (target->xcodeFrameworks);
  1584. if (project.getConfigFlag ("JUCE_QUICKTIME") == Project::configFlagDisabled)
  1585. s.removeString ("QuickTime");
  1586. s.trim();
  1587. s.removeDuplicates (true);
  1588. s.sort (true);
  1589. for (int i = 0; i < s.size(); ++i)
  1590. {
  1591. String frameworkID = addFramework (s[i]);
  1592. // find all the targets that are referring to this object
  1593. for (auto& target : targets)
  1594. if (xcodeFrameworks.contains (s[i]) || target->xcodeFrameworks.contains (s[i]))
  1595. target->frameworkIDs.add (frameworkID);
  1596. }
  1597. }
  1598. }
  1599. void addCustomResourceFolders() const
  1600. {
  1601. StringArray crf;
  1602. crf.addTokens (getCustomResourceFoldersString(), ":", "");
  1603. crf.trim();
  1604. for (int i = 0; i < crf.size(); ++i)
  1605. addCustomResourceFolder (crf[i]);
  1606. }
  1607. void addXcassets() const
  1608. {
  1609. String customXcassetsPath = getCustomXcassetsFolderString();
  1610. if (customXcassetsPath.isEmpty())
  1611. createXcassetsFolderFromIcons();
  1612. else
  1613. addCustomResourceFolder (customXcassetsPath, "folder.assetcatalog");
  1614. }
  1615. void addCustomResourceFolder (String folderPathRelativeToProjectFolder, const String fileType = "folder") const
  1616. {
  1617. String folderPath = RelativePath (folderPathRelativeToProjectFolder, RelativePath::projectFolder)
  1618. .rebased (projectFolder, getTargetFolder(), RelativePath::buildTargetFolder)
  1619. .toUnixStyle();
  1620. const String fileRefID (createFileRefID (folderPath));
  1621. addFileOrFolderReference (folderPath, "<group>", fileType);
  1622. resourceIDs.add (addBuildFile (folderPath, fileRefID, false, false));
  1623. resourceFileRefs.add (createFileRefID (folderPath));
  1624. }
  1625. //==============================================================================
  1626. void writeProjectFile (OutputStream& output) const
  1627. {
  1628. output << "// !$*UTF8*$!\n{\n"
  1629. "\tarchiveVersion = 1;\n"
  1630. "\tclasses = {\n\t};\n"
  1631. "\tobjectVersion = 46;\n"
  1632. "\tobjects = {\n\n";
  1633. Array<ValueTree*> objects;
  1634. objects.addArray (pbxBuildFiles);
  1635. objects.addArray (pbxFileReferences);
  1636. objects.addArray (pbxGroups);
  1637. objects.addArray (targetConfigs);
  1638. objects.addArray (projectConfigs);
  1639. objects.addArray (misc);
  1640. for (int i = 0; i < objects.size(); ++i)
  1641. {
  1642. ValueTree& o = *objects.getUnchecked(i);
  1643. output << "\t\t" << o.getType().toString() << " = {";
  1644. for (int j = 0; j < o.getNumProperties(); ++j)
  1645. {
  1646. const Identifier propertyName (o.getPropertyName(j));
  1647. String val (o.getProperty (propertyName).toString());
  1648. if (val.isEmpty() || (val.containsAnyOf (" \t;<>()=,&+-_@~\r\n\\#%^`*")
  1649. && ! (val.trimStart().startsWithChar ('(')
  1650. || val.trimStart().startsWithChar ('{'))))
  1651. val = "\"" + val + "\"";
  1652. output << propertyName.toString() << " = " << val << "; ";
  1653. }
  1654. output << "};\n";
  1655. }
  1656. output << "\t};\n\trootObject = " << createID ("__root") << ";\n}\n";
  1657. }
  1658. String addBuildFile (const String& path, const String& fileRefID, bool addToSourceBuildPhase, bool inhibitWarnings, Target* xcodeTarget = nullptr) const
  1659. {
  1660. String fileID (createID (path + "buildref"));
  1661. if (addToSourceBuildPhase)
  1662. {
  1663. if (xcodeTarget != nullptr) xcodeTarget->sourceIDs.add (fileID);
  1664. else sourceIDs.add (fileID);
  1665. }
  1666. ValueTree* v = new ValueTree (fileID);
  1667. v->setProperty ("isa", "PBXBuildFile", nullptr);
  1668. v->setProperty ("fileRef", fileRefID, nullptr);
  1669. if (inhibitWarnings)
  1670. v->setProperty ("settings", "{COMPILER_FLAGS = \"-w\"; }", nullptr);
  1671. pbxBuildFiles.add (v);
  1672. return fileID;
  1673. }
  1674. String addBuildFile (const RelativePath& path, bool addToSourceBuildPhase, bool inhibitWarnings, Target* xcodeTarget = nullptr) const
  1675. {
  1676. return addBuildFile (path.toUnixStyle(), createFileRefID (path), addToSourceBuildPhase, inhibitWarnings, xcodeTarget);
  1677. }
  1678. String addFileReference (String pathString) const
  1679. {
  1680. String sourceTree ("SOURCE_ROOT");
  1681. RelativePath path (pathString, RelativePath::unknown);
  1682. if (pathString.startsWith ("${"))
  1683. {
  1684. sourceTree = pathString.substring (2).upToFirstOccurrenceOf ("}", false, false);
  1685. pathString = pathString.fromFirstOccurrenceOf ("}/", false, false);
  1686. }
  1687. else if (path.isAbsolute())
  1688. {
  1689. sourceTree = "<absolute>";
  1690. }
  1691. String fileType = getFileType (path);
  1692. return addFileOrFolderReference (pathString, sourceTree, fileType);
  1693. }
  1694. String addFileOrFolderReference (String pathString, String sourceTree, String fileType) const
  1695. {
  1696. const String fileRefID (createFileRefID (pathString));
  1697. ScopedPointer<ValueTree> v (new ValueTree (fileRefID));
  1698. v->setProperty ("isa", "PBXFileReference", nullptr);
  1699. v->setProperty ("lastKnownFileType", fileType, nullptr);
  1700. v->setProperty (Ids::name, pathString.fromLastOccurrenceOf ("/", false, false), nullptr);
  1701. v->setProperty ("path", sanitisePath (pathString), nullptr);
  1702. v->setProperty ("sourceTree", sourceTree, nullptr);
  1703. const int existing = pbxFileReferences.indexOfSorted (*this, v);
  1704. if (existing >= 0)
  1705. {
  1706. // If this fails, there's either a string hash collision, or the same file is being added twice (incorrectly)
  1707. jassert (pbxFileReferences.getUnchecked (existing)->isEquivalentTo (*v));
  1708. }
  1709. else
  1710. {
  1711. pbxFileReferences.addSorted (*this, v.release());
  1712. }
  1713. return fileRefID;
  1714. }
  1715. public:
  1716. static int compareElements (const ValueTree* first, const ValueTree* second)
  1717. {
  1718. return first->getType().getCharPointer().compare (second->getType().getCharPointer());
  1719. }
  1720. private:
  1721. static String getFileType (const RelativePath& file)
  1722. {
  1723. if (file.hasFileExtension (cppFileExtensions)) return "sourcecode.cpp.cpp";
  1724. if (file.hasFileExtension (".mm")) return "sourcecode.cpp.objcpp";
  1725. if (file.hasFileExtension (".m")) return "sourcecode.c.objc";
  1726. if (file.hasFileExtension (".c")) return "sourcecode.c.c";
  1727. if (file.hasFileExtension (headerFileExtensions)) return "sourcecode.c.h";
  1728. if (file.hasFileExtension (asmFileExtensions)) return "sourcecode.c.asm";
  1729. if (file.hasFileExtension (".framework")) return "wrapper.framework";
  1730. if (file.hasFileExtension (".jpeg;.jpg")) return "image.jpeg";
  1731. if (file.hasFileExtension ("png;gif")) return "image" + file.getFileExtension();
  1732. if (file.hasFileExtension ("html;htm")) return "text.html";
  1733. if (file.hasFileExtension ("xml;zip;wav")) return "file" + file.getFileExtension();
  1734. if (file.hasFileExtension ("txt;rtf")) return "text" + file.getFileExtension();
  1735. if (file.hasFileExtension ("plist")) return "text.plist.xml";
  1736. if (file.hasFileExtension ("entitlements")) return "text.plist.xml";
  1737. if (file.hasFileExtension ("app")) return "wrapper.application";
  1738. if (file.hasFileExtension ("component;vst;plugin")) return "wrapper.cfbundle";
  1739. if (file.hasFileExtension ("xcodeproj")) return "wrapper.pb-project";
  1740. if (file.hasFileExtension ("a")) return "archive.ar";
  1741. if (file.hasFileExtension ("xcassets")) return "folder.assetcatalog";
  1742. return "file" + file.getFileExtension();
  1743. }
  1744. String addFile (const RelativePath& path, bool shouldBeCompiled, bool shouldBeAddedToBinaryResources,
  1745. bool shouldBeAddedToXcodeResources, bool inhibitWarnings, Target* xcodeTarget) const
  1746. {
  1747. const String pathAsString (path.toUnixStyle());
  1748. const String refID (addFileReference (path.toUnixStyle()));
  1749. if (shouldBeCompiled)
  1750. {
  1751. addBuildFile (pathAsString, refID, true, inhibitWarnings, xcodeTarget);
  1752. }
  1753. else if (! shouldBeAddedToBinaryResources || shouldBeAddedToXcodeResources)
  1754. {
  1755. const String fileType (getFileType (path));
  1756. if (shouldBeAddedToXcodeResources)
  1757. {
  1758. resourceIDs.add (addBuildFile (pathAsString, refID, false, false));
  1759. resourceFileRefs.add (refID);
  1760. }
  1761. }
  1762. return refID;
  1763. }
  1764. String addRezFile (const Project::Item& projectItem, const RelativePath& path) const
  1765. {
  1766. const String pathAsString (path.toUnixStyle());
  1767. const String refID (addFileReference (path.toUnixStyle()));
  1768. if (projectItem.isModuleCode())
  1769. {
  1770. if (Target* xcodeTarget = getTargetOfType (getTargetTypeFromFilePath (projectItem.getFile(), false)))
  1771. {
  1772. String rezFileID = addBuildFile (pathAsString, refID, false, false, xcodeTarget);
  1773. xcodeTarget->rezFileIDs.add (rezFileID);
  1774. return refID;
  1775. }
  1776. }
  1777. return String();
  1778. }
  1779. String getEntitlementsFileName() const
  1780. {
  1781. return project.getProjectFilenameRoot() + String (".entitlements");
  1782. }
  1783. String addEntitlementsFile() const
  1784. {
  1785. const char* sandboxEntitlement =
  1786. "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
  1787. "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">"
  1788. "<plist version=\"1.0\">"
  1789. "<dict>"
  1790. " <key>com.apple.security.app-sandbox</key>"
  1791. " <true/>"
  1792. "</dict>"
  1793. "</plist>";
  1794. File entitlementsFile = getTargetFolder().getChildFile (getEntitlementsFileName());
  1795. overwriteFileIfDifferentOrThrow (entitlementsFile, sandboxEntitlement);
  1796. RelativePath plistPath (entitlementsFile, getTargetFolder(), RelativePath::buildTargetFolder);
  1797. return addFile (plistPath, false, false, false, false, nullptr);
  1798. }
  1799. String addProjectItem (const Project::Item& projectItem) const
  1800. {
  1801. if (projectItem.isGroup())
  1802. {
  1803. StringArray childIDs;
  1804. for (int i = 0; i < projectItem.getNumChildren(); ++i)
  1805. {
  1806. const String childID (addProjectItem (projectItem.getChild(i)));
  1807. if (childID.isNotEmpty())
  1808. childIDs.add (childID);
  1809. }
  1810. return addGroup (projectItem, childIDs);
  1811. }
  1812. if (projectItem.shouldBeAddedToTargetProject())
  1813. {
  1814. const String itemPath (projectItem.getFilePath());
  1815. RelativePath path;
  1816. if (itemPath.startsWith ("${"))
  1817. path = RelativePath (itemPath, RelativePath::unknown);
  1818. else
  1819. path = RelativePath (projectItem.getFile(), getTargetFolder(), RelativePath::buildTargetFolder);
  1820. if (path.hasFileExtension (".r"))
  1821. return addRezFile (projectItem, path);
  1822. Target* xcodeTarget = nullptr;
  1823. if (projectItem.isModuleCode() && projectItem.shouldBeCompiled())
  1824. xcodeTarget = getTargetOfType (getTargetTypeFromFilePath (projectItem.getFile(), false));
  1825. return addFile (path, projectItem.shouldBeCompiled(),
  1826. projectItem.shouldBeAddedToBinaryResources(),
  1827. projectItem.shouldBeAddedToXcodeResources(),
  1828. projectItem.shouldInhibitWarnings(),
  1829. xcodeTarget);
  1830. }
  1831. return String();
  1832. }
  1833. String addFramework (const String& frameworkName) const
  1834. {
  1835. String path (frameworkName);
  1836. if (! File::isAbsolutePath (path))
  1837. path = "System/Library/Frameworks/" + path;
  1838. if (! path.endsWithIgnoreCase (".framework"))
  1839. path << ".framework";
  1840. const String fileRefID (createFileRefID (path));
  1841. addFileReference ((File::isAbsolutePath (frameworkName) ? "" : "${SDKROOT}/") + path);
  1842. frameworkFileIDs.add (fileRefID);
  1843. return addBuildFile (path, fileRefID, false, false);
  1844. }
  1845. void addGroup (const String& groupID, const String& groupName, const StringArray& childIDs) const
  1846. {
  1847. ValueTree* v = new ValueTree (groupID);
  1848. v->setProperty ("isa", "PBXGroup", nullptr);
  1849. v->setProperty ("children", indentParenthesisedList (childIDs), nullptr);
  1850. v->setProperty (Ids::name, groupName, nullptr);
  1851. v->setProperty ("sourceTree", "<group>", nullptr);
  1852. pbxGroups.add (v);
  1853. }
  1854. String addGroup (const Project::Item& item, StringArray& childIDs) const
  1855. {
  1856. const String groupName (item.getName());
  1857. const String groupID (getIDForGroup (item));
  1858. addGroup (groupID, groupName, childIDs);
  1859. return groupID;
  1860. }
  1861. void addProjectConfig (const String& configName, const StringArray& buildSettings) const
  1862. {
  1863. ValueTree* v = new ValueTree (createID ("projectconfigid_" + configName));
  1864. v->setProperty ("isa", "XCBuildConfiguration", nullptr);
  1865. v->setProperty ("buildSettings", indentBracedList (buildSettings), nullptr);
  1866. v->setProperty (Ids::name, configName, nullptr);
  1867. projectConfigs.add (v);
  1868. }
  1869. void addConfigList (Target& target, const OwnedArray <ValueTree>& configsToUse, const String& listID) const
  1870. {
  1871. ValueTree* v = new ValueTree (listID);
  1872. v->setProperty ("isa", "XCConfigurationList", nullptr);
  1873. v->setProperty ("buildConfigurations", indentParenthesisedList (target.configIDs), nullptr);
  1874. v->setProperty ("defaultConfigurationIsVisible", (int) 0, nullptr);
  1875. if (configsToUse[0] != nullptr)
  1876. v->setProperty ("defaultConfigurationName", configsToUse[0]->getProperty (Ids::name), nullptr);
  1877. misc.add (v);
  1878. }
  1879. void addProjectConfigList (const OwnedArray <ValueTree>& configsToUse, const String& listID) const
  1880. {
  1881. StringArray configIDs;
  1882. for (int i = 0; i < configsToUse.size(); ++i)
  1883. configIDs.add (configsToUse[i]->getType().toString());
  1884. ValueTree* v = new ValueTree (listID);
  1885. v->setProperty ("isa", "XCConfigurationList", nullptr);
  1886. v->setProperty ("buildConfigurations", indentParenthesisedList (configIDs), nullptr);
  1887. v->setProperty ("defaultConfigurationIsVisible", (int) 0, nullptr);
  1888. if (configsToUse[0] != nullptr)
  1889. v->setProperty ("defaultConfigurationName", configsToUse[0]->getProperty (Ids::name), nullptr);
  1890. misc.add (v);
  1891. }
  1892. void addProjectObject() const
  1893. {
  1894. ValueTree* const v = new ValueTree (createID ("__root"));
  1895. v->setProperty ("isa", "PBXProject", nullptr);
  1896. v->setProperty ("buildConfigurationList", createID ("__projList"), nullptr);
  1897. v->setProperty ("attributes", getProjectObjectAttributes(), nullptr);
  1898. v->setProperty ("compatibilityVersion", "Xcode 3.2", nullptr);
  1899. v->setProperty ("hasScannedForEncodings", (int) 0, nullptr);
  1900. v->setProperty ("mainGroup", createID ("__mainsourcegroup"), nullptr);
  1901. v->setProperty ("projectDirPath", "\"\"", nullptr);
  1902. v->setProperty ("projectRoot", "\"\"", nullptr);
  1903. String targetString = "(" + targetIDs.joinIntoString (", ") + ")";
  1904. v->setProperty ("targets", targetString, nullptr);
  1905. misc.add (v);
  1906. }
  1907. static Target::Type getTargetTypeFromFilePath (const File& file, bool returnSharedTargetIfNoValidSuffic)
  1908. {
  1909. if (LibraryModule::CompileUnit::hasSuffix (file, "_AU")) return Target::AudioUnitPlugIn;
  1910. else if (LibraryModule::CompileUnit::hasSuffix (file, "_AUv3")) return Target::AudioUnitv3PlugIn;
  1911. else if (LibraryModule::CompileUnit::hasSuffix (file, "_AAX")) return Target::AAXPlugIn;
  1912. else if (LibraryModule::CompileUnit::hasSuffix (file, "_RTAS")) return Target::RTASPlugIn;
  1913. else if (LibraryModule::CompileUnit::hasSuffix (file, "_VST2")) return Target::VSTPlugIn;
  1914. else if (LibraryModule::CompileUnit::hasSuffix (file, "_VST3")) return Target::VST3PlugIn;
  1915. else if (LibraryModule::CompileUnit::hasSuffix (file, "_Standalone")) return Target::StandalonePlugIn;
  1916. return (returnSharedTargetIfNoValidSuffic ? Target::SharedCodeTarget : Target::unspecified);
  1917. }
  1918. //==============================================================================
  1919. void removeMismatchedXcuserdata() const
  1920. {
  1921. File xcuserdata = getProjectBundle().getChildFile ("xcuserdata");
  1922. if (! xcuserdata.exists())
  1923. return;
  1924. if (! xcuserdataMatchesTargets (xcuserdata))
  1925. {
  1926. xcuserdata.deleteRecursively();
  1927. getProjectBundle().getChildFile ("project.xcworkspace").deleteRecursively();
  1928. }
  1929. }
  1930. bool xcuserdataMatchesTargets (const File& xcuserdata) const
  1931. {
  1932. Array<File> xcschemeManagementPlists;
  1933. xcuserdata.findChildFiles (xcschemeManagementPlists, File::findFiles, true, "xcschememanagement.plist");
  1934. for (auto& plist : xcschemeManagementPlists)
  1935. if (! xcschemeManagementPlistMatchesTargets (plist))
  1936. return false;
  1937. return true;
  1938. }
  1939. static StringArray parseNamesOfTargetsFromPlist (const XmlElement& dictXML)
  1940. {
  1941. forEachXmlChildElementWithTagName (dictXML, schemesKey, "key")
  1942. {
  1943. if (schemesKey->getAllSubText().trim().equalsIgnoreCase ("SchemeUserState"))
  1944. {
  1945. if (auto* dict = schemesKey->getNextElement())
  1946. {
  1947. if (dict->hasTagName ("dict"))
  1948. {
  1949. StringArray names;
  1950. forEachXmlChildElementWithTagName (*dict, key, "key")
  1951. names.add (key->getAllSubText().upToLastOccurrenceOf (".xcscheme", false, false).trim());
  1952. names.sort (false);
  1953. return names;
  1954. }
  1955. }
  1956. }
  1957. }
  1958. return StringArray();
  1959. }
  1960. StringArray getNamesOfTargets() const
  1961. {
  1962. StringArray names;
  1963. for (auto& target : targets)
  1964. names.add (target->getXCodeSchemeName());
  1965. names.sort (false);
  1966. return names;
  1967. }
  1968. bool xcschemeManagementPlistMatchesTargets (const File& plist) const
  1969. {
  1970. ScopedPointer<XmlElement> xml (XmlDocument::parse (plist));
  1971. if (xml != nullptr)
  1972. if (auto* dict = xml->getChildByName ("dict"))
  1973. return parseNamesOfTargetsFromPlist (*dict) == getNamesOfTargets();
  1974. return false;
  1975. }
  1976. //==============================================================================
  1977. struct AppIconType
  1978. {
  1979. const char* idiom;
  1980. const char* sizeString;
  1981. const char* filename;
  1982. const char* scale;
  1983. int size;
  1984. };
  1985. static Array<AppIconType> getiOSAppIconTypes()
  1986. {
  1987. AppIconType types[] =
  1988. {
  1989. { "iphone", "29x29", "Icon-29.png", "1x", 29 },
  1990. { "iphone", "29x29", "Icon-29@2x.png", "2x", 58 },
  1991. { "iphone", "29x29", "Icon-29@3x.png", "3x", 87 },
  1992. { "iphone", "40x40", "Icon-Spotlight-40@2x.png", "2x", 80 },
  1993. { "iphone", "40x40", "Icon-Spotlight-40@3x.png", "3x", 120 },
  1994. { "iphone", "57x57", "Icon.png", "1x", 57 },
  1995. { "iphone", "57x57", "Icon@2x.png", "2x", 114 },
  1996. { "iphone", "60x60", "Icon-60@2x.png", "2x", 120 },
  1997. { "iphone", "60x60", "Icon-@3x.png", "3x", 180 },
  1998. { "ipad", "29x29", "Icon-Small-1.png", "1x", 29 },
  1999. { "ipad", "29x29", "Icon-Small@2x-1.png", "2x", 58 },
  2000. { "ipad", "40x40", "Icon-Spotlight-40.png", "1x", 40 },
  2001. { "ipad", "40x40", "Icon-Spotlight-40@2x-1.png", "2x", 80 },
  2002. { "ipad", "50x50", "Icon-Small-50.png", "1x", 50 },
  2003. { "ipad", "50x50", "Icon-Small-50@2x.png", "2x", 100 },
  2004. { "ipad", "72x72", "Icon-72.png", "1x", 72 },
  2005. { "ipad", "72x72", "Icon-72@2x.png", "2x", 144 },
  2006. { "ipad", "76x76", "Icon-76.png", "1x", 76 },
  2007. { "ipad", "76x76", "Icon-76@2x.png", "2x", 152 },
  2008. { "ipad", "83.5x83.5", "Icon-83.5@2x.png", "2x", 167 }
  2009. };
  2010. return Array<AppIconType> (types, numElementsInArray (types));
  2011. }
  2012. static String getiOSAppIconContents()
  2013. {
  2014. const Array<AppIconType> types (getiOSAppIconTypes());
  2015. var images;
  2016. for (int i = 0; i < types.size(); ++i)
  2017. {
  2018. AppIconType type = types.getUnchecked(i);
  2019. DynamicObject::Ptr d = new DynamicObject();
  2020. d->setProperty ("idiom", type.idiom);
  2021. d->setProperty ("size", type.sizeString);
  2022. d->setProperty ("filename", type.filename);
  2023. d->setProperty ("scale", type.scale);
  2024. images.append (var (d));
  2025. }
  2026. return getiOSAssetContents (images);
  2027. }
  2028. String getProjectObjectAttributes() const
  2029. {
  2030. String attributes;
  2031. attributes << "{ LastUpgradeCheck = 0440; ";
  2032. if (projectType.isGUIApplication() || projectType.isAudioPlugin())
  2033. {
  2034. attributes << "TargetAttributes = { ";
  2035. for (auto& target : targets)
  2036. attributes << target->getTargetAttributes();
  2037. attributes << " }; ";
  2038. }
  2039. attributes << "}";
  2040. return attributes;
  2041. }
  2042. //==============================================================================
  2043. struct ImageType
  2044. {
  2045. const char* orientation;
  2046. const char* idiom;
  2047. const char* subtype;
  2048. const char* extent;
  2049. const char* scale;
  2050. const char* filename;
  2051. int width;
  2052. int height;
  2053. };
  2054. static Array<ImageType> getiOSLaunchImageTypes()
  2055. {
  2056. ImageType types[] =
  2057. {
  2058. { "portrait", "iphone", nullptr, "full-screen", "2x", "LaunchImage-iphone-2x.png", 640, 960 },
  2059. { "portrait", "iphone", "retina4", "full-screen", "2x", "LaunchImage-iphone-retina4.png", 640, 1136 },
  2060. { "portrait", "ipad", nullptr, "full-screen", "1x", "LaunchImage-ipad-portrait-1x.png", 768, 1024 },
  2061. { "landscape","ipad", nullptr, "full-screen", "1x", "LaunchImage-ipad-landscape-1x.png", 1024, 768 },
  2062. { "portrait", "ipad", nullptr, "full-screen", "2x", "LaunchImage-ipad-portrait-2x.png", 1536, 2048 },
  2063. { "landscape","ipad", nullptr, "full-screen", "2x", "LaunchImage-ipad-landscape-2x.png", 2048, 1536 }
  2064. };
  2065. return Array<ImageType> (types, numElementsInArray (types));
  2066. }
  2067. static String getiOSLaunchImageContents()
  2068. {
  2069. const Array<ImageType> types (getiOSLaunchImageTypes());
  2070. var images;
  2071. for (int i = 0; i < types.size(); ++i)
  2072. {
  2073. const ImageType& type = types.getReference(i);
  2074. DynamicObject::Ptr d = new DynamicObject();
  2075. d->setProperty ("orientation", type.orientation);
  2076. d->setProperty ("idiom", type.idiom);
  2077. d->setProperty ("extent", type.extent);
  2078. d->setProperty ("minimum-system-version", "7.0");
  2079. d->setProperty ("scale", type.scale);
  2080. d->setProperty ("filename", type.filename);
  2081. if (type.subtype != nullptr)
  2082. d->setProperty ("subtype", type.subtype);
  2083. images.append (var (d));
  2084. }
  2085. return getiOSAssetContents (images);
  2086. }
  2087. static void createiOSLaunchImageFiles (const File& launchImageSet)
  2088. {
  2089. const Array<ImageType> types (getiOSLaunchImageTypes());
  2090. for (int i = 0; i < types.size(); ++i)
  2091. {
  2092. const ImageType& type = types.getReference(i);
  2093. Image image (Image::ARGB, type.width, type.height, true); // (empty black image)
  2094. image.clear (image.getBounds(), Colours::black);
  2095. MemoryOutputStream pngData;
  2096. PNGImageFormat pngFormat;
  2097. pngFormat.writeImageToStream (image, pngData);
  2098. overwriteFileIfDifferentOrThrow (launchImageSet.getChildFile (type.filename), pngData);
  2099. }
  2100. }
  2101. //==============================================================================
  2102. static String getiOSAssetContents (var images)
  2103. {
  2104. DynamicObject::Ptr v (new DynamicObject());
  2105. var info (new DynamicObject());
  2106. info.getDynamicObject()->setProperty ("version", 1);
  2107. info.getDynamicObject()->setProperty ("author", "xcode");
  2108. v->setProperty ("images", images);
  2109. v->setProperty ("info", info);
  2110. return JSON::toString (var (v));
  2111. }
  2112. void createXcassetsFolderFromIcons() const
  2113. {
  2114. const File assets (getTargetFolder().getChildFile (project.getProjectFilenameRoot())
  2115. .getChildFile ("Images.xcassets"));
  2116. const File iconSet (assets.getChildFile ("AppIcon.appiconset"));
  2117. const File launchImage (assets.getChildFile ("LaunchImage.launchimage"));
  2118. overwriteFileIfDifferentOrThrow (iconSet.getChildFile ("Contents.json"), getiOSAppIconContents());
  2119. createiOSIconFiles (iconSet);
  2120. overwriteFileIfDifferentOrThrow (launchImage.getChildFile ("Contents.json"), getiOSLaunchImageContents());
  2121. createiOSLaunchImageFiles (launchImage);
  2122. RelativePath assetsPath (assets, getTargetFolder(), RelativePath::buildTargetFolder);
  2123. addFileReference (assetsPath.toUnixStyle());
  2124. resourceIDs.add (addBuildFile (assetsPath, false, false));
  2125. resourceFileRefs.add (createFileRefID (assetsPath));
  2126. }
  2127. //==============================================================================
  2128. static String indentBracedList (const StringArray& list) { return "{" + indentList (list, ";", 0, true) + " }"; }
  2129. static String indentParenthesisedList (const StringArray& list) { return "(" + indentList (list, ",", 1, false) + " )"; }
  2130. static String indentList (const StringArray& list, const String& separator, int extraTabs, bool shouldSort)
  2131. {
  2132. if (list.size() == 0)
  2133. return " ";
  2134. const String tabs ("\n" + String::repeatedString ("\t", extraTabs + 4));
  2135. if (shouldSort)
  2136. {
  2137. StringArray sorted (list);
  2138. sorted.sort (true);
  2139. return tabs + sorted.joinIntoString (separator + tabs) + separator;
  2140. }
  2141. return tabs + list.joinIntoString (separator + tabs) + separator;
  2142. }
  2143. String createID (String rootString) const
  2144. {
  2145. if (rootString.startsWith ("${"))
  2146. rootString = rootString.fromFirstOccurrenceOf ("}/", false, false);
  2147. rootString += project.getProjectUID();
  2148. return MD5 (rootString.toUTF8()).toHexString().substring (0, 24).toUpperCase();
  2149. }
  2150. String createFileRefID (const RelativePath& path) const { return createFileRefID (path.toUnixStyle()); }
  2151. String createFileRefID (const String& path) const { return createID ("__fileref_" + path); }
  2152. String getIDForGroup (const Project::Item& item) const { return createID (item.getID()); }
  2153. bool shouldFileBeCompiledByDefault (const RelativePath& file) const override
  2154. {
  2155. return file.hasFileExtension (sourceFileExtensions);
  2156. }
  2157. static String getOSXVersionName (int version)
  2158. {
  2159. jassert (version >= 4);
  2160. return "10." + String (version);
  2161. }
  2162. static String getSDKName (int version)
  2163. {
  2164. return getOSXVersionName (version) + " SDK";
  2165. }
  2166. void initialiseDependencyPathValues()
  2167. {
  2168. vst3Path.referTo (Value (new DependencyPathValueSource (getSetting (Ids::vst3Folder), Ids::vst3Path, TargetOS::osx)));
  2169. aaxPath. referTo (Value (new DependencyPathValueSource (getSetting (Ids::aaxFolder), Ids::aaxPath, TargetOS::osx)));
  2170. rtasPath.referTo (Value (new DependencyPathValueSource (getSetting (Ids::rtasFolder), Ids::rtasPath, TargetOS::osx)));
  2171. }
  2172. void addRTASPluginSettings()
  2173. {
  2174. xcodeCanUseDwarf = false;
  2175. extraSearchPaths.add ("$(DEVELOPER_DIR)/Headers/FlatCarbon");
  2176. extraSearchPaths.add ("$(SDKROOT)/Developer/Headers/FlatCarbon");
  2177. static const char* p[] = { "AlturaPorts/TDMPlugIns/PlugInLibrary/Controls",
  2178. "AlturaPorts/TDMPlugIns/PlugInLibrary/CoreClasses",
  2179. "AlturaPorts/TDMPlugIns/PlugInLibrary/DSPClasses",
  2180. "AlturaPorts/TDMPlugIns/PlugInLibrary/EffectClasses",
  2181. "AlturaPorts/TDMPlugIns/PlugInLibrary/MacBuild",
  2182. "AlturaPorts/TDMPlugIns/PlugInLibrary/Meters",
  2183. "AlturaPorts/TDMPlugIns/PlugInLibrary/ProcessClasses",
  2184. "AlturaPorts/TDMPlugIns/PlugInLibrary/ProcessClasses/Interfaces",
  2185. "AlturaPorts/TDMPlugIns/PlugInLibrary/RTASP_Adapt",
  2186. "AlturaPorts/TDMPlugIns/PlugInLibrary/Utilities",
  2187. "AlturaPorts/TDMPlugIns/PlugInLibrary/ViewClasses",
  2188. "AlturaPorts/TDMPlugIns/DSPManager/**",
  2189. "AlturaPorts/TDMPlugIns/SupplementalPlugInLib/Encryption",
  2190. "AlturaPorts/TDMPlugIns/SupplementalPlugInLib/GraphicsExtensions",
  2191. "AlturaPorts/TDMPlugIns/common/**",
  2192. "AlturaPorts/TDMPlugIns/common/PI_LibInterface",
  2193. "AlturaPorts/TDMPlugIns/PACEProtection/**",
  2194. "AlturaPorts/TDMPlugIns/SignalProcessing/**",
  2195. "AlturaPorts/OMS/Headers",
  2196. "AlturaPorts/Fic/Interfaces/**",
  2197. "AlturaPorts/Fic/Source/SignalNets",
  2198. "AlturaPorts/DSIPublicInterface/PublicHeaders",
  2199. "DAEWin/Include",
  2200. "AlturaPorts/DigiPublic/Interfaces",
  2201. "AlturaPorts/DigiPublic",
  2202. "AlturaPorts/NewFileLibs/DOA",
  2203. "AlturaPorts/NewFileLibs/Cmn",
  2204. "xplat/AVX/avx2/avx2sdk/inc",
  2205. "xplat/AVX/avx2/avx2sdk/utils" };
  2206. RelativePath rtasFolder (getRTASPathValue().toString(), RelativePath::projectFolder);
  2207. for (int i = 0; i < numElementsInArray (p); ++i)
  2208. addToExtraSearchPaths (rtasFolder.getChildFile (p[i]));
  2209. }
  2210. JUCE_DECLARE_NON_COPYABLE (XCodeProjectExporter)
  2211. };