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.

2746 lines
120KB

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