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.

1062 lines
48KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-10 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. #ifndef __JUCER_PROJECTEXPORT_XCODE_JUCEHEADER__
  19. #define __JUCER_PROJECTEXPORT_XCODE_JUCEHEADER__
  20. #include "jucer_ProjectExporter.h"
  21. //==============================================================================
  22. class XCodeProjectExporter : public ProjectExporter
  23. {
  24. public:
  25. //==============================================================================
  26. static const char* getNameMac() { return "XCode (MacOSX)"; }
  27. static const char* getNameiPhone() { return "XCode (iPhone)"; }
  28. static const char* getValueTreeTypeName (bool iPhone) { return iPhone ? "XCODE_IPHONE" : "XCODE_MAC"; }
  29. //==============================================================================
  30. XCodeProjectExporter (Project& project_, const ValueTree& settings_, const bool iPhone_)
  31. : ProjectExporter (project_, settings_),
  32. iPhone (iPhone_)
  33. {
  34. name = iPhone ? getNameiPhone() : getNameMac();
  35. projectIDSalt = hashCode64 (project.getProjectUID());
  36. if (getTargetLocation().toString().isEmpty())
  37. getTargetLocation() = getDefaultBuildsRootFolder() + (iPhone ? "iPhone" : "MacOSX");
  38. if (getVSTFolder().toString().isEmpty())
  39. getVSTFolder() = "~/SDKs/vstsdk2.4";
  40. if (getRTASFolder().toString().isEmpty())
  41. getRTASFolder() = "~/SDKs/PT_80_SDK";
  42. }
  43. ~XCodeProjectExporter()
  44. {
  45. }
  46. static XCodeProjectExporter* createForSettings (Project& project, const ValueTree& settings)
  47. {
  48. if (settings.hasType (getValueTreeTypeName (false)))
  49. return new XCodeProjectExporter (project, settings, false);
  50. else if (settings.hasType (getValueTreeTypeName (true)))
  51. return new XCodeProjectExporter (project, settings, true);
  52. return 0;
  53. }
  54. //==============================================================================
  55. bool isDefaultFormatForCurrentOS()
  56. {
  57. #if JUCE_MAC
  58. return ! iPhone;
  59. #else
  60. return false;
  61. #endif
  62. }
  63. bool isPossibleForCurrentProject() { return project.isGUIApplication() || ! iPhone; }
  64. bool usesMMFiles() const { return true; }
  65. void createPropertyEditors (Array <PropertyComponent*>& props)
  66. {
  67. ProjectExporter::createPropertyEditors (props);
  68. props.add (new TextPropertyComponent (getSetting ("objCExtraSuffix"), "Objective-C class name suffix", 64, false));
  69. props.getLast()->setTooltip ("Because objective-C linkage is done by string-matching, you can get horrible linkage mix-ups when different modules containing the "
  70. "same class-names are loaded simultaneously. This setting lets you provide a unique string that will be used in naming the obj-C classes in your executable to avoid this.");
  71. if (project.isGUIApplication() && ! iPhone)
  72. {
  73. props.add (new TextPropertyComponent (getSetting ("documentExtensions"), "Document file extensions", 128, false));
  74. props.getLast()->setTooltip ("A comma-separated list of file extensions for documents that your app can open.");
  75. }
  76. }
  77. void launchProject()
  78. {
  79. getProjectBundle().startAsProcess();
  80. }
  81. //==============================================================================
  82. const String create()
  83. {
  84. infoPlistFile = getTargetFolder().getChildFile ("Info.plist");
  85. File projectBundle (getProjectBundle());
  86. if (! projectBundle.createDirectory())
  87. return "Can't write to the target directory";
  88. createObjects();
  89. File projectFile (projectBundle.getChildFile ("project.pbxproj"));
  90. {
  91. MemoryOutputStream mo;
  92. writeProjectFile (mo);
  93. if (! FileHelpers::overwriteFileWithNewDataIfDifferent (projectFile, mo))
  94. return "Can't write to file: " + projectFile.getFullPathName();
  95. }
  96. if (! writeInfoPlistFile())
  97. return "Can't write the Info.plist file";
  98. return String::empty;
  99. }
  100. private:
  101. OwnedArray<ValueTree> pbxBuildFiles, pbxFileReferences, groups, misc, projectConfigs, targetConfigs;
  102. StringArray buildPhaseIDs, resourceIDs, sourceIDs, frameworkIDs;
  103. StringArray frameworkFileIDs, rezFileIDs, resourceFileRefs;
  104. File infoPlistFile;
  105. int64 projectIDSalt;
  106. const bool iPhone;
  107. static const String sanitisePath (const String& path)
  108. {
  109. if (path.startsWithChar ('~'))
  110. return "$(HOME)" + path.substring (1);
  111. return path;
  112. }
  113. const File getProjectBundle() const { return getTargetFolder().getChildFile (project.getProjectFilenameRoot()).withFileExtension (".xcodeproj"); }
  114. bool hasPList() const { return ! (project.isLibrary() || project.isCommandLineApp()); }
  115. const String getAudioPluginBundleExtension() const { return "component"; }
  116. //==============================================================================
  117. void createObjects()
  118. {
  119. if (! project.isLibrary())
  120. addFrameworks();
  121. const String productName (project.getConfiguration (0).getTargetBinaryName().toString());
  122. if (project.isGUIApplication())
  123. addBuildProduct ("wrapper.application", productName + ".app");
  124. else if (project.isCommandLineApp())
  125. addBuildProduct ("compiled.mach-o.executable", productName);
  126. else if (project.isLibrary())
  127. addBuildProduct ("archive.ar", getLibbedFilename (productName));
  128. else if (project.isAudioPlugin())
  129. addBuildProduct ("wrapper.cfbundle", productName + "." + getAudioPluginBundleExtension());
  130. else if (project.isBrowserPlugin())
  131. addBuildProduct ("wrapper.cfbundle", productName + ".plugin");
  132. else
  133. jassert (productName.isEmpty());
  134. if (hasPList())
  135. {
  136. RelativePath plistPath (infoPlistFile, getTargetFolder(), RelativePath::buildTargetFolder);
  137. addFileReference (plistPath);
  138. resourceFileRefs.add (createID (plistPath));
  139. }
  140. addProjectItem (project.getMainGroup());
  141. for (int i = 0; i < project.getNumConfigurations(); ++i)
  142. {
  143. Project::BuildConfiguration config (project.getConfiguration (i));
  144. addProjectConfig (config.getName().getValue(), getProjectSettings (config));
  145. addTargetConfig (config.getName().getValue(), getTargetSettings (config));
  146. }
  147. addConfigList (projectConfigs, createID ("__projList"));
  148. addConfigList (targetConfigs, createID ("__configList"));
  149. if (! project.isLibrary())
  150. addBuildPhase ("PBXResourcesBuildPhase", resourceIDs);
  151. if (rezFileIDs.size() > 0)
  152. addBuildPhase ("PBXRezBuildPhase", rezFileIDs);
  153. addBuildPhase ("PBXSourcesBuildPhase", sourceIDs);
  154. if (! project.isLibrary())
  155. addBuildPhase ("PBXFrameworksBuildPhase", frameworkIDs);
  156. if (project.isAudioPlugin())
  157. addPluginShellScriptPhase();
  158. addTargetObject();
  159. addProjectObject();
  160. }
  161. bool writeInfoPlistFile()
  162. {
  163. if (! hasPList())
  164. return true;
  165. XmlElement plist ("plist");
  166. XmlElement* dict = plist.createNewChildElement ("dict");
  167. addPlistDictionaryKey (dict, "CFBundleExecutable", "${EXECUTABLE_NAME}");
  168. addPlistDictionaryKey (dict, "CFBundleIconFile", "");
  169. addPlistDictionaryKey (dict, "CFBundleIdentifier", project.getBundleIdentifier().toString());
  170. addPlistDictionaryKey (dict, "CFBundleName", project.getProjectName().toString());
  171. if (project.isAudioPlugin())
  172. {
  173. addPlistDictionaryKey (dict, "CFBundlePackageType", "TDMw");
  174. addPlistDictionaryKey (dict, "CFBundleSignature", "PTul");
  175. }
  176. else
  177. {
  178. addPlistDictionaryKey (dict, "CFBundlePackageType", "APPL");
  179. addPlistDictionaryKey (dict, "CFBundleSignature", "????");
  180. }
  181. addPlistDictionaryKey (dict, "CFBundleShortVersionString", project.getVersion().toString());
  182. addPlistDictionaryKey (dict, "CFBundleVersion", project.getVersion().toString());
  183. StringArray documentExtensions;
  184. documentExtensions.addTokens (getSetting ("documentExtensions").toString(), ",", String::empty);
  185. documentExtensions.trim();
  186. documentExtensions.removeEmptyStrings (true);
  187. if (documentExtensions.size() > 0)
  188. {
  189. dict->createNewChildElement ("key")->addTextElement ("CFBundleDocumentTypes");
  190. XmlElement* dict2 = dict->createNewChildElement ("array")->createNewChildElement ("dict");
  191. for (int i = 0; i < documentExtensions.size(); ++i)
  192. {
  193. String ex (documentExtensions[i]);
  194. if (ex.startsWithChar ('.'))
  195. ex = ex.substring (1);
  196. dict2->createNewChildElement ("key")->addTextElement ("CFBundleTypeExtensions");
  197. dict2->createNewChildElement ("array")->createNewChildElement ("string")->addTextElement (ex);
  198. addPlistDictionaryKey (dict2, "CFBundleTypeName", ex);
  199. addPlistDictionaryKey (dict2, "CFBundleTypeRole", "Editor");
  200. addPlistDictionaryKey (dict2, "NSPersistentStoreTypeKey", "XML");
  201. }
  202. }
  203. MemoryOutputStream mo;
  204. plist.writeToStream (mo, "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">");
  205. return FileHelpers::overwriteFileWithNewDataIfDifferent (infoPlistFile, mo);
  206. }
  207. const StringArray getHeaderSearchPaths (const Project::BuildConfiguration& config)
  208. {
  209. StringArray searchPaths (config.getHeaderSearchPaths());
  210. if (project.shouldAddVSTFolderToPath() && getVSTFolder().toString().isNotEmpty())
  211. searchPaths.add (RelativePath (getVSTFolder().toString(), RelativePath::projectFolder)
  212. .rebased (project.getFile().getParentDirectory(), getTargetFolder(), RelativePath::buildTargetFolder)
  213. .toUnixStyle());
  214. if (project.isAudioPlugin())
  215. {
  216. if (isAU())
  217. {
  218. searchPaths.add ("$(DEVELOPER_DIR)/Extras/CoreAudio/PublicUtility");
  219. searchPaths.add ("$(DEVELOPER_DIR)/Extras/CoreAudio/AudioUnits/AUPublic/Utility");
  220. }
  221. if (isRTAS())
  222. {
  223. searchPaths.add ("/Developer/Headers/FlatCarbon");
  224. static const char* rtasIncludePaths[] = { "AlturaPorts/TDMPlugIns/PlugInLibrary/Controls",
  225. "AlturaPorts/TDMPlugIns/PlugInLibrary/CoreClasses",
  226. "AlturaPorts/TDMPlugIns/PlugInLibrary/DSPClasses",
  227. "AlturaPorts/TDMPlugIns/PlugInLibrary/EffectClasses",
  228. "AlturaPorts/TDMPlugIns/PlugInLibrary/MacBuild",
  229. "AlturaPorts/TDMPlugIns/PlugInLibrary/Meters",
  230. "AlturaPorts/TDMPlugIns/PlugInLibrary/ProcessClasses",
  231. "AlturaPorts/TDMPlugIns/PlugInLibrary/ProcessClasses/Interfaces",
  232. "AlturaPorts/TDMPlugIns/PlugInLibrary/RTASP_Adapt",
  233. "AlturaPorts/TDMPlugIns/PlugInLibrary/Utilities",
  234. "AlturaPorts/TDMPlugIns/PlugInLibrary/ViewClasses",
  235. "AlturaPorts/TDMPlugIns/DSPManager/**",
  236. "AlturaPorts/TDMPlugIns/SupplementalPlugInLib/Encryption",
  237. "AlturaPorts/TDMPlugIns/SupplementalPlugInLib/GraphicsExtensions",
  238. "AlturaPorts/TDMPlugIns/common",
  239. "AlturaPorts/TDMPlugIns/common/PI_LibInterface",
  240. "AlturaPorts/TDMPlugIns/PACEProtection/**",
  241. "AlturaPorts/TDMPlugIns/SignalProcessing/**",
  242. "AlturaPorts/OMS/Headers",
  243. "AlturaPorts/Fic/Interfaces/**",
  244. "AlturaPorts/Fic/Source/SignalNets",
  245. "AlturaPorts/DSIPublicInterface/PublicHeaders",
  246. "DAEWin/Include",
  247. "AlturaPorts/DigiPublic/Interfaces",
  248. "AlturaPorts/DigiPublic",
  249. "AlturaPorts/NewFileLibs/DOA",
  250. "AlturaPorts/NewFileLibs/Cmn",
  251. "xplat/AVX/avx2/avx2sdk/inc",
  252. "xplat/AVX/avx2/avx2sdk/utils" };
  253. RelativePath sdkFolder (getRTASFolder().toString(), RelativePath::projectFolder);
  254. sdkFolder = sdkFolder.rebased (project.getFile().getParentDirectory(), getTargetFolder(), RelativePath::buildTargetFolder);
  255. for (int i = 0; i < numElementsInArray (rtasIncludePaths); ++i)
  256. searchPaths.add (sdkFolder.getChildFile (rtasIncludePaths[i]).toUnixStyle());
  257. }
  258. }
  259. return searchPaths;
  260. }
  261. void getLinkerFlagsForStaticLibrary (const RelativePath& library, StringArray& flags, StringArray& librarySearchPaths)
  262. {
  263. jassert (library.getFileNameWithoutExtension().substring (0, 3) == "lib");
  264. flags.add ("-l" + library.getFileNameWithoutExtension().substring (3));
  265. String searchPath (library.toUnixStyle().upToLastOccurrenceOf ("/", false, false));
  266. if (! library.isAbsolute())
  267. searchPath = "$(SRCROOT)/" + searchPath;
  268. librarySearchPaths.add (sanitisePath (searchPath));
  269. }
  270. void getLinkerFlags (const Project::BuildConfiguration& config, StringArray& flags, StringArray& librarySearchPaths)
  271. {
  272. if (project.isAudioPlugin())
  273. {
  274. flags.add ("-bundle");
  275. if (isRTAS() && getRTASFolder().toString().isNotEmpty())
  276. {
  277. getLinkerFlagsForStaticLibrary (RelativePath (getRTASFolder().toString(), RelativePath::buildTargetFolder)
  278. .getChildFile (config.isDebug().getValue() ? "MacBag/Libs/Debug/libPluginLibrary.a"
  279. : "MacBag/Libs/Release/libPluginLibrary.a"),
  280. flags, librarySearchPaths);
  281. }
  282. }
  283. if (project.getJuceLinkageMode() == Project::useLinkedJuce)
  284. {
  285. RelativePath juceLib (getJucePathFromTargetFolder().getChildFile (config.isDebug().getValue() ? "bin/libjucedebug.a"
  286. : "bin/libjuce.a"));
  287. getLinkerFlagsForStaticLibrary (juceLib, flags, librarySearchPaths);
  288. }
  289. flags.add (getExtraLinkerFlags().toString());
  290. flags.removeEmptyStrings (true);
  291. }
  292. const StringArray getProjectSettings (const Project::BuildConfiguration& config)
  293. {
  294. StringArray s;
  295. s.add ("ALWAYS_SEARCH_USER_PATHS = NO");
  296. s.add ("GCC_C_LANGUAGE_STANDARD = c99");
  297. s.add ("GCC_WARN_ABOUT_RETURN_TYPE = YES");
  298. s.add ("GCC_WARN_CHECK_SWITCH_STATEMENTS = YES");
  299. s.add ("GCC_WARN_UNUSED_VARIABLE = YES");
  300. s.add ("GCC_WARN_MISSING_PARENTHESES = YES");
  301. s.add ("GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES");
  302. s.add ("GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES");
  303. s.add ("GCC_MODEL_TUNING = G5");
  304. s.add ("GCC_INLINES_ARE_PRIVATE_EXTERN = YES");
  305. s.add ("ZERO_LINK = NO");
  306. s.add ("DEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\"");
  307. s.add ("PRODUCT_NAME = \"" + config.getTargetBinaryName().toString() + "\"");
  308. return s;
  309. }
  310. const StringArray getTargetSettings (const Project::BuildConfiguration& config)
  311. {
  312. StringArray s;
  313. s.add ("ARCHS = \"$(ARCHS_STANDARD_32_BIT)\"");
  314. s.add ("PREBINDING = NO");
  315. s.add ("HEADER_SEARCH_PATHS = \"" + getHeaderSearchPaths (config).joinIntoString (" ") + " $(inherited)\"");
  316. s.add ("GCC_OPTIMIZATION_LEVEL = " + config.getGCCOptimisationFlag());
  317. s.add ("INFOPLIST_FILE = " + infoPlistFile.getFileName());
  318. if (getExtraCompilerFlags().toString().isNotEmpty())
  319. s.add ("OTHER_CPLUSPLUSFLAGS = " + getExtraCompilerFlags().toString());
  320. if (project.isGUIApplication())
  321. {
  322. s.add ("INSTALL_PATH = \"$(HOME)/Applications\"");
  323. }
  324. else if (project.isAudioPlugin())
  325. {
  326. s.add ("LIBRARY_STYLE = Bundle");
  327. s.add ("INSTALL_PATH = \"$(HOME)/Library/Audio/Plug-Ins/Components/\"");
  328. s.add ("WRAPPER_EXTENSION = " + getAudioPluginBundleExtension());
  329. s.add ("GENERATE_PKGINFO_FILE = YES");
  330. s.add ("OTHER_REZFLAGS = \"-d ppc_$ppc -d i386_$i386 -d ppc64_$ppc64 -d x86_64_$x86_64"
  331. " -I /System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework/Versions/A/Headers"
  332. " -I \\\"$(DEVELOPER_DIR)/Extras/CoreAudio/AudioUnits/AUPublic/AUBase\\\"\"");
  333. }
  334. else if (project.isBrowserPlugin())
  335. {
  336. s.add ("LIBRARY_STYLE = Bundle");
  337. s.add ("INSTALL_PATH = \"/Library/Internet Plug-Ins/\"");
  338. }
  339. else if (project.isLibrary())
  340. {
  341. if (config.getTargetBinaryRelativePath().toString().isNotEmpty())
  342. {
  343. RelativePath binaryPath (config.getTargetBinaryRelativePath().toString(), RelativePath::projectFolder);
  344. binaryPath = binaryPath.rebased (project.getFile().getParentDirectory(), getTargetFolder(), RelativePath::buildTargetFolder);
  345. s.add ("DSTROOT = " + sanitisePath (binaryPath.toUnixStyle()));
  346. s.add ("SYMROOT = " + sanitisePath (binaryPath.toUnixStyle()));
  347. }
  348. s.add ("CONFIGURATION_BUILD_DIR = \"$(BUILD_DIR)\"");
  349. s.add ("DEPLOYMENT_LOCATION = YES");
  350. }
  351. else if (project.isCommandLineApp())
  352. {
  353. }
  354. else
  355. {
  356. jassertfalse;
  357. }
  358. if (iPhone)
  359. {
  360. s.add ("SDKROOT = iphonesimulator3.0");
  361. }
  362. else
  363. {
  364. const String sdk (config.getMacSDKVersion().toString());
  365. const String sdkCompat (config.getMacCompatibilityVersion().toString());
  366. if (sdk == Project::BuildConfiguration::osxVersion10_4)
  367. {
  368. s.add ("SDKROOT = macosx10.4");
  369. s.add ("GCC_VERSION = 4.0");
  370. }
  371. else if (sdk == Project::BuildConfiguration::osxVersion10_5)
  372. {
  373. s.add ("SDKROOT = macosx10.5");
  374. }
  375. else if (sdk == Project::BuildConfiguration::osxVersion10_6)
  376. {
  377. s.add ("SDKROOT = macosx10.6");
  378. }
  379. if (sdkCompat == Project::BuildConfiguration::osxVersion10_4)
  380. s.add ("MACOSX_DEPLOYMENT_TARGET = 10.4");
  381. else if (sdkCompat == Project::BuildConfiguration::osxVersion10_5)
  382. s.add ("MACOSX_DEPLOYMENT_TARGET = 10.5");
  383. else if (sdkCompat == Project::BuildConfiguration::osxVersion10_6)
  384. s.add ("MACOSX_DEPLOYMENT_TARGET = 10.6");
  385. s.add ("MACOSX_DEPLOYMENT_TARGET_ppc = 10.4");
  386. }
  387. {
  388. StringArray linkerFlags, librarySearchPaths;
  389. getLinkerFlags (config, linkerFlags, librarySearchPaths);
  390. if (linkerFlags.size() > 0)
  391. s.add ("OTHER_LDFLAGS = \"" + linkerFlags.joinIntoString (" ") + "\"");
  392. if (librarySearchPaths.size() > 0)
  393. {
  394. String libPaths ("LIBRARY_SEARCH_PATHS = (\"$(inherited)\"");
  395. for (int i = 0; i < librarySearchPaths.size(); ++i)
  396. libPaths += ", \"\\\"" + librarySearchPaths[i] + "\\\"\"";
  397. s.add (libPaths + ")");
  398. }
  399. }
  400. StringArray defines;
  401. defines.add (getExporterIdentifierMacro() + "=1");
  402. if (config.isDebug().getValue())
  403. {
  404. defines.add ("_DEBUG=1");
  405. defines.add ("DEBUG=1 ");
  406. s.add ("ONLY_ACTIVE_ARCH = YES");
  407. s.add ("COPY_PHASE_STRIP = NO");
  408. s.add ("GCC_DYNAMIC_NO_PIC = NO");
  409. s.add ("GCC_ENABLE_FIX_AND_CONTINUE = NO");
  410. }
  411. else
  412. {
  413. defines.add ("_NDEBUG=1");
  414. defines.add ("NDEBUG=1 ");
  415. s.add ("GCC_GENERATE_DEBUGGING_SYMBOLS = NO");
  416. s.add ("GCC_SYMBOLS_PRIVATE_EXTERN = YES");
  417. }
  418. {
  419. const String objCSuffix (getSetting ("objCExtraSuffix").toString().trim());
  420. if (objCSuffix.isNotEmpty())
  421. defines.add ("JUCE_ObjCExtraSuffix=" + objCSuffix);
  422. }
  423. {
  424. defines.addArray (config.parsePreprocessorDefs());
  425. defines.addArray (parsePreprocessorDefs());
  426. for (int i = defines.size(); --i >= 0;)
  427. defines.set (i, defines[i].quoted());
  428. s.add ("GCC_PREPROCESSOR_DEFINITIONS = (" + indentList (defines, ",") + ")");
  429. }
  430. return s;
  431. }
  432. void addFrameworks()
  433. {
  434. StringArray s;
  435. if (iPhone)
  436. {
  437. s.addTokens ("UIKit Foundation CoreGraphics AudioToolbox QuartzCore OpenGLES", false);
  438. }
  439. else
  440. {
  441. s.addTokens ("Cocoa Carbon IOKit CoreAudio CoreMIDI WebKit DiscRecording OpenGL QuartzCore QTKit QuickTime", false);
  442. if (isAU())
  443. s.addTokens ("AudioUnit CoreAudioKit AudioToolbox", false);
  444. else if (project.getJuceConfigFlag ("JUCE_PLUGINHOST_AU").toString() == Project::configFlagEnabled)
  445. s.addTokens ("AudioUnit CoreAudioKit", false);
  446. }
  447. for (int i = 0; i < s.size(); ++i)
  448. addFramework (s[i]);
  449. }
  450. //==============================================================================
  451. void writeProjectFile (OutputStream& output)
  452. {
  453. output << "// !$*UTF8*$!\n{\n"
  454. "\tarchiveVersion = 1;\n"
  455. "\tclasses = {\n\t};\n"
  456. "\tobjectVersion = 44;\n"
  457. "\tobjects = {\n\n";
  458. Array <ValueTree*> objects;
  459. objects.addArray (pbxBuildFiles);
  460. objects.addArray (pbxFileReferences);
  461. objects.addArray (groups);
  462. objects.addArray (targetConfigs);
  463. objects.addArray (projectConfigs);
  464. objects.addArray (misc);
  465. for (int i = 0; i < objects.size(); ++i)
  466. {
  467. ValueTree& o = *objects.getUnchecked(i);
  468. output << "\t\t" << static_cast <const juce_wchar*> (o.getType()) << " = { ";
  469. for (int j = 0; j < o.getNumProperties(); ++j)
  470. {
  471. const Identifier propertyName (o.getPropertyName(j));
  472. String val (o.getProperty (propertyName).toString());
  473. if (val.isEmpty() || (val.containsAnyOf (" \t;<>()=,-\r\n")
  474. && ! (val.trimStart().startsWithChar ('(')
  475. || val.trimStart().startsWithChar ('{'))))
  476. val = val.quoted();
  477. output << propertyName.toString() << " = " << val << "; ";
  478. }
  479. output << "};\n";
  480. }
  481. output << "\t};\n\trootObject = " << createID ("__root") << ";\n}\n";
  482. }
  483. static void addPlistDictionaryKey (XmlElement* xml, const String& key, const String& value)
  484. {
  485. xml->createNewChildElement ("key")->addTextElement (key);
  486. xml->createNewChildElement ("string")->addTextElement (value);
  487. }
  488. const String addBuildFile (const RelativePath& path, const String& fileRefID, bool addToSourceBuildPhase, bool inhibitWarnings)
  489. {
  490. String fileID (createID (path.toUnixStyle() + "buildref"));
  491. if (addToSourceBuildPhase)
  492. sourceIDs.add (fileID);
  493. ValueTree* v = new ValueTree (fileID);
  494. v->setProperty ("isa", "PBXBuildFile", 0);
  495. v->setProperty ("fileRef", fileRefID, 0);
  496. if (inhibitWarnings)
  497. v->setProperty ("settings", "{COMPILER_FLAGS = \"-w\"; }", 0);
  498. pbxBuildFiles.add (v);
  499. return fileID;
  500. }
  501. const String addBuildFile (const RelativePath& path, bool addToSourceBuildPhase, bool inhibitWarnings)
  502. {
  503. return addBuildFile (path, createID (path), addToSourceBuildPhase, inhibitWarnings);
  504. }
  505. void addFileReference (const RelativePath& path, const String& sourceTree, const String& lastKnownFileType, const String& fileRefID)
  506. {
  507. ValueTree* v = new ValueTree (fileRefID);
  508. v->setProperty ("isa", "PBXFileReference", 0);
  509. v->setProperty ("lastKnownFileType", lastKnownFileType, 0);
  510. v->setProperty (Ids::name, path.getFileName(), 0);
  511. v->setProperty ("path", sanitisePath (path.toUnixStyle()), 0);
  512. v->setProperty ("sourceTree", sourceTree, 0);
  513. pbxFileReferences.add (v);
  514. }
  515. const String addFileReference (const RelativePath& path)
  516. {
  517. const String fileRefID (createID (path));
  518. jassert (path.isAbsolute() || path.getRoot() == RelativePath::buildTargetFolder);
  519. addFileReference (path, path.isAbsolute() ? "<absolute>" : "SOURCE_ROOT",
  520. getFileType (path), fileRefID);
  521. return fileRefID;
  522. }
  523. static const String getFileType (const RelativePath& file)
  524. {
  525. if (file.hasFileExtension (".cpp")) return "sourcecode.cpp.cpp";
  526. else if (file.hasFileExtension (".mm")) return "sourcecode.cpp.objcpp";
  527. else if (file.hasFileExtension (".m")) return "sourcecode.c.objc";
  528. else if (file.hasFileExtension (".h;.hpp")) return "sourcecode.c.h";
  529. else if (file.hasFileExtension (".framework")) return "wrapper.framework";
  530. else if (file.hasFileExtension (".jpeg;.jpg")) return "image.jpeg";
  531. else if (file.hasFileExtension ("png;gif")) return "image" + file.getFileExtension();
  532. else if (file.hasFileExtension ("html;htm")) return "text.html";
  533. else if (file.hasFileExtension ("txt;rtf")) return "text" + file.getFileExtension();
  534. else if (file.hasFileExtension ("plist")) return "text.plist.xml";
  535. else if (file.hasFileExtension ("app")) return "wrapper.application";
  536. else if (file.hasFileExtension ("component;vst;plugin")) return "wrapper.cfbundle";
  537. else if (file.hasFileExtension ("xcodeproj")) return "wrapper.pb-project";
  538. else if (file.hasFileExtension ("a")) return "archive.ar";
  539. return "file" + file.getFileExtension();
  540. }
  541. const String addFile (const RelativePath& path, bool shouldBeCompiled, bool inhibitWarnings)
  542. {
  543. if (shouldBeCompiled)
  544. addBuildFile (path, true, inhibitWarnings);
  545. else if (path.hasFileExtension (".r"))
  546. rezFileIDs.add (addBuildFile (path, false, inhibitWarnings));
  547. return addFileReference (path);
  548. }
  549. const String addProjectItem (const Project::Item& projectItem)
  550. {
  551. if (projectItem.isGroup())
  552. {
  553. StringArray childIDs;
  554. for (int i = 0; i < projectItem.getNumChildren(); ++i)
  555. {
  556. const String childID (addProjectItem (projectItem.getChild(i)));
  557. if (childID.isNotEmpty())
  558. childIDs.add (childID);
  559. }
  560. return addGroup (projectItem, childIDs);
  561. }
  562. else
  563. {
  564. if (projectItem.shouldBeAddedToTargetProject())
  565. {
  566. const RelativePath path (projectItem.getFile(), getTargetFolder(), RelativePath::buildTargetFolder);
  567. return addFile (path, projectItem.shouldBeCompiled(), false);
  568. }
  569. }
  570. return String::empty;
  571. }
  572. void addFramework (const String& frameworkName)
  573. {
  574. const RelativePath path ("System/Library/Frameworks/" + frameworkName + ".framework", RelativePath::unknown);
  575. const String fileRefID (createID (path));
  576. addFileReference (path, "SDKROOT", getFileType (path), fileRefID);
  577. frameworkIDs.add (addBuildFile (path, fileRefID, false, false));
  578. frameworkFileIDs.add (fileRefID);
  579. }
  580. void addGroup (const String& groupID, const String& groupName, const StringArray& childIDs)
  581. {
  582. ValueTree* v = new ValueTree (groupID);
  583. v->setProperty ("isa", "PBXGroup", 0);
  584. v->setProperty ("children", "(" + indentList (childIDs, ",") + " )", 0);
  585. v->setProperty (Ids::name, groupName, 0);
  586. v->setProperty ("sourceTree", "<group>", 0);
  587. groups.add (v);
  588. }
  589. const String createGroup (const Array<RelativePath>& files, const String& groupName, const String& groupIDName, bool inhibitWarnings)
  590. {
  591. StringArray fileIDs;
  592. for (int i = 0; i < files.size(); ++i)
  593. {
  594. addFile (files.getReference(i), shouldFileBeCompiledByDefault (files.getReference(i)), inhibitWarnings);
  595. fileIDs.add (createID (files.getReference(i)));
  596. }
  597. const String groupID (createID (groupIDName));
  598. addGroup (groupID, groupName, fileIDs);
  599. return groupID;
  600. }
  601. const String addGroup (const Project::Item& item, StringArray& childIDs)
  602. {
  603. String groupName (item.getName().toString());
  604. if (item.isMainGroup())
  605. {
  606. groupName = "Source";
  607. // Add 'Juce Library Code' group
  608. if (juceWrapperFiles.size() > 0)
  609. childIDs.add (createGroup (juceWrapperFiles, project.getJuceCodeGroupName(), "__jucelibfiles", false));
  610. if (isVST())
  611. childIDs.add (createGroup (getVSTFilesRequired(), "Juce VST Wrapper", "__jucevstfiles", false));
  612. if (isAU())
  613. childIDs.add (createAUWrappersGroup());
  614. if (isRTAS())
  615. childIDs.add (createGroup (getRTASFilesRequired(), "Juce RTAS Wrapper", "__jucertasfiles", true));
  616. { // Add 'resources' group
  617. String resourcesGroupID (createID ("__resources"));
  618. addGroup (resourcesGroupID, "Resources", resourceFileRefs);
  619. childIDs.add (resourcesGroupID);
  620. }
  621. { // Add 'frameworks' group
  622. String frameworksGroupID (createID ("__frameworks"));
  623. addGroup (frameworksGroupID, "Frameworks", frameworkFileIDs);
  624. childIDs.add (frameworksGroupID);
  625. }
  626. { // Add 'products' group
  627. String productsGroupID (createID ("__products"));
  628. StringArray products;
  629. products.add (createID ("__productFileID"));
  630. addGroup (productsGroupID, "Products", products);
  631. childIDs.add (productsGroupID);
  632. }
  633. }
  634. String groupID (getIDForGroup (item));
  635. addGroup (groupID, groupName, childIDs);
  636. return groupID;
  637. }
  638. void addBuildProduct (const String& fileType, const String& binaryName)
  639. {
  640. ValueTree* v = new ValueTree (createID ("__productFileID"));
  641. v->setProperty ("isa", "PBXFileReference", 0);
  642. v->setProperty ("explicitFileType", fileType, 0);
  643. v->setProperty ("includeInIndex", (int) 0, 0);
  644. v->setProperty ("path", sanitisePath (binaryName), 0);
  645. v->setProperty ("sourceTree", "BUILT_PRODUCTS_DIR", 0);
  646. pbxFileReferences.add (v);
  647. }
  648. void addTargetConfig (const String& configName, const StringArray& buildSettings)
  649. {
  650. ValueTree* v = new ValueTree (createID ("targetconfigid_" + configName));
  651. v->setProperty ("isa", "XCBuildConfiguration", 0);
  652. v->setProperty ("buildSettings", "{" + indentList (buildSettings, ";") + " }", 0);
  653. v->setProperty (Ids::name, configName, 0);
  654. targetConfigs.add (v);
  655. }
  656. void addProjectConfig (const String& configName, const StringArray& buildSettings)
  657. {
  658. ValueTree* v = new ValueTree (createID ("projectconfigid_" + configName));
  659. v->setProperty ("isa", "XCBuildConfiguration", 0);
  660. v->setProperty ("buildSettings", "{" + indentList (buildSettings, ";") + " }", 0);
  661. v->setProperty (Ids::name, configName, 0);
  662. projectConfigs.add (v);
  663. }
  664. void addConfigList (const OwnedArray <ValueTree>& configsToUse, const String& listID)
  665. {
  666. StringArray configIDs;
  667. for (int i = 0; i < configsToUse.size(); ++i)
  668. configIDs.add (configsToUse[i]->getType().toString());
  669. ValueTree* v = new ValueTree (listID);
  670. v->setProperty ("isa", "XCConfigurationList", 0);
  671. v->setProperty ("buildConfigurations", "(" + indentList (configIDs, ",") + " )", 0);
  672. v->setProperty ("defaultConfigurationIsVisible", (int) 0, 0);
  673. if (configsToUse[0] != 0)
  674. v->setProperty ("defaultConfigurationName", configsToUse[0]->getProperty (Ids::name), 0);
  675. misc.add (v);
  676. }
  677. ValueTree* addBuildPhase (const String& phaseType, const StringArray& fileIds)
  678. {
  679. String phaseId (createID (phaseType + "resbuildphase"));
  680. buildPhaseIDs.add (phaseId);
  681. ValueTree* v = new ValueTree (phaseId);
  682. v->setProperty ("isa", phaseType, 0);
  683. v->setProperty ("buildActionMask", "2147483647", 0);
  684. v->setProperty ("files", "(" + indentList (fileIds, ",") + " )", 0);
  685. v->setProperty ("runOnlyForDeploymentPostprocessing", (int) 0, 0);
  686. misc.add (v);
  687. return v;
  688. }
  689. void addTargetObject()
  690. {
  691. ValueTree* v = new ValueTree (createID ("__target"));
  692. v->setProperty ("isa", "PBXNativeTarget", 0);
  693. v->setProperty ("buildConfigurationList", createID ("__configList"), 0);
  694. v->setProperty ("buildPhases", "(" + indentList (buildPhaseIDs, ",") + " )", 0);
  695. v->setProperty ("buildRules", "( )", 0);
  696. v->setProperty ("dependencies", "( )", 0);
  697. v->setProperty (Ids::name, project.getDocumentTitle(), 0);
  698. v->setProperty ("productName", project.getDocumentTitle(), 0);
  699. v->setProperty ("productReference", createID ("__productFileID"), 0);
  700. if (project.isGUIApplication())
  701. {
  702. v->setProperty ("productInstallPath", "$(HOME)/Applications", 0);
  703. v->setProperty ("productType", "com.apple.product-type.application", 0);
  704. }
  705. else if (project.isCommandLineApp())
  706. {
  707. v->setProperty ("productInstallPath", "/usr/bin", 0);
  708. v->setProperty ("productType", "com.apple.product-type.tool", 0);
  709. }
  710. else if (project.isAudioPlugin() || project.isBrowserPlugin())
  711. {
  712. v->setProperty ("productInstallPath", "$(HOME)/Library/Audio/Plug-Ins/Components/", 0);
  713. v->setProperty ("productType", "com.apple.product-type.bundle", 0);
  714. }
  715. else if (project.isLibrary())
  716. {
  717. v->setProperty ("productType", "com.apple.product-type.library.static", 0);
  718. }
  719. else
  720. jassertfalse; //xxx
  721. misc.add (v);
  722. }
  723. void addProjectObject()
  724. {
  725. ValueTree* v = new ValueTree (createID ("__root"));
  726. v->setProperty ("isa", "PBXProject", 0);
  727. v->setProperty ("buildConfigurationList", createID ("__projList"), 0);
  728. v->setProperty ("compatibilityVersion", "Xcode 3.0", 0);
  729. v->setProperty ("hasScannedForEncodings", (int) 0, 0);
  730. v->setProperty ("mainGroup", getIDForGroup (project.getMainGroup()), 0);
  731. v->setProperty ("projectDirPath", "\"\"", 0);
  732. v->setProperty ("projectRoot", "\"\"", 0);
  733. v->setProperty ("targets", "( " + createID ("__target") + " )", 0);
  734. misc.add (v);
  735. }
  736. void addPluginShellScriptPhase()
  737. {
  738. ValueTree* v = addBuildPhase ("PBXShellScriptBuildPhase", StringArray());
  739. v->setProperty (Ids::name, "Copy to the different plugin folders", 0);
  740. v->setProperty ("shellPath", "/bin/sh", 0);
  741. v->setProperty ("shellScript", String::fromUTF8 (BinaryData::AudioPluginXCodeScript_txt, BinaryData::AudioPluginXCodeScript_txtSize)
  742. .replace ("\\", "\\\\")
  743. .replace ("\"", "\\\"")
  744. .replace ("\r\n", "\\n")
  745. .replace ("\n", "\\n"), 0);
  746. }
  747. //==============================================================================
  748. static const String indentList (const StringArray& list, const String& separator)
  749. {
  750. if (list.size() == 0)
  751. return " ";
  752. return "\n\t\t\t\t" + list.joinIntoString (separator + "\n\t\t\t\t")
  753. + (separator == ";" ? separator : String::empty);
  754. }
  755. const String createID (const RelativePath& path) const
  756. {
  757. return createID (path.toUnixStyle());
  758. }
  759. const String createID (const String& rootString) const
  760. {
  761. static const char digits[] = "0123456789ABCDEF";
  762. char n[24];
  763. Random ran (projectIDSalt + hashCode64 (rootString));
  764. for (int i = 0; i < numElementsInArray (n); ++i)
  765. n[i] = digits [ran.nextInt (16)];
  766. return String (n, numElementsInArray (n));
  767. }
  768. const String getIDForGroup (const Project::Item& item) const
  769. {
  770. return createID (item.getID());
  771. }
  772. bool shouldFileBeCompiledByDefault (const RelativePath& file) const
  773. {
  774. return file.hasFileExtension ("cpp;mm;c;m");
  775. }
  776. //==============================================================================
  777. const Array<RelativePath> getRTASFilesRequired() const
  778. {
  779. Array<RelativePath> s;
  780. if (isRTAS())
  781. {
  782. const char* files[] = { "extras/audio plugins/wrapper/RTAS/juce_RTAS_DigiCode1.cpp",
  783. "extras/audio plugins/wrapper/RTAS/juce_RTAS_DigiCode2.cpp",
  784. "extras/audio plugins/wrapper/RTAS/juce_RTAS_DigiCode3.cpp",
  785. "extras/audio plugins/wrapper/RTAS/juce_RTAS_DigiCode_Header.h",
  786. "extras/audio plugins/wrapper/RTAS/juce_RTAS_MacResources.r",
  787. "extras/audio plugins/wrapper/RTAS/juce_RTAS_MacUtilities.mm",
  788. "extras/audio plugins/wrapper/RTAS/juce_RTAS_Wrapper.cpp" };
  789. for (int i = 0; i < numElementsInArray (files); ++i)
  790. s.add (getJucePathFromTargetFolder().getChildFile (files[i]));
  791. }
  792. return s;
  793. }
  794. const String createAUWrappersGroup()
  795. {
  796. Array<RelativePath> auWrappers;
  797. const char* files[] = { "extras/audio plugins/wrapper/AU/juce_AU_Resources.r",
  798. "extras/audio plugins/wrapper/AU/juce_AU_Wrapper.mm" };
  799. int i;
  800. for (i = 0; i < numElementsInArray (files); ++i)
  801. auWrappers.add (getJucePathFromTargetFolder().getChildFile (files[i]));
  802. const char* appleAUFiles[] = { "Extras/CoreAudio/PublicUtility/CADebugMacros.h",
  803. "Extras/CoreAudio/PublicUtility/CAAUParameter.cpp",
  804. "Extras/CoreAudio/PublicUtility/CAAUParameter.h",
  805. "Extras/CoreAudio/PublicUtility/CAAudioChannelLayout.cpp",
  806. "Extras/CoreAudio/PublicUtility/CAAudioChannelLayout.h",
  807. "Extras/CoreAudio/PublicUtility/CAMutex.cpp",
  808. "Extras/CoreAudio/PublicUtility/CAMutex.h",
  809. "Extras/CoreAudio/PublicUtility/CAStreamBasicDescription.cpp",
  810. "Extras/CoreAudio/PublicUtility/CAStreamBasicDescription.h",
  811. "Extras/CoreAudio/PublicUtility/CAVectorUnitTypes.h",
  812. "Extras/CoreAudio/PublicUtility/CAVectorUnit.cpp",
  813. "Extras/CoreAudio/PublicUtility/CAVectorUnit.h",
  814. "Extras/CoreAudio/AudioUnits/AUPublic/AUViewBase/AUViewLocalizedStringKeys.h",
  815. "Extras/CoreAudio/AudioUnits/AUPublic/AUCarbonViewBase/AUCarbonViewDispatch.cpp",
  816. "Extras/CoreAudio/AudioUnits/AUPublic/AUCarbonViewBase/AUCarbonViewControl.cpp",
  817. "Extras/CoreAudio/AudioUnits/AUPublic/AUCarbonViewBase/AUCarbonViewControl.h",
  818. "Extras/CoreAudio/AudioUnits/AUPublic/AUCarbonViewBase/CarbonEventHandler.cpp",
  819. "Extras/CoreAudio/AudioUnits/AUPublic/AUCarbonViewBase/CarbonEventHandler.h",
  820. "Extras/CoreAudio/AudioUnits/AUPublic/AUCarbonViewBase/AUCarbonViewBase.cpp",
  821. "Extras/CoreAudio/AudioUnits/AUPublic/AUCarbonViewBase/AUCarbonViewBase.h",
  822. "Extras/CoreAudio/AudioUnits/AUPublic/AUBase/AUBase.cpp",
  823. "Extras/CoreAudio/AudioUnits/AUPublic/AUBase/AUBase.h",
  824. "Extras/CoreAudio/AudioUnits/AUPublic/AUBase/AUDispatch.cpp",
  825. "Extras/CoreAudio/AudioUnits/AUPublic/AUBase/AUDispatch.h",
  826. "Extras/CoreAudio/AudioUnits/AUPublic/AUBase/AUInputElement.cpp",
  827. "Extras/CoreAudio/AudioUnits/AUPublic/AUBase/AUInputElement.h",
  828. "Extras/CoreAudio/AudioUnits/AUPublic/AUBase/AUOutputElement.cpp",
  829. "Extras/CoreAudio/AudioUnits/AUPublic/AUBase/AUOutputElement.h",
  830. "Extras/CoreAudio/AudioUnits/AUPublic/AUBase/AUResources.r",
  831. "Extras/CoreAudio/AudioUnits/AUPublic/AUBase/AUScopeElement.cpp",
  832. "Extras/CoreAudio/AudioUnits/AUPublic/AUBase/AUScopeElement.h",
  833. "Extras/CoreAudio/AudioUnits/AUPublic/AUBase/ComponentBase.cpp",
  834. "Extras/CoreAudio/AudioUnits/AUPublic/AUBase/ComponentBase.h",
  835. "Extras/CoreAudio/AudioUnits/AUPublic/OtherBases/AUMIDIBase.cpp",
  836. "Extras/CoreAudio/AudioUnits/AUPublic/OtherBases/AUMIDIBase.h",
  837. "Extras/CoreAudio/AudioUnits/AUPublic/OtherBases/AUMIDIEffectBase.cpp",
  838. "Extras/CoreAudio/AudioUnits/AUPublic/OtherBases/AUMIDIEffectBase.h",
  839. "Extras/CoreAudio/AudioUnits/AUPublic/OtherBases/AUOutputBase.cpp",
  840. "Extras/CoreAudio/AudioUnits/AUPublic/OtherBases/AUOutputBase.h",
  841. "Extras/CoreAudio/AudioUnits/AUPublic/OtherBases/MusicDeviceBase.cpp",
  842. "Extras/CoreAudio/AudioUnits/AUPublic/OtherBases/MusicDeviceBase.h",
  843. "Extras/CoreAudio/AudioUnits/AUPublic/OtherBases/AUEffectBase.cpp",
  844. "Extras/CoreAudio/AudioUnits/AUPublic/OtherBases/AUEffectBase.h",
  845. "Extras/CoreAudio/AudioUnits/AUPublic/Utility/AUBuffer.cpp",
  846. "Extras/CoreAudio/AudioUnits/AUPublic/Utility/AUBuffer.h",
  847. "Extras/CoreAudio/AudioUnits/AUPublic/Utility/AUDebugDispatcher.cpp",
  848. "Extras/CoreAudio/AudioUnits/AUPublic/Utility/AUDebugDispatcher.h",
  849. "Extras/CoreAudio/AudioUnits/AUPublic/Utility/AUInputFormatConverter.h",
  850. "Extras/CoreAudio/AudioUnits/AUPublic/Utility/AUSilentTimeout.h",
  851. "Extras/CoreAudio/AudioUnits/AUPublic/Utility/AUTimestampGenerator.h" };
  852. StringArray fileIDs, appleFileIDs;
  853. for (i = 0; i < auWrappers.size(); ++i)
  854. {
  855. addFile (auWrappers.getReference(i), shouldFileBeCompiledByDefault (auWrappers.getReference(i)), false);
  856. fileIDs.add (createID (auWrappers.getReference(i)));
  857. }
  858. for (i = 0; i < numElementsInArray (appleAUFiles); ++i)
  859. {
  860. RelativePath file (appleAUFiles[i], RelativePath::unknown);
  861. const String fileRefID (createID (file));
  862. addFileReference (file, "DEVELOPER_DIR", getFileType (file), fileRefID);
  863. if (shouldFileBeCompiledByDefault (file))
  864. addBuildFile (file, fileRefID, true, true);
  865. appleFileIDs.add (fileRefID);
  866. }
  867. const String appleGroupID (createID ("__juceappleaufiles"));
  868. addGroup (appleGroupID, "Apple AU Files", appleFileIDs);
  869. fileIDs.add (appleGroupID);
  870. const String groupID (createID ("__juceaufiles"));
  871. addGroup (groupID, "Juce AU Wrapper", fileIDs);
  872. return groupID;
  873. }
  874. };
  875. #endif // __JUCER_PROJECTEXPORT_XCODE_JUCEHEADER__