1 /* 2 ============================================================================== 3 4 This file is part of the JUCE library. 5 Copyright (c) 2020 - Raw Material Software Limited 6 7 JUCE is an open source library subject to commercial or open-source 8 licensing. 9 10 By using JUCE, you agree to the terms of both the JUCE 6 End-User License 11 Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). 12 13 End User License Agreement: www.juce.com/juce-6-licence 14 Privacy Policy: www.juce.com/juce-privacy-policy 15 16 Or: You may also use this code under the terms of the GPL v3 (see 17 www.gnu.org/licenses). 18 19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER 20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE 21 DISCLAIMED. 22 23 ============================================================================== 24 */ 25 26 #pragma once 27 28 #include "jucer_XcodeProjectParser.h" 29 30 //============================================================================== 31 namespace 32 { 33 static const char* const iOSDefaultVersion = "9.3"; 34 static const StringArray iOSVersions { "9.0", "9.1", "9.2", "9.3", "10.0", "10.1", "10.2", "10.3", 35 "11.0", "12.0", "13.0", "14.0" }; 36 37 enum class MacOSVersion 38 { 39 v10_7, 40 v10_8, 41 v10_9, 42 v10_10, 43 v10_11, 44 v10_12, 45 v10_13, 46 v10_14, 47 v10_15, 48 v10_16, 49 v11_0, 50 v11_1, 51 }; 52 getName(MacOSVersion m)53 static const char* getName (MacOSVersion m) 54 { 55 switch (m) 56 { 57 case MacOSVersion::v10_7: return "10.7"; 58 case MacOSVersion::v10_8: return "10.8"; 59 case MacOSVersion::v10_9: return "10.9"; 60 case MacOSVersion::v10_10: return "10.10"; 61 case MacOSVersion::v10_11: return "10.11"; 62 case MacOSVersion::v10_12: return "10.12"; 63 case MacOSVersion::v10_13: return "10.13"; 64 case MacOSVersion::v10_14: return "10.14"; 65 case MacOSVersion::v10_15: return "10.15"; 66 case MacOSVersion::v10_16: return "10.16"; 67 case MacOSVersion::v11_0: return "11.0"; 68 case MacOSVersion::v11_1: return "11.1"; 69 default: break; 70 } 71 72 jassertfalse; 73 return ""; 74 } 75 getDisplayName(MacOSVersion m)76 static String getDisplayName (MacOSVersion m) { return getName (m) + String (" SDK"); } getRootName(MacOSVersion m)77 static String getRootName (MacOSVersion m) { return String ("macosx") + getName (m); } 78 79 constexpr auto nextMacOSVersion = (MacOSVersion) ((int) MacOSVersion::v11_1 + 1); 80 constexpr auto oldestDeploymentTarget = MacOSVersion::v10_7; 81 constexpr auto macOSDefaultVersion = MacOSVersion::v10_11; 82 constexpr auto oldestSDKVersion = MacOSVersion::v10_11; 83 constexpr auto minimumAUv3SDKVersion = MacOSVersion::v10_11; 84 85 static MacOSVersion& operator++ (MacOSVersion& m) 86 { 87 return m = (MacOSVersion) ((int) m + 1); 88 } 89 getOSXSDKVersion(const String & sdkVersion)90 static String getOSXSDKVersion (const String& sdkVersion) 91 { 92 for (auto v = oldestSDKVersion; v != nextMacOSVersion; ++v) 93 if (sdkVersion == getDisplayName (v)) 94 return getRootName (v); 95 96 return "macosx"; 97 } 98 99 template <class ContainerType> getSDKChoiceList(MacOSVersion oldestVersion,bool displayName)100 static ContainerType getSDKChoiceList (MacOSVersion oldestVersion, bool displayName) 101 { 102 ContainerType container; 103 104 for (auto v = oldestVersion; v != nextMacOSVersion; ++v) 105 container.add (displayName ? getDisplayName (v) : getName (v)); 106 107 return container; 108 } 109 110 static const char* const osxArch_Default = "default"; 111 static const char* const osxArch_Native = "Native"; 112 static const char* const osxArch_32BitUniversal = "32BitUniversal"; 113 static const char* const osxArch_64BitUniversal = "64BitUniversal"; 114 static const char* const osxArch_64Bit = "64BitIntel"; 115 } 116 117 //============================================================================== 118 class XcodeProjectExporter : public ProjectExporter 119 { 120 public: 121 //============================================================================== getDisplayNameMac()122 static String getDisplayNameMac() { return "Xcode (macOS)"; } getDisplayNameiOS()123 static String getDisplayNameiOS() { return "Xcode (iOS)"; } 124 getTargetFolderNameMac()125 static String getTargetFolderNameMac() { return "MacOSX"; } getTargetFolderNameiOS()126 static String getTargetFolderNameiOS() { return "iOS"; } 127 getValueTreeTypeNameMac()128 static String getValueTreeTypeNameMac() { return "XCODE_MAC"; } getValueTreeTypeNameiOS()129 static String getValueTreeTypeNameiOS() { return "XCODE_IPHONE"; } 130 131 //============================================================================== XcodeProjectExporter(Project & p,const ValueTree & t,const bool isIOS)132 XcodeProjectExporter (Project& p, const ValueTree& t, const bool isIOS) 133 : ProjectExporter (p, t), 134 xcodeCanUseDwarf (true), 135 iOS (isIOS), 136 customPListValue (settings, Ids::customPList, getUndoManager()), 137 pListPrefixHeaderValue (settings, Ids::pListPrefixHeader, getUndoManager()), 138 pListPreprocessValue (settings, Ids::pListPreprocess, getUndoManager()), 139 subprojectsValue (settings, Ids::xcodeSubprojects, getUndoManager()), 140 validArchsValue (settings, Ids::xcodeValidArchs, getUndoManager(), getAllArchs(), ","), 141 extraFrameworksValue (settings, Ids::extraFrameworks, getUndoManager()), 142 frameworkSearchPathsValue (settings, Ids::frameworkSearchPaths, getUndoManager()), 143 extraCustomFrameworksValue (settings, Ids::extraCustomFrameworks, getUndoManager()), 144 embeddedFrameworksValue (settings, Ids::embeddedFrameworks, getUndoManager()), 145 postbuildCommandValue (settings, Ids::postbuildCommand, getUndoManager()), 146 prebuildCommandValue (settings, Ids::prebuildCommand, getUndoManager()), 147 duplicateAppExResourcesFolderValue (settings, Ids::duplicateAppExResourcesFolder, getUndoManager(), true), 148 iosDeviceFamilyValue (settings, Ids::iosDeviceFamily, getUndoManager(), "1,2"), 149 iPhoneScreenOrientationValue (settings, Ids::iPhoneScreenOrientation, getUndoManager(), getDefaultScreenOrientations(), ","), 150 iPadScreenOrientationValue (settings, Ids::iPadScreenOrientation, getUndoManager(), getDefaultScreenOrientations(), ","), 151 customXcodeResourceFoldersValue (settings, Ids::customXcodeResourceFolders, getUndoManager()), 152 customXcassetsFolderValue (settings, Ids::customXcassetsFolder, getUndoManager()), 153 appSandboxValue (settings, Ids::appSandbox, getUndoManager()), 154 appSandboxInheritanceValue (settings, Ids::appSandboxInheritance, getUndoManager()), 155 appSandboxOptionsValue (settings, Ids::appSandboxOptions, getUndoManager(), Array<var>(), ","), 156 hardenedRuntimeValue (settings, Ids::hardenedRuntime, getUndoManager()), 157 hardenedRuntimeOptionsValue (settings, Ids::hardenedRuntimeOptions, getUndoManager(), Array<var>(), ","), 158 microphonePermissionNeededValue (settings, Ids::microphonePermissionNeeded, getUndoManager()), 159 microphonePermissionsTextValue (settings, Ids::microphonePermissionsText, getUndoManager(), 160 "This app requires audio input. If you do not have an audio interface connected it will use the built-in microphone."), 161 cameraPermissionNeededValue (settings, Ids::cameraPermissionNeeded, getUndoManager()), 162 cameraPermissionTextValue (settings, Ids::cameraPermissionText, getUndoManager(), 163 "This app requires access to the camera to function correctly."), 164 bluetoothPermissionNeededValue (settings, Ids::iosBluetoothPermissionNeeded, getUndoManager()), 165 bluetoothPermissionTextValue (settings, Ids::iosBluetoothPermissionText, getUndoManager(), 166 "This app requires access to Bluetooth to function correctly."), 167 sendAppleEventsPermissionNeededValue (settings, Ids::sendAppleEventsPermissionNeeded, getUndoManager()), 168 sendAppleEventsPermissionTextValue (settings, Ids::sendAppleEventsPermissionText, getUndoManager(), 169 "This app requires the ability to send Apple events to function correctly."), 170 uiFileSharingEnabledValue (settings, Ids::UIFileSharingEnabled, getUndoManager()), 171 uiSupportsDocumentBrowserValue (settings, Ids::UISupportsDocumentBrowser, getUndoManager()), 172 uiStatusBarHiddenValue (settings, Ids::UIStatusBarHidden, getUndoManager()), 173 uiRequiresFullScreenValue (settings, Ids::UIRequiresFullScreen, getUndoManager(), true), 174 documentExtensionsValue (settings, Ids::documentExtensions, getUndoManager()), 175 iosInAppPurchasesValue (settings, Ids::iosInAppPurchases, getUndoManager()), 176 iosContentSharingValue (settings, Ids::iosContentSharing, getUndoManager(), true), 177 iosBackgroundAudioValue (settings, Ids::iosBackgroundAudio, getUndoManager()), 178 iosBackgroundBleValue (settings, Ids::iosBackgroundBle, getUndoManager()), 179 iosPushNotificationsValue (settings, Ids::iosPushNotifications, getUndoManager()), 180 iosAppGroupsValue (settings, Ids::iosAppGroups, getUndoManager()), 181 iCloudPermissionsValue (settings, Ids::iCloudPermissions, getUndoManager()), 182 iosDevelopmentTeamIDValue (settings, Ids::iosDevelopmentTeamID, getUndoManager()), 183 iosAppGroupsIDValue (settings, Ids::iosAppGroupsId, getUndoManager()), 184 keepCustomXcodeSchemesValue (settings, Ids::keepCustomXcodeSchemes, getUndoManager()), 185 useHeaderMapValue (settings, Ids::useHeaderMap, getUndoManager()), 186 customLaunchStoryboardValue (settings, Ids::customLaunchStoryboard, getUndoManager()), 187 exporterBundleIdentifierValue (settings, Ids::bundleIdentifier, getUndoManager()), 188 suppressPlistResourceUsageValue (settings, Ids::suppressPlistResourceUsage, getUndoManager()), 189 useLegacyBuildSystemValue (settings, Ids::useLegacyBuildSystem, getUndoManager()) 190 { 191 if (iOS) 192 { 193 name = getDisplayNameiOS(); 194 targetLocationValue.setDefault (getDefaultBuildsRootFolder() + getTargetFolderNameiOS()); 195 } 196 else 197 { 198 name = getDisplayNameMac(); 199 targetLocationValue.setDefault (getDefaultBuildsRootFolder() + getTargetFolderNameMac()); 200 } 201 } 202 createForSettings(Project & projectToUse,const ValueTree & settingsToUse)203 static XcodeProjectExporter* createForSettings (Project& projectToUse, const ValueTree& settingsToUse) 204 { 205 if (settingsToUse.hasType (getValueTreeTypeNameMac())) return new XcodeProjectExporter (projectToUse, settingsToUse, false); 206 if (settingsToUse.hasType (getValueTreeTypeNameiOS())) return new XcodeProjectExporter (projectToUse, settingsToUse, true); 207 208 return nullptr; 209 } 210 211 //============================================================================== getPListToMergeString()212 String getPListToMergeString() const { return customPListValue.get(); } getPListPrefixHeaderString()213 String getPListPrefixHeaderString() const { return pListPrefixHeaderValue.get(); } isPListPreprocessEnabled()214 bool isPListPreprocessEnabled() const { return pListPreprocessValue.get(); } 215 getSubprojectsString()216 String getSubprojectsString() const { return subprojectsValue.get(); } 217 getExtraFrameworksString()218 String getExtraFrameworksString() const { return extraFrameworksValue.get(); } getFrameworkSearchPathsString()219 String getFrameworkSearchPathsString() const { return frameworkSearchPathsValue.get(); } getExtraCustomFrameworksString()220 String getExtraCustomFrameworksString() const { return extraCustomFrameworksValue.get(); } getEmbeddedFrameworksString()221 String getEmbeddedFrameworksString() const { return embeddedFrameworksValue.get(); } 222 getPostBuildScript()223 String getPostBuildScript() const { return postbuildCommandValue.get(); } getPreBuildScript()224 String getPreBuildScript() const { return prebuildCommandValue.get(); } 225 shouldDuplicateAppExResourcesFolder()226 bool shouldDuplicateAppExResourcesFolder() const { return duplicateAppExResourcesFolderValue.get(); } 227 getDeviceFamilyString()228 String getDeviceFamilyString() const { return iosDeviceFamilyValue.get(); } 229 getDefaultScreenOrientations()230 Array<var> getDefaultScreenOrientations() const { return { "UIInterfaceOrientationPortrait", 231 "UIInterfaceOrientationLandscapeLeft", 232 "UIInterfaceOrientationLandscapeRight" }; } 233 getAllArchs()234 Array<var> getAllArchs() const { return { "i386", "x86_64", "arm64", "arm64e"}; } 235 getiPhoneScreenOrientations()236 Array<var> getiPhoneScreenOrientations() const { return *iPhoneScreenOrientationValue.get().getArray(); } getiPadScreenOrientations()237 Array<var> getiPadScreenOrientations() const { return *iPadScreenOrientationValue.get().getArray(); } 238 getCustomResourceFoldersString()239 String getCustomResourceFoldersString() const { return customXcodeResourceFoldersValue.get().toString().replaceCharacters ("\r\n", "::"); } getCustomXcassetsFolderString()240 String getCustomXcassetsFolderString() const { return customXcassetsFolderValue.get(); } getCustomLaunchStoryboardString()241 String getCustomLaunchStoryboardString() const { return customLaunchStoryboardValue.get(); } shouldAddStoryboardToProject()242 bool shouldAddStoryboardToProject() const { return getCustomLaunchStoryboardString().isNotEmpty() || getCustomXcassetsFolderString().isEmpty(); } 243 isHardenedRuntimeEnabled()244 bool isHardenedRuntimeEnabled() const { return hardenedRuntimeValue.get(); } getHardenedRuntimeOptions()245 Array<var> getHardenedRuntimeOptions() const { return *hardenedRuntimeOptionsValue.get().getArray(); } 246 isAppSandboxEnabled()247 bool isAppSandboxEnabled() const { return appSandboxValue.get(); } isAppSandboxInhertianceEnabled()248 bool isAppSandboxInhertianceEnabled() const { return appSandboxInheritanceValue.get(); } getAppSandboxOptions()249 Array<var> getAppSandboxOptions() const { return *appSandboxOptionsValue.get().getArray(); } 250 getValidArchs()251 Array<var> getValidArchs() const { return *validArchsValue.get().getArray(); } 252 isMicrophonePermissionEnabled()253 bool isMicrophonePermissionEnabled() const { return microphonePermissionNeededValue.get(); } getMicrophonePermissionsTextString()254 String getMicrophonePermissionsTextString() const { return microphonePermissionsTextValue.get(); } 255 isCameraPermissionEnabled()256 bool isCameraPermissionEnabled() const { return cameraPermissionNeededValue.get(); } getCameraPermissionTextString()257 String getCameraPermissionTextString() const { return cameraPermissionTextValue.get(); } 258 isBluetoothPermissionEnabled()259 bool isBluetoothPermissionEnabled() const { return bluetoothPermissionNeededValue.get(); } getBluetoothPermissionTextString()260 String getBluetoothPermissionTextString() const { return bluetoothPermissionTextValue.get(); } 261 isSendAppleEventsPermissionEnabled()262 bool isSendAppleEventsPermissionEnabled() const { return sendAppleEventsPermissionNeededValue.get(); } getSendAppleEventsPermissionTextString()263 String getSendAppleEventsPermissionTextString() const { return sendAppleEventsPermissionTextValue.get(); } 264 isInAppPurchasesEnabled()265 bool isInAppPurchasesEnabled() const { return iosInAppPurchasesValue.get(); } isContentSharingEnabled()266 bool isContentSharingEnabled() const { return iosContentSharingValue.get(); } isBackgroundAudioEnabled()267 bool isBackgroundAudioEnabled() const { return iosBackgroundAudioValue.get(); } isBackgroundBleEnabled()268 bool isBackgroundBleEnabled() const { return iosBackgroundBleValue.get(); } isPushNotificationsEnabled()269 bool isPushNotificationsEnabled() const { return iosPushNotificationsValue.get(); } isAppGroupsEnabled()270 bool isAppGroupsEnabled() const { return iosAppGroupsValue.get(); } isiCloudPermissionsEnabled()271 bool isiCloudPermissionsEnabled() const { return iCloudPermissionsValue.get(); } isFileSharingEnabled()272 bool isFileSharingEnabled() const { return uiFileSharingEnabledValue.get(); } isDocumentBrowserEnabled()273 bool isDocumentBrowserEnabled() const { return uiSupportsDocumentBrowserValue.get(); } isStatusBarHidden()274 bool isStatusBarHidden() const { return uiStatusBarHiddenValue.get(); } requiresFullScreen()275 bool requiresFullScreen() const { return uiRequiresFullScreenValue.get(); } 276 getSuppressPlistResourceUsage()277 bool getSuppressPlistResourceUsage() const { return suppressPlistResourceUsageValue.get(); } 278 shouldUseLegacyBuildSystem()279 bool shouldUseLegacyBuildSystem() const { return useLegacyBuildSystemValue.get(); } 280 getDocumentExtensionsString()281 String getDocumentExtensionsString() const { return documentExtensionsValue.get(); } 282 shouldKeepCustomXcodeSchemes()283 bool shouldKeepCustomXcodeSchemes() const { return keepCustomXcodeSchemesValue.get(); } 284 getDevelopmentTeamIDString()285 String getDevelopmentTeamIDString() const { return iosDevelopmentTeamIDValue.get(); } getAppGroupIdString()286 String getAppGroupIdString() const { return iosAppGroupsIDValue.get(); } 287 getDefaultLaunchStoryboardName()288 String getDefaultLaunchStoryboardName() const { return "LaunchScreen"; } 289 290 //============================================================================== usesMMFiles()291 bool usesMMFiles() const override { return true; } canCopeWithDuplicateFiles()292 bool canCopeWithDuplicateFiles() override { return true; } supportsUserDefinedConfigurations()293 bool supportsUserDefinedConfigurations() const override { return true; } 294 isXcode()295 bool isXcode() const override { return true; } isVisualStudio()296 bool isVisualStudio() const override { return false; } isCodeBlocks()297 bool isCodeBlocks() const override { return false; } isMakefile()298 bool isMakefile() const override { return false; } isAndroidStudio()299 bool isAndroidStudio() const override { return false; } isCLion()300 bool isCLion() const override { return false; } 301 isAndroid()302 bool isAndroid() const override { return false; } isWindows()303 bool isWindows() const override { return false; } isLinux()304 bool isLinux() const override { return false; } isOSX()305 bool isOSX() const override { return ! iOS; } isiOS()306 bool isiOS() const override { return iOS; } 307 supportsPrecompiledHeaders()308 bool supportsPrecompiledHeaders() const override { return true; } 309 getNewLineString()310 String getNewLineString() const override { return "\n"; } 311 supportsTargetType(build_tools::ProjectType::Target::Type type)312 bool supportsTargetType (build_tools::ProjectType::Target::Type type) const override 313 { 314 switch (type) 315 { 316 case build_tools::ProjectType::Target::AudioUnitv3PlugIn: 317 case build_tools::ProjectType::Target::StandalonePlugIn: 318 case build_tools::ProjectType::Target::GUIApp: 319 case build_tools::ProjectType::Target::StaticLibrary: 320 case build_tools::ProjectType::Target::DynamicLibrary: 321 case build_tools::ProjectType::Target::SharedCodeTarget: 322 case build_tools::ProjectType::Target::AggregateTarget: 323 return true; 324 case build_tools::ProjectType::Target::ConsoleApp: 325 case build_tools::ProjectType::Target::VSTPlugIn: 326 case build_tools::ProjectType::Target::VST3PlugIn: 327 case build_tools::ProjectType::Target::AAXPlugIn: 328 case build_tools::ProjectType::Target::RTASPlugIn: 329 case build_tools::ProjectType::Target::AudioUnitPlugIn: 330 case build_tools::ProjectType::Target::UnityPlugIn: 331 return ! iOS; 332 case build_tools::ProjectType::Target::unspecified: 333 default: 334 break; 335 } 336 337 return false; 338 } 339 createExporterProperties(PropertyListBuilder & props)340 void createExporterProperties (PropertyListBuilder& props) override 341 { 342 if (iOS) 343 { 344 props.add (new TextPropertyComponent (customXcassetsFolderValue, "Custom Xcassets Folder", 128, false), 345 "If this field is not empty, your Xcode project will use the custom xcassets folder specified here " 346 "for the app icons and launchimages, and will ignore the Icon files specified above. This will also prevent " 347 "a launch storyboard from being used."); 348 349 props.add (new TextPropertyComponent (customLaunchStoryboardValue, "Custom Launch Storyboard", 256, false), 350 "If this field is not empty then the specified launch storyboard file will be added to the project as an Xcode " 351 "resource and will be used for the app's launch screen, otherwise a default blank launch storyboard will be used. " 352 "The file path should be relative to the project folder."); 353 } 354 355 props.add (new TextPropertyComponent (customXcodeResourceFoldersValue, "Custom Xcode Resource Folders", 8192, true), 356 "You can specify a list of custom resource folders here (separated by newlines or whitespace). " 357 "References to these folders will then be added to the Xcode resources. " 358 "This way you can specify them for OS X and iOS separately, and modify the content of the resource folders " 359 "without re-saving the Projucer project."); 360 361 if (getProject().isAudioPluginProject()) 362 props.add (new ChoicePropertyComponent (duplicateAppExResourcesFolderValue, "Add Duplicate Resources Folder to App Extension"), 363 "Disable this to prevent the Projucer from creating a duplicate resources folder for AUv3 app extensions."); 364 365 if (iOS) 366 { 367 props.add (new ChoicePropertyComponent (iosDeviceFamilyValue, "Device Family", 368 { "iPhone", "iPad", "Universal" }, 369 { "1", "2", "1,2" }), 370 "The device family to target."); 371 372 { 373 StringArray orientationStrings { "Portrait", "Portrait Upside Down", 374 "Landscape Left", "Landscape Right" }; 375 376 Array<var> orientationVars { "UIInterfaceOrientationPortrait", "UIInterfaceOrientationPortraitUpsideDown", 377 "UIInterfaceOrientationLandscapeLeft", "UIInterfaceOrientationLandscapeRight" }; 378 379 props.add (new MultiChoicePropertyComponent (iPhoneScreenOrientationValue, "iPhone Screen Orientation", orientationStrings, orientationVars), 380 "The screen orientations that this app should support on iPhones."); 381 382 props.add (new MultiChoicePropertyComponent (iPadScreenOrientationValue, "iPad Screen Orientation", orientationStrings, orientationVars), 383 "The screen orientations that this app should support on iPads."); 384 } 385 386 props.add (new ChoicePropertyComponent (uiFileSharingEnabledValue, "File Sharing Enabled"), 387 "Enable this to expose your app's files to iTunes."); 388 389 props.add (new ChoicePropertyComponent (uiSupportsDocumentBrowserValue, "Support Document Browser"), 390 "Enable this to allow the user to access your app documents from a native file chooser."); 391 392 props.add (new ChoicePropertyComponent (uiStatusBarHiddenValue, "Status Bar Hidden"), 393 "Enable this to disable the status bar in your app."); 394 395 props.add (new ChoicePropertyComponent (uiRequiresFullScreenValue, "Requires Full Screen"), 396 "Disable this to enable non-fullscreen views such as Slide Over or Split View in your app. " 397 "You will also need to enable all orientations."); 398 } 399 else if (projectType.isGUIApplication()) 400 { 401 props.add (new TextPropertyComponent (documentExtensionsValue, "Document File Extensions", 128, false), 402 "A comma-separated list of file extensions for documents that your app can open. " 403 "Using a leading '.' is optional, and the extensions are not case-sensitive."); 404 } 405 406 props.add (new ChoicePropertyComponent (useLegacyBuildSystemValue, "Use Legacy Build System"), 407 "Enable this to use the deprecated \"Legacy Build System\" in Xcode 10 and above. " 408 "This may fix build issues that were introduced with the new build system in Xcode 10 and subsequently fixed in Xcode 10.2, " 409 "however the new build system is recommended for apps targeting Apple silicon."); 410 411 if (isOSX()) 412 { 413 props.add (new MultiChoicePropertyComponent (validArchsValue, "Valid Architectures", getAllArchs(), getAllArchs()), 414 "The full set of architectures which this project may target. " 415 "Each configuration will build for the intersection of this property, and the per-configuration macOS Architecture property"); 416 417 props.add (new ChoicePropertyComponent (appSandboxValue, "Use App Sandbox"), 418 "Enable this to use the app sandbox."); 419 420 props.add (new ChoicePropertyComponentWithEnablement (appSandboxInheritanceValue, appSandboxValue, "App Sandbox Inheritance"), 421 "If app sandbox is enabled, this setting will configure a child process to inherit the sandbox of its parent. " 422 "Note that if you enable this and have specified any other app sandbox entitlements below, the child process " 423 "will fail to launch."); 424 425 std::vector<std::pair<String, String>> sandboxOptions 426 { 427 { "Network: Incoming Connections (Server)", "network.server" }, 428 { "Network: Outgoing Connections (Client)", "network.client" }, 429 430 { "Hardware: Camera", "device.camera" }, 431 { "Hardware: Microphone", "device.microphone" }, 432 { "Hardware: USB", "device.usb" }, 433 { "Hardware: Printing", "print" }, 434 { "Hardware: Bluetooth", "device.bluetooth" }, 435 436 { "App Data: Contacts", "personal-information.addressbook" }, 437 { "App Data: Location", "personal-information.location" }, 438 { "App Data: Calendar", "personal-information.calendars" }, 439 440 { "File Access: User Selected File (Read Only)", "files.user-selected.read-only" }, 441 { "File Access: User Selected File (Read/Write)", "files.user-selected.read-write" }, 442 { "File Access: Downloads Folder (Read Only)", "files.downloads.read-only" }, 443 { "File Access: Downloads Folder (Read/Write)", "files.downloads.read-write" }, 444 { "File Access: Pictures Folder (Read Only)", "files.pictures.read-only" }, 445 { "File Access: Pictures Folder (Read/Write)", "files.pictures.read-write" }, 446 { "File Access: Music Folder (Read Only)", "assets.music.read-only" }, 447 { "File Access: Music Folder (Read/Write)", "assets.music.read-write" }, 448 { "File Access: Movies Folder (Read Only)", "assets.movies.read-only" }, 449 { "File Access: Movies Folder (Read/Write)", "assets.movies.read-write" }, 450 451 { "Temporary Exception: Audio Unit Hosting", "temporary-exception.audio-unit-host" }, 452 { "Temporary Exception: Global Mach Service", "temporary-exception.mach-lookup.global-name" }, 453 { "Temporary Exception: Global Mach Service Dynamic Registration", "temporary-exception.mach-register.global-name" }, 454 { "Temporary Exception: Home Directory File Access (Read Only)", "temporary-exception.files.home-relative-path.read-only" }, 455 { "Temporary Exception: Home Directory File Access (Read/Write)", "temporary-exception.files.home-relative-path.read-write" }, 456 { "Temporary Exception: Absolute Path File Access (Read Only)", "temporary-exception.files.absolute-path.read-only" }, 457 { "Temporary Exception: Absolute Path File Access (Read/Write)", "temporary-exception.files.absolute-path.read-write" }, 458 { "Temporary Exception: IOKit User Client Class", "temporary-exception.iokit-user-client-class" }, 459 { "Temporary Exception: Shared Preference Domain (Read Only)", "temporary-exception.shared-preference.read-only" }, 460 { "Temporary Exception: Shared Preference Domain (Read/Write)", "temporary-exception.shared-preference.read-write" } 461 }; 462 463 StringArray sandboxKeys; 464 Array<var> sanboxValues; 465 466 for (auto& opt : sandboxOptions) 467 { 468 sandboxKeys.add (opt.first); 469 sanboxValues.add ("com.apple.security." + opt.second); 470 } 471 472 props.add (new MultiChoicePropertyComponentWithEnablement (appSandboxOptionsValue, 473 appSandboxValue, 474 "App Sandbox Options", 475 sandboxKeys, 476 sanboxValues)); 477 478 props.add (new ChoicePropertyComponent (hardenedRuntimeValue, "Use Hardened Runtime"), 479 "Enable this to use the hardened runtime required for app notarization."); 480 481 std::vector<std::pair<String, String>> hardeningOptions 482 { 483 { "Runtime Exceptions: Allow Execution of JIT-compiled Code", "cs.allow-jit" }, 484 { "Runtime Exceptions: Allow Unsigned Executable Memory", "cs.allow-unsigned-executable-memory" }, 485 { "Runtime Exceptions: Allow DYLD Environment Variables", "cs.allow-dyld-environment-variables" }, 486 { "Runtime Exceptions: Disable Library Validation", "cs.disable-library-validation" }, 487 { "Runtime Exceptions: Disable Executable Memory Protection", "cs.disable-executable-page-protection" }, 488 { "Runtime Exceptions: Debugging Tool", "cs.debugger" }, 489 490 { "Resource Access: Audio Input", "device.audio-input" }, 491 { "Resource Access: Camera", "device.camera" }, 492 { "Resource Access: Location", "personal-information.location" }, 493 { "Resource Access: Address Book", "personal-information.addressbook" }, 494 { "Resource Access: Calendar", "personal-information.calendars" }, 495 { "Resource Access: Photos Library", "personal-information.photos-library" }, 496 { "Resource Access: Apple Events", "automation.apple-events" } 497 }; 498 499 StringArray hardeningKeys; 500 Array<var> hardeningValues; 501 502 for (auto& opt : hardeningOptions) 503 { 504 hardeningKeys.add (opt.first); 505 hardeningValues.add ("com.apple.security." + opt.second); 506 } 507 508 props.add (new MultiChoicePropertyComponentWithEnablement (hardenedRuntimeOptionsValue, 509 hardenedRuntimeValue, 510 "Hardened Runtime Options", 511 hardeningKeys, 512 hardeningValues)); 513 } 514 515 props.add (new ChoicePropertyComponent (microphonePermissionNeededValue, "Microphone Access"), 516 "Enable this to allow your app to use the microphone. " 517 "The user of your app will be prompted to grant microphone access permissions."); 518 519 props.add (new TextPropertyComponentWithEnablement (microphonePermissionsTextValue, microphonePermissionNeededValue, 520 "Microphone Access Text", 1024, false), 521 "A short description of why your app requires microphone access."); 522 523 props.add (new ChoicePropertyComponent (cameraPermissionNeededValue, "Camera Access"), 524 "Enable this to allow your app to use the camera. " 525 "The user of your app will be prompted to grant camera access permissions."); 526 527 props.add (new TextPropertyComponentWithEnablement (cameraPermissionTextValue, cameraPermissionNeededValue, 528 "Camera Access Text", 1024, false), 529 "A short description of why your app requires camera access."); 530 531 props.add (new ChoicePropertyComponent (bluetoothPermissionNeededValue, "Bluetooth Access"), 532 "Enable this to allow your app to use Bluetooth on iOS 13.0 and above, and macOS 11.0 and above. " 533 "The user of your app will be prompted to grant Bluetooth access permissions."); 534 535 props.add (new TextPropertyComponentWithEnablement (bluetoothPermissionTextValue, bluetoothPermissionNeededValue, 536 "Bluetooth Access Text", 1024, false), 537 "A short description of why your app requires Bluetooth access."); 538 539 if (! iOS) 540 { 541 props.add (new ChoicePropertyComponent (sendAppleEventsPermissionNeededValue, "Send Apple Events"), 542 "Enable this to allow your app to send Apple events. " 543 "The user of your app will be prompted to grant permissions to control other apps."); 544 545 props.add (new TextPropertyComponentWithEnablement (sendAppleEventsPermissionTextValue, sendAppleEventsPermissionNeededValue, 546 "Send Apple Events Text", 1024, false), 547 "A short description of why your app requires the ability to send Apple events."); 548 } 549 550 props.add (new ChoicePropertyComponent (iosInAppPurchasesValue, "In-App Purchases Capability"), 551 "Enable this to grant your app the capability for in-app purchases. " 552 "This option requires that you specify a valid Development Team ID."); 553 554 if (iOS) 555 { 556 props.add (new ChoicePropertyComponent (iosContentSharingValue, "Content Sharing"), 557 "Enable this to allow your app to share content with other apps."); 558 559 props.add (new ChoicePropertyComponent (iosBackgroundAudioValue, "Audio Background Capability"), 560 "Enable this to grant your app the capability to access audio when in background mode. " 561 "This permission is required if your app creates a MIDI input or output device."); 562 563 props.add (new ChoicePropertyComponent (iosBackgroundBleValue, "Bluetooth MIDI Background Capability"), 564 "Enable this to grant your app the capability to connect to Bluetooth LE devices when in background mode."); 565 566 props.add (new ChoicePropertyComponent (iosAppGroupsValue, "App Groups Capability"), 567 "Enable this to grant your app the capability to share resources between apps using the same app group ID."); 568 569 props.add (new ChoicePropertyComponent (iCloudPermissionsValue, "iCloud Permissions"), 570 "Enable this to grant your app the capability to use native file load/save browser windows on iOS."); 571 } 572 573 props.add (new ChoicePropertyComponent (iosPushNotificationsValue, "Push Notifications Capability"), 574 "Enable this to grant your app the capability to receive push notifications."); 575 576 props.add (new TextPropertyComponent (customPListValue, "Custom PList", 8192, true), 577 "You can paste the contents of an XML PList file in here, and the settings that it contains will override any " 578 "settings that the Projucer creates. BEWARE! When doing this, be careful to remove from the XML any " 579 "values that you DO want the Projucer to change!"); 580 581 props.add (new ChoicePropertyComponent (pListPreprocessValue, "PList Preprocess"), 582 "Enable this to preprocess PList file. This will allow you to set values to preprocessor defines," 583 " for instance if you define: #define MY_FLAG 1 in a prefix header file (see PList prefix header), you can have" 584 " a key with MY_FLAG value and it will be replaced with 1."); 585 586 props.add (new TextPropertyComponent (pListPrefixHeaderValue, "PList Prefix Header", 512, false), 587 "Header file containing definitions used in plist file (see PList Preprocess)."); 588 589 props.add (new ChoicePropertyComponent (suppressPlistResourceUsageValue, "Suppress AudioUnit Plist resourceUsage Key"), 590 "Suppress the resourceUsage key in the target's generated Plist. This is useful for AU" 591 " plugins that must access resources which cannot be declared in the resourceUsage block, such" 592 " as UNIX domain sockets. In particular, PACE-protected AU plugins may require this option to be enabled" 593 " in order for the plugin to load in GarageBand."); 594 595 props.add (new TextPropertyComponent (extraFrameworksValue, "Extra System Frameworks", 2048, false), 596 "A comma-separated list of extra system frameworks that should be added to the build. " 597 "(Don't include the .framework extension in the name)" 598 " The frameworks are expected to be located in /System/Library/Frameworks"); 599 600 props.add (new TextPropertyComponent (frameworkSearchPathsValue, "Framework Search Paths", 8192, true), 601 "A set of paths to search for custom frameworks (one per line)."); 602 603 props.add (new TextPropertyComponent (extraCustomFrameworksValue, "Extra Custom Frameworks", 8192, true), 604 "Paths to custom frameworks that should be added to the build (one per line). " 605 "You will probably need to add an entry to the Framework Search Paths for each unique directory."); 606 607 props.add (new TextPropertyComponent (embeddedFrameworksValue, "Embedded Frameworks", 8192, true), 608 "Paths to frameworks to be embedded with the app (one per line). " 609 "If you are adding a framework here then you do not need to specify it in Extra Custom Frameworks too. " 610 "You will probably need to add an entry to the Framework Search Paths for each unique directory."); 611 612 props.add (new TextPropertyComponent (subprojectsValue, "Xcode Subprojects", 8192, true), 613 "Paths to Xcode projects that should be added to the build (one per line). " 614 "These can be absolute or relative to the build directory. " 615 "The names of the required build products can be specified after a colon, comma separated, " 616 "e.g. \"path/to/MySubProject.xcodeproj: MySubProject, OtherTarget\". " 617 "If no build products are specified, all build products associated with a subproject will be added."); 618 619 props.add (new TextPropertyComponent (prebuildCommandValue, "Pre-Build Shell Script", 32768, true), 620 "Some shell-script that will be run before a build starts."); 621 622 props.add (new TextPropertyComponent (postbuildCommandValue, "Post-Build Shell Script", 32768, true), 623 "Some shell-script that will be run after a build completes."); 624 625 props.add (new TextPropertyComponent (exporterBundleIdentifierValue, "Exporter Bundle Identifier", 256, false), 626 "Use this to override the project bundle identifier for this exporter. " 627 "This is useful if you want to use different bundle identifiers for Mac and iOS exporters in the same project."); 628 629 props.add (new TextPropertyComponent (iosDevelopmentTeamIDValue, "Development Team ID", 10, false), 630 "The Development Team ID to be used for setting up code-signing your app. This is a ten-character " 631 "string (for example, \"S7B6T5XJ2Q\") that describes the distribution certificate Apple issued to you. " 632 "You can find this string in the OS X app Keychain Access under \"Certificates\"."); 633 634 if (iOS) 635 props.add (new TextPropertyComponentWithEnablement (iosAppGroupsIDValue, iosAppGroupsValue, "App Group ID", 256, false), 636 "The App Group ID to be used for allowing multiple apps to access a shared resource folder. Multiple IDs can be " 637 "added separated by a semicolon."); 638 639 props.add (new ChoicePropertyComponent (keepCustomXcodeSchemesValue, "Keep Custom Xcode Schemes"), 640 "Enable this to keep any Xcode schemes you have created for debugging or running, e.g. to launch a plug-in in" 641 "various hosts. If disabled, all schemes are replaced by a default set."); 642 643 props.add (new ChoicePropertyComponent (useHeaderMapValue, "USE_HEADERMAP"), 644 "Enable this to make Xcode search all the projects folders for include files. This means you can be lazy " 645 "and not bother using relative paths to include your headers, but it means your code won't be " 646 "compatible with other build systems"); 647 } 648 launchProject()649 bool launchProject() override 650 { 651 #if JUCE_MAC 652 return getProjectBundle().startAsProcess(); 653 #else 654 return false; 655 #endif 656 } 657 canLaunchProject()658 bool canLaunchProject() override 659 { 660 #if JUCE_MAC 661 return true; 662 #else 663 return false; 664 #endif 665 } 666 667 //============================================================================== create(const OwnedArray<LibraryModule> &)668 void create (const OwnedArray<LibraryModule>&) const override 669 { 670 for (auto& target : targets) 671 if (target->shouldCreatePList()) 672 target->infoPlistFile = getTargetFolder().getChildFile (target->getInfoPlistName()); 673 674 menuNibFile = getTargetFolder().getChildFile ("RecentFilesMenuTemplate.nib"); 675 676 createIconFile(); 677 678 auto projectBundle = getProjectBundle(); 679 createDirectoryOrThrow (projectBundle); 680 681 createObjects(); 682 683 build_tools::writeStreamToFile (projectBundle.getChildFile ("project.pbxproj"), 684 [this] (MemoryOutputStream& mo) { writeProjectFile (mo); }); 685 686 writeInfoPlistFiles(); 687 writeWorkspaceSettings(); 688 689 // Deleting the .rsrc files can be needed to force Xcode to update the version number. 690 deleteRsrcFiles (getTargetFolder().getChildFile ("build")); 691 } 692 693 //============================================================================== addPlatformSpecificSettingsForProjectType(const build_tools::ProjectType &)694 void addPlatformSpecificSettingsForProjectType (const build_tools::ProjectType&) override 695 { 696 callForAllSupportedTargets ([this] (build_tools::ProjectType::Target::Type targetType) 697 { 698 targets.insert (targetType == build_tools::ProjectType::Target::AggregateTarget ? 0 : -1, 699 new XcodeTarget (targetType, *this)); 700 }); 701 702 // If you hit this assert, you tried to generate a project for an exporter 703 // that does not support any of your targets! 704 jassert (targets.size() > 0); 705 } 706 updateDeprecatedSettings()707 void updateDeprecatedSettings() override 708 { 709 if (iOS) 710 updateOldOrientationSettings(); 711 } 712 updateDeprecatedSettingsInteractively()713 void updateDeprecatedSettingsInteractively() override 714 { 715 if (hasInvalidPostBuildScript()) 716 { 717 String alertWindowText = iOS ? "Your Xcode (iOS) Exporter settings use an invalid post-build script. Click 'Update' to remove it." 718 : "Your Xcode (macOS) Exporter settings use a pre-JUCE 4.2 post-build script to move the plug-in binaries to their plug-in install folders.\n\n" 719 "Since JUCE 4.2, this is instead done using \"AU/VST/VST2/AAX/RTAS Binary Location\" in the Xcode (OS X) configuration settings.\n\n" 720 "Click 'Update' to remove the script (otherwise your plug-in may not compile correctly)."; 721 722 if (AlertWindow::showOkCancelBox (AlertWindow::WarningIcon, 723 "Project settings: " + project.getDocumentTitle(), 724 alertWindowText, "Update", "Cancel", nullptr, nullptr)) 725 postbuildCommandValue.resetToDefault(); 726 } 727 } 728 hasInvalidPostBuildScript()729 bool hasInvalidPostBuildScript() const 730 { 731 // check whether the script is identical to the old one that the Introjucer used to auto-generate 732 return (MD5 (getPostBuildScript().toUTF8()).toHexString() == "265ac212a7e734c5bbd6150e1eae18a1"); 733 } 734 735 //============================================================================== initialiseDependencyPathValues()736 void initialiseDependencyPathValues() override 737 { 738 vstLegacyPathValueWrapper.init ({ settings, Ids::vstLegacyFolder, nullptr }, 739 getAppSettings().getStoredPath (Ids::vstLegacyPath, TargetOS::osx), TargetOS::osx); 740 741 aaxPathValueWrapper.init ({ settings, Ids::aaxFolder, nullptr }, 742 getAppSettings().getStoredPath (Ids::aaxPath, TargetOS::osx), TargetOS::osx); 743 744 rtasPathValueWrapper.init ({ settings, Ids::rtasFolder, nullptr }, 745 getAppSettings().getStoredPath (Ids::rtasPath, TargetOS::osx), TargetOS::osx); 746 } 747 748 protected: 749 //============================================================================== 750 class XcodeBuildConfiguration : public BuildConfiguration 751 { 752 public: XcodeBuildConfiguration(Project & p,const ValueTree & t,const bool isIOS,const ProjectExporter & e)753 XcodeBuildConfiguration (Project& p, const ValueTree& t, const bool isIOS, const ProjectExporter& e) 754 : BuildConfiguration (p, t, e), 755 iOS (isIOS), 756 osxSDKVersion (config, Ids::osxSDK, getUndoManager()), 757 osxDeploymentTarget (config, Ids::osxCompatibility, getUndoManager(), getDisplayName (macOSDefaultVersion)), 758 iosDeploymentTarget (config, Ids::iosCompatibility, getUndoManager(), iOSDefaultVersion), 759 osxArchitecture (config, Ids::osxArchitecture, getUndoManager(), osxArch_Default), 760 customXcodeFlags (config, Ids::customXcodeFlags, getUndoManager()), 761 plistPreprocessorDefinitions (config, Ids::plistPreprocessorDefinitions, getUndoManager()), 762 codeSignIdentity (config, Ids::codeSigningIdentity, getUndoManager()), 763 fastMathEnabled (config, Ids::fastMath, getUndoManager()), 764 stripLocalSymbolsEnabled (config, Ids::stripLocalSymbols, getUndoManager()), 765 pluginBinaryCopyStepEnabled (config, Ids::enablePluginBinaryCopyStep, getUndoManager(), true), 766 vstBinaryLocation (config, Ids::vstBinaryLocation, getUndoManager(), "$(HOME)/Library/Audio/Plug-Ins/VST/"), 767 vst3BinaryLocation (config, Ids::vst3BinaryLocation, getUndoManager(), "$(HOME)/Library/Audio/Plug-Ins/VST3/"), 768 auBinaryLocation (config, Ids::auBinaryLocation, getUndoManager(), "$(HOME)/Library/Audio/Plug-Ins/Components/"), 769 rtasBinaryLocation (config, Ids::rtasBinaryLocation, getUndoManager(), "/Library/Application Support/Digidesign/Plug-Ins/"), 770 aaxBinaryLocation (config, Ids::aaxBinaryLocation, getUndoManager(), "/Library/Application Support/Avid/Audio/Plug-Ins/"), 771 unityPluginBinaryLocation (config, Ids::unityPluginBinaryLocation, getUndoManager()) 772 { 773 updateOldPluginBinaryLocations(); 774 updateOldSDKDefaults(); 775 776 optimisationLevelValue.setDefault (isDebug() ? gccO0 : gccO3); 777 } 778 779 //============================================================================== createConfigProperties(PropertyListBuilder & props)780 void createConfigProperties (PropertyListBuilder& props) override 781 { 782 if (project.isAudioPluginProject()) 783 addXcodePluginInstallPathProperties (props); 784 785 addRecommendedLLVMCompilerWarningsProperty (props); 786 addGCCOptimisationProperty (props); 787 788 if (iOS) 789 { 790 Array<var> iOSVersionVars; 791 792 for (auto& s : iOSVersions) 793 iOSVersionVars.add (s); 794 795 props.add (new ChoicePropertyComponent (iosDeploymentTarget, "iOS Deployment Target", iOSVersions, iOSVersionVars), 796 "The minimum version of iOS that the target binary will run on."); 797 } 798 else 799 { 800 props.add (new ChoicePropertyComponent (osxSDKVersion, "macOS Base SDK Version", getSDKChoiceList<StringArray> (oldestSDKVersion, true), 801 getSDKChoiceList<Array<var>> (oldestSDKVersion, true)), 802 "The version of the macOS SDK to link against. If \"Default\" is selected then the Xcode default will be used."); 803 804 props.add (new ChoicePropertyComponent (osxDeploymentTarget, "macOS Deployment Target", getSDKChoiceList<StringArray> (oldestDeploymentTarget, false), 805 getSDKChoiceList<Array<var>> (oldestDeploymentTarget, true)), 806 "The minimum version of macOS that the target binary will be compatible with."); 807 808 props.add (new ChoicePropertyComponent (osxArchitecture, "macOS Architecture", 809 { "Native architecture of build machine", "Standard 32-bit", "Standard 32/64-bit", "Standard 64-bit" }, 810 { osxArch_Native, osxArch_32BitUniversal, osxArch_64BitUniversal, osxArch_64Bit }), 811 "The type of macOS binary that will be produced."); 812 } 813 814 props.add (new TextPropertyComponent (customXcodeFlags, "Custom Xcode Flags", 8192, true), 815 "A comma-separated list of custom Xcode setting flags which will be appended to the list of generated flags, " 816 "e.g. MACOSX_DEPLOYMENT_TARGET_i386 = 10.5"); 817 818 props.add (new TextPropertyComponent (plistPreprocessorDefinitions, "PList Preprocessor Definitions", 2048, true), 819 "Preprocessor definitions used during PList preprocessing (see PList Preprocess)."); 820 821 props.add (new TextPropertyComponent (codeSignIdentity, "Code-Signing Identity", 1024, false), 822 "The name of a code-signing identity for Xcode to apply."); 823 824 props.add (new ChoicePropertyComponent (fastMathEnabled, "Relax IEEE Compliance"), 825 "Enable this to use FAST_MATH non-IEEE mode. (Warning: this can have unexpected results!)"); 826 827 props.add (new ChoicePropertyComponent (stripLocalSymbolsEnabled, "Strip Local Symbols"), 828 "Enable this to strip any locally defined symbols resulting in a smaller binary size. Enabling this " 829 "will also remove any function names from crash logs. Must be disabled for static library projects. " 830 "Note that disabling this will not necessarily generate full debug symbols. For release configs, " 831 "you will also need to add the following to the \"Custom Xcode Flags\" field: " 832 "GCC_GENERATE_DEBUGGING_SYMBOLS = YES, STRIP_INSTALLED_PRODUCT = NO, COPY_PHASE_STRIP = NO"); 833 } 834 getModuleLibraryArchName()835 String getModuleLibraryArchName() const override 836 { 837 return "${CURRENT_ARCH}"; 838 } 839 840 //============================================================================== getOSXArchitectureString()841 String getOSXArchitectureString() const { return osxArchitecture.get(); } getPListPreprocessorDefinitionsString()842 String getPListPreprocessorDefinitionsString() const { return plistPreprocessorDefinitions.get(); } 843 isFastMathEnabled()844 bool isFastMathEnabled() const { return fastMathEnabled.get(); } 845 isStripLocalSymbolsEnabled()846 bool isStripLocalSymbolsEnabled() const { return stripLocalSymbolsEnabled.get(); } 847 getCustomXcodeFlagsString()848 String getCustomXcodeFlagsString() const { return customXcodeFlags.get(); } 849 getOSXSDKVersionString()850 String getOSXSDKVersionString() const { return osxSDKVersion.get(); } getOSXDeploymentTargetString()851 String getOSXDeploymentTargetString() const { return osxDeploymentTarget.get(); } 852 getCodeSignIdentityString()853 String getCodeSignIdentityString() const { return codeSignIdentity.get(); } 854 getiOSDeploymentTargetString()855 String getiOSDeploymentTargetString() const { return iosDeploymentTarget.get(); } 856 isPluginBinaryCopyStepEnabled()857 bool isPluginBinaryCopyStepEnabled() const { return pluginBinaryCopyStepEnabled.get(); } getVSTBinaryLocationString()858 String getVSTBinaryLocationString() const { return vstBinaryLocation.get(); } getVST3BinaryLocationString()859 String getVST3BinaryLocationString() const { return vst3BinaryLocation.get(); } getAUBinaryLocationString()860 String getAUBinaryLocationString() const { return auBinaryLocation.get(); } getRTASBinaryLocationString()861 String getRTASBinaryLocationString() const { return rtasBinaryLocation.get();} getAAXBinaryLocationString()862 String getAAXBinaryLocationString() const { return aaxBinaryLocation.get();} getUnityPluginBinaryLocationString()863 String getUnityPluginBinaryLocationString() const { return unityPluginBinaryLocation.get(); } 864 865 private: 866 //============================================================================== 867 bool iOS; 868 869 ValueWithDefault osxSDKVersion, osxDeploymentTarget, iosDeploymentTarget, osxArchitecture, 870 customXcodeFlags, plistPreprocessorDefinitions, codeSignIdentity, 871 fastMathEnabled, stripLocalSymbolsEnabled, pluginBinaryCopyStepEnabled, 872 vstBinaryLocation, vst3BinaryLocation, auBinaryLocation, rtasBinaryLocation, 873 aaxBinaryLocation, unityPluginBinaryLocation; 874 875 //============================================================================== addXcodePluginInstallPathProperties(PropertyListBuilder & props)876 void addXcodePluginInstallPathProperties (PropertyListBuilder& props) 877 { 878 auto isBuildingAnyPlugins = (project.shouldBuildVST() || project.shouldBuildVST3() || project.shouldBuildAU() 879 || project.shouldBuildRTAS() || project.shouldBuildAAX() || project.shouldBuildUnityPlugin()); 880 881 if (isBuildingAnyPlugins) 882 props.add (new ChoicePropertyComponent (pluginBinaryCopyStepEnabled, "Enable Plugin Copy Step"), 883 "Enable this to copy plugin binaries to the specified folder after building."); 884 885 if (project.shouldBuildVST3()) 886 props.add (new TextPropertyComponentWithEnablement (vst3BinaryLocation, pluginBinaryCopyStepEnabled, "VST3 Binary Location", 887 1024, false), 888 "The folder in which the compiled VST3 binary should be placed."); 889 890 if (project.shouldBuildAU()) 891 props.add (new TextPropertyComponentWithEnablement (auBinaryLocation, pluginBinaryCopyStepEnabled, "AU Binary Location", 892 1024, false), 893 "The folder in which the compiled AU binary should be placed."); 894 895 if (project.shouldBuildRTAS()) 896 props.add (new TextPropertyComponentWithEnablement (rtasBinaryLocation, pluginBinaryCopyStepEnabled, "RTAS Binary Location", 897 1024, false), 898 "The folder in which the compiled RTAS binary should be placed."); 899 900 if (project.shouldBuildAAX()) 901 props.add (new TextPropertyComponentWithEnablement (aaxBinaryLocation, pluginBinaryCopyStepEnabled, "AAX Binary Location", 902 1024, false), 903 "The folder in which the compiled AAX binary should be placed."); 904 905 if (project.shouldBuildUnityPlugin()) 906 props.add (new TextPropertyComponentWithEnablement (unityPluginBinaryLocation, pluginBinaryCopyStepEnabled, "Unity Binary Location", 907 1024, false), 908 "The folder in which the compiled Unity plugin binary and associated C# GUI script should be placed."); 909 910 if (project.shouldBuildVST()) 911 props.add (new TextPropertyComponentWithEnablement (vstBinaryLocation, pluginBinaryCopyStepEnabled, "VST Binary Location", 912 1024, false), 913 "The folder in which the compiled legacy VST binary should be placed."); 914 } 915 updateOldPluginBinaryLocations()916 void updateOldPluginBinaryLocations() 917 { 918 if (! config ["xcodeVstBinaryLocation"].isVoid()) vstBinaryLocation = config ["xcodeVstBinaryLocation"]; 919 if (! config ["xcodeVst3BinaryLocation"].isVoid()) vst3BinaryLocation = config ["xcodeVst3BinaryLocation"]; 920 if (! config ["xcodeAudioUnitBinaryLocation"].isVoid()) auBinaryLocation = config ["xcodeAudioUnitBinaryLocation"]; 921 if (! config ["xcodeRtasBinaryLocation"].isVoid()) rtasBinaryLocation = config ["xcodeRtasBinaryLocation"]; 922 if (! config ["xcodeAaxBinaryLocation"].isVoid()) aaxBinaryLocation = config ["xcodeAaxBinaryLocation"]; 923 } 924 updateOldSDKDefaults()925 void updateOldSDKDefaults() 926 { 927 if (iosDeploymentTarget.get() == "default") iosDeploymentTarget.resetToDefault(); 928 if (osxArchitecture.get() == "default") osxArchitecture.resetToDefault(); 929 if (osxSDKVersion.get() == "default") osxSDKVersion.resetToDefault(); 930 if (osxDeploymentTarget.get() == "default") osxDeploymentTarget.resetToDefault(); 931 } 932 }; 933 createBuildConfig(const ValueTree & v)934 BuildConfiguration::Ptr createBuildConfig (const ValueTree& v) const override 935 { 936 return *new XcodeBuildConfiguration (project, v, iOS, *this); 937 } 938 939 public: 940 //============================================================================== 941 /* The numbers for these enum values are defined by Xcode for the different 942 possible destinations of a "copy files" post-build step. 943 */ 944 enum XcodeCopyFilesDestinationIDs 945 { 946 kWrapperFolder = 1, 947 kExecutablesFolder = 6, 948 kResourcesFolder = 7, 949 kFrameworksFolder = 10, 950 kSharedFrameworksFolder = 11, 951 kSharedSupportFolder = 12, 952 kPluginsFolder = 13, 953 kJavaResourcesFolder = 15, 954 kXPCServicesFolder = 16 955 }; 956 957 //============================================================================== 958 struct XcodeTarget : build_tools::ProjectType::Target 959 { 960 //============================================================================== XcodeTargetXcodeTarget961 XcodeTarget (build_tools::ProjectType::Target::Type targetType, const XcodeProjectExporter& exporter) 962 : Target (targetType), 963 owner (exporter) 964 { 965 switch (type) 966 { 967 case GUIApp: 968 xcodeFileType = "wrapper.application"; 969 xcodeBundleExtension = ".app"; 970 xcodeProductType = "com.apple.product-type.application"; 971 xcodeCopyToProductInstallPathAfterBuild = false; 972 break; 973 974 case ConsoleApp: 975 xcodeFileType = "compiled.mach-o.executable"; 976 xcodeBundleExtension = String(); 977 xcodeProductType = "com.apple.product-type.tool"; 978 xcodeCopyToProductInstallPathAfterBuild = false; 979 break; 980 981 case StaticLibrary: 982 xcodeFileType = "archive.ar"; 983 xcodeBundleExtension = ".a"; 984 xcodeProductType = "com.apple.product-type.library.static"; 985 xcodeCopyToProductInstallPathAfterBuild = false; 986 break; 987 988 case DynamicLibrary: 989 xcodeFileType = "compiled.mach-o.dylib"; 990 xcodeProductType = "com.apple.product-type.library.dynamic"; 991 xcodeBundleExtension = ".dylib"; 992 xcodeCopyToProductInstallPathAfterBuild = false; 993 break; 994 995 case VSTPlugIn: 996 xcodeFileType = "wrapper.cfbundle"; 997 xcodeBundleExtension = ".vst"; 998 xcodeProductType = "com.apple.product-type.bundle"; 999 xcodeCopyToProductInstallPathAfterBuild = true; 1000 break; 1001 1002 case VST3PlugIn: 1003 xcodeFileType = "wrapper.cfbundle"; 1004 xcodeBundleExtension = ".vst3"; 1005 xcodeProductType = "com.apple.product-type.bundle"; 1006 xcodeCopyToProductInstallPathAfterBuild = true; 1007 break; 1008 1009 case AudioUnitPlugIn: 1010 xcodeFileType = "wrapper.cfbundle"; 1011 xcodeBundleExtension = ".component"; 1012 xcodeProductType = "com.apple.product-type.bundle"; 1013 xcodeCopyToProductInstallPathAfterBuild = true; 1014 1015 addExtraAudioUnitTargetSettings(); 1016 break; 1017 1018 case StandalonePlugIn: 1019 xcodeFileType = "wrapper.application"; 1020 xcodeBundleExtension = ".app"; 1021 xcodeProductType = "com.apple.product-type.application"; 1022 xcodeCopyToProductInstallPathAfterBuild = false; 1023 break; 1024 1025 case AudioUnitv3PlugIn: 1026 xcodeFileType = "wrapper.app-extension"; 1027 xcodeBundleExtension = ".appex"; 1028 xcodeBundleIDSubPath = "AUv3"; 1029 xcodeProductType = "com.apple.product-type.app-extension"; 1030 xcodeCopyToProductInstallPathAfterBuild = false; 1031 1032 addExtraAudioUnitv3PlugInTargetSettings(); 1033 break; 1034 1035 case AAXPlugIn: 1036 xcodeFileType = "wrapper.cfbundle"; 1037 xcodeBundleExtension = ".aaxplugin"; 1038 xcodeProductType = "com.apple.product-type.bundle"; 1039 xcodeCopyToProductInstallPathAfterBuild = true; 1040 break; 1041 1042 case RTASPlugIn: 1043 xcodeFileType = "wrapper.cfbundle"; 1044 xcodeBundleExtension = ".dpm"; 1045 xcodeProductType = "com.apple.product-type.bundle"; 1046 xcodeCopyToProductInstallPathAfterBuild = true; 1047 break; 1048 1049 case UnityPlugIn: 1050 xcodeFileType = "wrapper.cfbundle"; 1051 xcodeBundleExtension = ".bundle"; 1052 xcodeProductType = "com.apple.product-type.bundle"; 1053 xcodeCopyToProductInstallPathAfterBuild = true; 1054 break; 1055 1056 case SharedCodeTarget: 1057 xcodeFileType = "archive.ar"; 1058 xcodeBundleExtension = ".a"; 1059 xcodeProductType = "com.apple.product-type.library.static"; 1060 xcodeCopyToProductInstallPathAfterBuild = false; 1061 break; 1062 1063 case AggregateTarget: 1064 xcodeCopyToProductInstallPathAfterBuild = false; 1065 break; 1066 1067 case unspecified: 1068 default: 1069 // unknown target type! 1070 jassertfalse; 1071 break; 1072 } 1073 } 1074 getXcodeSchemeNameXcodeTarget1075 String getXcodeSchemeName() const 1076 { 1077 return owner.projectName + " - " + getName(); 1078 } 1079 getIDXcodeTarget1080 String getID() const 1081 { 1082 return owner.createID (String ("__target") + getName()); 1083 } 1084 getInfoPlistNameXcodeTarget1085 String getInfoPlistName() const 1086 { 1087 return String ("Info-") + String (getName()).replace (" ", "_") + String (".plist"); 1088 } 1089 getEntitlementsFilenameXcodeTarget1090 String getEntitlementsFilename() const 1091 { 1092 return String (getName()).replace (" ", "_") + String (".entitlements"); 1093 } 1094 1095 String xcodeBundleExtension; 1096 String xcodeProductType, xcodeFileType; 1097 String xcodeOtherRezFlags, xcodeBundleIDSubPath; 1098 bool xcodeCopyToProductInstallPathAfterBuild; 1099 StringArray xcodeFrameworks, xcodeLibs; 1100 Array<XmlElement> xcodeExtraPListEntries; 1101 1102 StringArray frameworkIDs, buildPhaseIDs, configIDs, sourceIDs, rezFileIDs, dependencyIDs; 1103 StringArray frameworkNames; 1104 String mainBuildProductID; 1105 File infoPlistFile; 1106 1107 struct SourceFileInfo 1108 { 1109 build_tools::RelativePath path; 1110 bool shouldBeCompiled = false; 1111 }; 1112 getSourceFilesInfoXcodeTarget1113 Array<SourceFileInfo> getSourceFilesInfo (const Project::Item& projectItem) const 1114 { 1115 Array<SourceFileInfo> result; 1116 1117 auto targetType = (owner.getProject().isAudioPluginProject() ? type : SharedCodeTarget); 1118 1119 if (projectItem.isGroup()) 1120 { 1121 for (int i = 0; i < projectItem.getNumChildren(); ++i) 1122 result.addArray (getSourceFilesInfo (projectItem.getChild (i))); 1123 } 1124 else if (projectItem.shouldBeAddedToTargetProject() && projectItem.shouldBeAddedToTargetExporter (owner) 1125 && owner.getProject().getTargetTypeFromFilePath (projectItem.getFile(), true) == targetType) 1126 { 1127 SourceFileInfo info; 1128 1129 info.path = build_tools::RelativePath (projectItem.getFile(), 1130 owner.getTargetFolder(), 1131 build_tools::RelativePath::buildTargetFolder); 1132 1133 jassert (info.path.getRoot() == build_tools::RelativePath::buildTargetFolder); 1134 1135 if (targetType == SharedCodeTarget || projectItem.shouldBeCompiled()) 1136 info.shouldBeCompiled = projectItem.shouldBeCompiled(); 1137 1138 result.add (info); 1139 } 1140 1141 return result; 1142 } 1143 1144 //============================================================================== addMainBuildProductXcodeTarget1145 void addMainBuildProduct() const 1146 { 1147 jassert (xcodeFileType.isNotEmpty()); 1148 jassert (xcodeBundleExtension.isEmpty() || xcodeBundleExtension.startsWithChar ('.')); 1149 1150 if (ProjectExporter::BuildConfiguration::Ptr config = owner.getConfiguration (0)) 1151 { 1152 auto productName = owner.replacePreprocessorTokens (*config, config->getTargetBinaryNameString (type == UnityPlugIn)); 1153 1154 if (xcodeFileType == "archive.ar") 1155 productName = getStaticLibbedFilename (productName); 1156 else 1157 productName += xcodeBundleExtension; 1158 1159 addBuildProduct (xcodeFileType, productName); 1160 } 1161 } 1162 1163 //============================================================================== addBuildProductXcodeTarget1164 void addBuildProduct (const String& fileType, const String& binaryName) const 1165 { 1166 ValueTree v (owner.createID (String ("__productFileID") + getName()) + " /* " + getName() + " */"); 1167 v.setProperty ("isa", "PBXFileReference", nullptr); 1168 v.setProperty ("explicitFileType", fileType, nullptr); 1169 v.setProperty ("includeInIndex", (int) 0, nullptr); 1170 v.setProperty ("path", binaryName, nullptr); 1171 v.setProperty ("sourceTree", "BUILT_PRODUCTS_DIR", nullptr); 1172 1173 owner.addObject (v); 1174 } 1175 1176 //============================================================================== addDependencyForXcodeTarget1177 String addDependencyFor (const XcodeTarget& dependentTarget) 1178 { 1179 auto dependencyID = owner.createID (String ("__dependency") + getName() + dependentTarget.getName()); 1180 ValueTree v (dependencyID); 1181 v.setProperty ("isa", "PBXTargetDependency", nullptr); 1182 v.setProperty ("target", getID(), nullptr); 1183 1184 owner.addObject (v); 1185 1186 return dependencyID; 1187 } 1188 addDependenciesXcodeTarget1189 void addDependencies() 1190 { 1191 if (! owner.project.isAudioPluginProject()) 1192 return; 1193 1194 if (type == XcodeTarget::StandalonePlugIn) // depends on AUv3 and shared code 1195 { 1196 if (auto* auv3Target = owner.getTargetOfType (XcodeTarget::AudioUnitv3PlugIn)) 1197 dependencyIDs.add (auv3Target->addDependencyFor (*this)); 1198 1199 if (auto* sharedCodeTarget = owner.getTargetOfType (XcodeTarget::SharedCodeTarget)) 1200 dependencyIDs.add (sharedCodeTarget->addDependencyFor (*this)); 1201 } 1202 else if (type == XcodeTarget::AggregateTarget) // depends on all other targets 1203 { 1204 for (auto* target : owner.targets) 1205 if (target->type != XcodeTarget::AggregateTarget) 1206 dependencyIDs.add (target->addDependencyFor (*this)); 1207 } 1208 else if (type != XcodeTarget::SharedCodeTarget) // shared code doesn't depend on anything; all other targets depend only on the shared code 1209 { 1210 if (auto* sharedCodeTarget = owner.getTargetOfType (XcodeTarget::SharedCodeTarget)) 1211 dependencyIDs.add (sharedCodeTarget->addDependencyFor (*this)); 1212 } 1213 } 1214 1215 //============================================================================== addTargetConfigXcodeTarget1216 void addTargetConfig (const String& configName, const StringArray& buildSettings) 1217 { 1218 auto configID = owner.createID (String ("targetconfigid_") + getName() + String ("_") + configName); 1219 1220 ValueTree v (configID); 1221 v.setProperty ("isa", "XCBuildConfiguration", nullptr); 1222 v.setProperty ("buildSettings", indentBracedList (buildSettings), nullptr); 1223 v.setProperty (Ids::name, configName, nullptr); 1224 1225 configIDs.add (configID); 1226 1227 owner.addObject (v); 1228 } 1229 1230 //============================================================================== getTargetAttributesXcodeTarget1231 String getTargetAttributes() const 1232 { 1233 StringArray attributes; 1234 1235 auto developmentTeamID = owner.getDevelopmentTeamIDString(); 1236 1237 if (developmentTeamID.isNotEmpty()) 1238 { 1239 attributes.add ("DevelopmentTeam = " + developmentTeamID); 1240 attributes.add ("ProvisioningStyle = Automatic"); 1241 } 1242 1243 std::map<String, bool> capabilities; 1244 1245 capabilities["ApplicationGroups.iOS"] = owner.iOS && owner.isAppGroupsEnabled(); 1246 capabilities["InAppPurchase"] = owner.isInAppPurchasesEnabled(); 1247 capabilities["InterAppAudio"] = owner.iOS && type == Target::StandalonePlugIn && owner.getProject().shouldEnableIAA(); 1248 capabilities["Push"] = owner.isPushNotificationsEnabled(); 1249 capabilities["Sandbox"] = type == Target::AudioUnitv3PlugIn || owner.isAppSandboxEnabled(); 1250 capabilities["HardenedRuntime"] = owner.isHardenedRuntimeEnabled(); 1251 1252 if (owner.iOS && owner.isiCloudPermissionsEnabled()) 1253 capabilities["com.apple.iCloud"] = true; 1254 1255 StringArray capabilitiesStrings; 1256 1257 for (auto& capability : capabilities) 1258 capabilitiesStrings.add ("com.apple." + capability.first + " = " + indentBracedList ({ String ("enabled = ") + (capability.second ? "1" : "0") }, 4)); 1259 1260 attributes.add ("SystemCapabilities = " + indentBracedList (capabilitiesStrings, 3)); 1261 1262 attributes.sort (false); 1263 1264 return getID() + " = " + indentBracedList (attributes, 2); 1265 } 1266 1267 //============================================================================== 1268 ValueTree addBuildPhase (const String& buildPhaseType, const StringArray& fileIds, const StringRef humanReadableName = StringRef()) 1269 { 1270 auto buildPhaseName = buildPhaseType + "_" + getName() + "_" + (humanReadableName.isNotEmpty() ? String (humanReadableName) : String ("resbuildphase")); 1271 auto buildPhaseId (owner.createID (buildPhaseName)); 1272 1273 int n = 0; 1274 while (buildPhaseIDs.contains (buildPhaseId)) 1275 buildPhaseId = owner.createID (buildPhaseName + String (++n)); 1276 1277 buildPhaseIDs.add (buildPhaseId); 1278 1279 ValueTree v (buildPhaseId); 1280 v.setProperty ("isa", buildPhaseType, nullptr); 1281 v.setProperty ("buildActionMask", "2147483647", nullptr); 1282 v.setProperty ("files", indentParenthesisedList (fileIds), nullptr); 1283 1284 if (humanReadableName.isNotEmpty()) 1285 v.setProperty ("name", String (humanReadableName), nullptr); 1286 1287 v.setProperty ("runOnlyForDeploymentPostprocessing", (int) 0, nullptr); 1288 1289 owner.addObject (v); 1290 1291 return v; 1292 } 1293 shouldCreatePListXcodeTarget1294 bool shouldCreatePList() const 1295 { 1296 auto fileType = getTargetFileType(); 1297 return (fileType == executable && type != ConsoleApp) || fileType == pluginBundle || fileType == macOSAppex; 1298 } 1299 1300 //============================================================================== shouldAddEntitlementsXcodeTarget1301 bool shouldAddEntitlements() const 1302 { 1303 if (owner.isPushNotificationsEnabled() 1304 || owner.isAppGroupsEnabled() 1305 || owner.isAppSandboxEnabled() 1306 || owner.isHardenedRuntimeEnabled() 1307 || (owner.isiOS() && owner.isiCloudPermissionsEnabled())) 1308 return true; 1309 1310 if (owner.project.isAudioPluginProject() 1311 && ((owner.isOSX() && type == Target::AudioUnitv3PlugIn) 1312 || (owner.isiOS() && type == Target::StandalonePlugIn && owner.getProject().shouldEnableIAA()))) 1313 return true; 1314 1315 return false; 1316 } 1317 getBundleIdentifierXcodeTarget1318 String getBundleIdentifier() const 1319 { 1320 auto exporterBundleIdentifier = owner.exporterBundleIdentifierValue.get().toString(); 1321 auto bundleIdentifier = exporterBundleIdentifier.isNotEmpty() ? exporterBundleIdentifier 1322 : owner.project.getBundleIdentifierString(); 1323 1324 if (xcodeBundleIDSubPath.isNotEmpty()) 1325 { 1326 auto bundleIdSegments = StringArray::fromTokens (bundleIdentifier, ".", StringRef()); 1327 1328 jassert (bundleIdSegments.size() > 0); 1329 bundleIdentifier += String (".") + bundleIdSegments[bundleIdSegments.size() - 1] + xcodeBundleIDSubPath; 1330 } 1331 1332 return bundleIdentifier; 1333 } 1334 getConfigPreprocessorDefsXcodeTarget1335 StringPairArray getConfigPreprocessorDefs (const XcodeBuildConfiguration& config) const 1336 { 1337 StringPairArray defines; 1338 1339 if (config.isDebug()) 1340 { 1341 defines.set ("_DEBUG", "1"); 1342 defines.set ("DEBUG", "1"); 1343 } 1344 else 1345 { 1346 defines.set ("_NDEBUG", "1"); 1347 defines.set ("NDEBUG", "1"); 1348 } 1349 1350 if (owner.isInAppPurchasesEnabled()) 1351 defines.set ("JUCE_IN_APP_PURCHASES", "1"); 1352 1353 if (owner.iOS && owner.isContentSharingEnabled()) 1354 defines.set ("JUCE_CONTENT_SHARING", "1"); 1355 1356 if (owner.isPushNotificationsEnabled()) 1357 defines.set ("JUCE_PUSH_NOTIFICATIONS", "1"); 1358 1359 return mergePreprocessorDefs (defines, owner.getAllPreprocessorDefs (config, type)); 1360 } 1361 1362 //============================================================================== getTargetSettingsXcodeTarget1363 StringPairArray getTargetSettings (const XcodeBuildConfiguration& config) const 1364 { 1365 StringPairArray s; 1366 1367 if (type == AggregateTarget && ! owner.isiOS()) 1368 { 1369 // the aggregate target needs to have the deployment target set for 1370 // pre-/post-build scripts 1371 s.set ("MACOSX_DEPLOYMENT_TARGET", getOSXDeploymentTarget (config.getOSXDeploymentTargetString())); 1372 s.set ("SDKROOT", getOSXSDKVersion (config.getOSXSDKVersionString())); 1373 1374 return s; 1375 } 1376 1377 s.set ("PRODUCT_NAME", owner.replacePreprocessorTokens (config, config.getTargetBinaryNameString (type == UnityPlugIn)).quoted()); 1378 s.set ("PRODUCT_BUNDLE_IDENTIFIER", getBundleIdentifier()); 1379 1380 auto arch = (! owner.isiOS() && type == Target::AudioUnitv3PlugIn) ? osxArch_64Bit 1381 : config.getOSXArchitectureString(); 1382 1383 if (arch == osxArch_Native) s.set ("ARCHS", "\"$(NATIVE_ARCH_ACTUAL)\""); 1384 else if (arch == osxArch_32BitUniversal) s.set ("ARCHS", "\"$(ARCHS_STANDARD_32_BIT)\""); 1385 else if (arch == osxArch_64BitUniversal) s.set ("ARCHS", "\"$(ARCHS_STANDARD_32_64_BIT)\""); 1386 else if (arch == osxArch_64Bit) s.set ("ARCHS", "\"$(ARCHS_STANDARD_64_BIT)\""); 1387 1388 if (! owner.isiOS()) 1389 { 1390 auto validArchs = owner.getValidArchs(); 1391 1392 if (! validArchs.isEmpty()) 1393 { 1394 const auto joined = std::accumulate (validArchs.begin(), 1395 validArchs.end(), 1396 String(), 1397 [] (String str, const var& v) { return str + v.toString() + " "; }); 1398 1399 s.set ("VALID_ARCHS", joined.trim().quoted()); 1400 } 1401 } 1402 1403 auto headerPaths = getHeaderSearchPaths (config); 1404 1405 auto mtlHeaderPaths = headerPaths; 1406 1407 for (auto& path : mtlHeaderPaths) 1408 path = path.unquoted(); 1409 1410 s.set ("MTL_HEADER_SEARCH_PATHS", "\"" + mtlHeaderPaths.joinIntoString (" ") + "\""); 1411 1412 headerPaths.add ("\"$(inherited)\""); 1413 s.set ("HEADER_SEARCH_PATHS", indentParenthesisedList (headerPaths, 1)); 1414 s.set ("USE_HEADERMAP", String (static_cast<bool> (config.exporter.settings.getProperty ("useHeaderMap")) ? "YES" : "NO")); 1415 1416 auto frameworksToSkip = [this]() -> String 1417 { 1418 const String openGLFramework (owner.iOS ? "OpenGLES" : "OpenGL"); 1419 1420 if (owner.xcodeFrameworks.contains (openGLFramework)) 1421 return openGLFramework; 1422 1423 return {}; 1424 }(); 1425 1426 if (frameworksToSkip.isNotEmpty()) 1427 s.set ("VALIDATE_WORKSPACE_SKIPPED_SDK_FRAMEWORKS", frameworksToSkip); 1428 1429 auto frameworkSearchPaths = getFrameworkSearchPaths (config); 1430 1431 if (! frameworkSearchPaths.isEmpty()) 1432 s.set ("FRAMEWORK_SEARCH_PATHS", String ("(") + frameworkSearchPaths.joinIntoString (", ") + ", \"$(inherited)\")"); 1433 1434 s.set ("GCC_OPTIMIZATION_LEVEL", config.getGCCOptimisationFlag()); 1435 1436 if (config.shouldUsePrecompiledHeaderFile()) 1437 { 1438 s.set ("GCC_PRECOMPILE_PREFIX_HEADER", "YES"); 1439 1440 auto pchFileContent = config.getPrecompiledHeaderFileContent(); 1441 1442 if (pchFileContent.isNotEmpty()) 1443 { 1444 auto pchFilename = config.getPrecompiledHeaderFilename() + ".h"; 1445 1446 build_tools::writeStreamToFile (owner.getTargetFolder().getChildFile (pchFilename), 1447 [&] (MemoryOutputStream& mo) { mo << pchFileContent; }); 1448 1449 s.set ("GCC_PREFIX_HEADER", pchFilename); 1450 } 1451 } 1452 1453 if (shouldCreatePList()) 1454 { 1455 s.set ("INFOPLIST_FILE", infoPlistFile.getFileName()); 1456 1457 if (owner.getPListPrefixHeaderString().isNotEmpty()) 1458 s.set ("INFOPLIST_PREFIX_HEADER", owner.getPListPrefixHeaderString()); 1459 1460 s.set ("INFOPLIST_PREPROCESS", (owner.isPListPreprocessEnabled() ? String ("YES") : String ("NO"))); 1461 1462 auto plistDefs = parsePreprocessorDefs (config.getPListPreprocessorDefinitionsString()); 1463 StringArray defsList; 1464 1465 for (int i = 0; i < plistDefs.size(); ++i) 1466 { 1467 auto def = plistDefs.getAllKeys()[i]; 1468 auto value = plistDefs.getAllValues()[i]; 1469 1470 if (value.isNotEmpty()) 1471 def << "=" << value.replace ("\"", "\\\\\\\""); 1472 1473 defsList.add ("\"" + def + "\""); 1474 } 1475 1476 if (defsList.size() > 0) 1477 s.set ("INFOPLIST_PREPROCESSOR_DEFINITIONS", indentParenthesisedList (defsList, 1)); 1478 } 1479 1480 if (config.isLinkTimeOptimisationEnabled()) 1481 s.set ("LLVM_LTO", "YES"); 1482 1483 if (config.isFastMathEnabled()) 1484 s.set ("GCC_FAST_MATH", "YES"); 1485 1486 auto flags = (config.getRecommendedCompilerWarningFlags().joinIntoString (" ") 1487 + " " + owner.getExtraCompilerFlagsString()).trim(); 1488 flags = owner.replacePreprocessorTokens (config, flags); 1489 1490 if (flags.isNotEmpty()) 1491 s.set ("OTHER_CPLUSPLUSFLAGS", flags.quoted()); 1492 1493 auto installPath = getInstallPathForConfiguration (config); 1494 1495 if (installPath.startsWith ("~")) 1496 installPath = installPath.replace ("~", "$(HOME)"); 1497 1498 if (installPath.isNotEmpty()) 1499 { 1500 s.set ("INSTALL_PATH", installPath.quoted()); 1501 1502 if (type == Target::SharedCodeTarget) 1503 s.set ("SKIP_INSTALL", "YES"); 1504 1505 if (! owner.embeddedFrameworkIDs.isEmpty()) 1506 s.set ("LD_RUNPATH_SEARCH_PATHS", "\"$(inherited) @executable_path/Frameworks @executable_path/../Frameworks\""); 1507 1508 if (xcodeCopyToProductInstallPathAfterBuild) 1509 { 1510 s.set ("DEPLOYMENT_LOCATION", "YES"); 1511 s.set ("DSTROOT", "/"); 1512 } 1513 } 1514 1515 if (getTargetFileType() == pluginBundle) 1516 { 1517 s.set ("LIBRARY_STYLE", "Bundle"); 1518 s.set ("WRAPPER_EXTENSION", xcodeBundleExtension.substring (1)); 1519 s.set ("GENERATE_PKGINFO_FILE", "YES"); 1520 } 1521 1522 if (xcodeOtherRezFlags.isNotEmpty()) 1523 s.set ("OTHER_REZFLAGS", "\"" + xcodeOtherRezFlags + "\""); 1524 1525 String configurationBuildDir ("$(PROJECT_DIR)/build/$(CONFIGURATION)"); 1526 1527 if (config.getTargetBinaryRelativePathString().isNotEmpty()) 1528 { 1529 // a target's position can either be defined via installPath + xcodeCopyToProductInstallPathAfterBuild 1530 // (= for audio plug-ins) or using a custom binary path (for everything else), but not both (= conflict!) 1531 jassert (! xcodeCopyToProductInstallPathAfterBuild); 1532 1533 build_tools::RelativePath binaryPath (config.getTargetBinaryRelativePathString(), 1534 build_tools::RelativePath::projectFolder); 1535 1536 configurationBuildDir = expandPath (binaryPath.rebased (owner.projectFolder, 1537 owner.getTargetFolder(), 1538 build_tools::RelativePath::buildTargetFolder) 1539 .toUnixStyle()); 1540 } 1541 1542 s.set ("CONFIGURATION_BUILD_DIR", addQuotesIfRequired (configurationBuildDir)); 1543 1544 if (owner.isHardenedRuntimeEnabled()) 1545 s.set ("ENABLE_HARDENED_RUNTIME", "YES"); 1546 1547 String gccVersion ("com.apple.compilers.llvm.clang.1_0"); 1548 1549 if (owner.iOS) 1550 { 1551 s.set ("ASSETCATALOG_COMPILER_APPICON_NAME", "AppIcon"); 1552 1553 if (! owner.shouldAddStoryboardToProject()) 1554 s.set ("ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME", "LaunchImage"); 1555 } 1556 else 1557 { 1558 s.set ("MACOSX_DEPLOYMENT_TARGET", getOSXDeploymentTarget (config.getOSXDeploymentTargetString())); 1559 } 1560 1561 s.set ("GCC_VERSION", gccVersion); 1562 s.set ("CLANG_LINK_OBJC_RUNTIME", "NO"); 1563 1564 auto codeSigningIdentity = owner.getCodeSigningIdentity (config); 1565 s.set (owner.iOS ? "\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\"" : "CODE_SIGN_IDENTITY", 1566 codeSigningIdentity.quoted()); 1567 1568 if (codeSigningIdentity.isNotEmpty()) 1569 s.set ("PROVISIONING_PROFILE_SPECIFIER", "\"\""); 1570 1571 if (owner.getDevelopmentTeamIDString().isNotEmpty()) 1572 s.set ("DEVELOPMENT_TEAM", owner.getDevelopmentTeamIDString()); 1573 1574 if (shouldAddEntitlements()) 1575 s.set ("CODE_SIGN_ENTITLEMENTS", getEntitlementsFilename().quoted()); 1576 1577 { 1578 auto cppStandard = owner.project.getCppStandardString(); 1579 1580 if (cppStandard == "latest") 1581 cppStandard = "17"; 1582 1583 s.set ("CLANG_CXX_LANGUAGE_STANDARD", (String (owner.shouldUseGNUExtensions() ? "gnu++" 1584 : "c++") + cppStandard).quoted()); 1585 } 1586 1587 s.set ("CLANG_CXX_LIBRARY", "\"libc++\""); 1588 1589 s.set ("COMBINE_HIDPI_IMAGES", "YES"); 1590 1591 { 1592 StringArray linkerFlags, librarySearchPaths; 1593 getLinkerSettings (config, linkerFlags, librarySearchPaths); 1594 1595 if (linkerFlags.size() > 0) 1596 s.set ("OTHER_LDFLAGS", linkerFlags.joinIntoString (" ").quoted()); 1597 1598 librarySearchPaths.addArray (config.getLibrarySearchPaths()); 1599 librarySearchPaths = getCleanedStringArray (librarySearchPaths); 1600 1601 if (librarySearchPaths.size() > 0) 1602 { 1603 StringArray libPaths; 1604 libPaths.add ("\"$(inherited)\""); 1605 1606 for (auto& p : librarySearchPaths) 1607 libPaths.add ("\"\\\"" + p + "\\\"\""); 1608 1609 s.set ("LIBRARY_SEARCH_PATHS", indentParenthesisedList (libPaths, 1)); 1610 } 1611 } 1612 1613 if (config.isDebug()) 1614 { 1615 s.set ("COPY_PHASE_STRIP", "NO"); 1616 s.set ("GCC_DYNAMIC_NO_PIC", "NO"); 1617 } 1618 else 1619 { 1620 s.set ("GCC_GENERATE_DEBUGGING_SYMBOLS", "NO"); 1621 s.set ("DEAD_CODE_STRIPPING", "YES"); 1622 } 1623 1624 if (type != Target::SharedCodeTarget && type != Target::StaticLibrary && type != Target::DynamicLibrary 1625 && config.isStripLocalSymbolsEnabled()) 1626 { 1627 s.set ("STRIPFLAGS", "\"-x\""); 1628 s.set ("DEPLOYMENT_POSTPROCESSING", "YES"); 1629 s.set ("SEPARATE_STRIP", "YES"); 1630 } 1631 1632 StringArray defsList; 1633 1634 const auto defines = getConfigPreprocessorDefs (config); 1635 1636 for (int i = 0; i < defines.size(); ++i) 1637 { 1638 auto def = defines.getAllKeys()[i]; 1639 auto value = defines.getAllValues()[i]; 1640 if (value.isNotEmpty()) 1641 def << "=" << value.replace ("\"", "\\\\\\\"").replace (" ", "\\\\ "); 1642 1643 defsList.add ("\"" + def + "\""); 1644 } 1645 1646 s.set ("GCC_PREPROCESSOR_DEFINITIONS", indentParenthesisedList (defsList, 1)); 1647 1648 StringArray customFlags; 1649 customFlags.addTokens (config.getCustomXcodeFlagsString(), ",", "\"'"); 1650 customFlags.removeEmptyStrings(); 1651 1652 for (auto flag : customFlags) 1653 { 1654 s.set (flag.upToFirstOccurrenceOf ("=", false, false).trim(), 1655 flag.fromFirstOccurrenceOf ("=", false, false).trim().quoted()); 1656 } 1657 1658 return s; 1659 } 1660 getInstallPathForConfigurationXcodeTarget1661 String getInstallPathForConfiguration (const XcodeBuildConfiguration& config) const 1662 { 1663 switch (type) 1664 { 1665 case GUIApp: return "$(HOME)/Applications"; 1666 case ConsoleApp: return "/usr/bin"; 1667 case VSTPlugIn: return config.isPluginBinaryCopyStepEnabled() ? config.getVSTBinaryLocationString() : String(); 1668 case VST3PlugIn: return config.isPluginBinaryCopyStepEnabled() ? config.getVST3BinaryLocationString() : String(); 1669 case AudioUnitPlugIn: return config.isPluginBinaryCopyStepEnabled() ? config.getAUBinaryLocationString() : String(); 1670 case RTASPlugIn: return config.isPluginBinaryCopyStepEnabled() ? config.getRTASBinaryLocationString() : String(); 1671 case AAXPlugIn: return config.isPluginBinaryCopyStepEnabled() ? config.getAAXBinaryLocationString() : String(); 1672 case UnityPlugIn: return config.isPluginBinaryCopyStepEnabled() ? config.getUnityPluginBinaryLocationString() : String(); 1673 case SharedCodeTarget: return owner.isiOS() ? "@executable_path/Frameworks" : "@executable_path/../Frameworks"; 1674 case StaticLibrary: 1675 case DynamicLibrary: 1676 case AudioUnitv3PlugIn: 1677 case StandalonePlugIn: 1678 case AggregateTarget: 1679 case unspecified: 1680 default: return {}; 1681 } 1682 } 1683 1684 //============================================================================== getLinkerSettingsXcodeTarget1685 void getLinkerSettings (const BuildConfiguration& config, StringArray& flags, StringArray& librarySearchPaths) const 1686 { 1687 if (getTargetFileType() == pluginBundle) 1688 flags.add (owner.isiOS() ? "-bitcode_bundle" : "-bundle"); 1689 1690 if (type != Target::SharedCodeTarget) 1691 { 1692 Array<build_tools::RelativePath> extraLibs; 1693 1694 addExtraLibsForTargetType (config, extraLibs); 1695 1696 for (auto& lib : extraLibs) 1697 { 1698 flags.add (getLinkerFlagForLib (lib.getFileNameWithoutExtension())); 1699 librarySearchPaths.add (owner.getSearchPathForStaticLibrary (lib)); 1700 } 1701 1702 if (owner.project.isAudioPluginProject()) 1703 { 1704 if (owner.getTargetOfType (Target::SharedCodeTarget) != nullptr) 1705 { 1706 auto productName = getStaticLibbedFilename (owner.replacePreprocessorTokens (config, config.getTargetBinaryNameString())); 1707 1708 build_tools::RelativePath sharedCodelib (productName, build_tools::RelativePath::buildTargetFolder); 1709 flags.add (getLinkerFlagForLib (sharedCodelib.getFileNameWithoutExtension())); 1710 } 1711 } 1712 1713 flags.add (owner.getExternalLibraryFlags (config)); 1714 1715 auto libs = owner.xcodeLibs; 1716 libs.addArray (xcodeLibs); 1717 1718 for (auto& l : libs) 1719 flags.add (getLinkerFlagForLib (l)); 1720 } 1721 1722 flags.add (owner.replacePreprocessorTokens (config, owner.getExtraLinkerFlagsString())); 1723 flags = getCleanedStringArray (flags); 1724 } 1725 1726 //========================================================================== writeInfoPlistFileXcodeTarget1727 void writeInfoPlistFile() const 1728 { 1729 if (! shouldCreatePList()) 1730 return; 1731 1732 build_tools::PlistOptions options; 1733 1734 options.type = type; 1735 options.executableName = "${EXECUTABLE_NAME}"; 1736 options.bundleIdentifier = getBundleIdentifier(); 1737 options.plistToMerge = owner.getPListToMergeString(); 1738 options.iOS = owner.iOS; 1739 options.microphonePermissionEnabled = owner.isMicrophonePermissionEnabled(); 1740 options.microphonePermissionText = owner.getMicrophonePermissionsTextString(); 1741 options.cameraPermissionEnabled = owner.isCameraPermissionEnabled(); 1742 options.cameraPermissionText = owner.getCameraPermissionTextString(); 1743 options.bluetoothPermissionEnabled = owner.isBluetoothPermissionEnabled(); 1744 options.bluetoothPermissionText = owner.getBluetoothPermissionTextString(); 1745 options.sendAppleEventsPermissionEnabled = owner.isSendAppleEventsPermissionEnabled(); 1746 options.sendAppleEventsPermissionText = owner.getSendAppleEventsPermissionTextString(); 1747 options.shouldAddStoryboardToProject = owner.shouldAddStoryboardToProject(); 1748 options.iconFile = owner.iconFile; 1749 options.projectName = owner.projectName; 1750 options.version = owner.project.getVersionString(); 1751 options.companyCopyright = owner.project.getCompanyCopyrightString(); 1752 options.allPreprocessorDefs = owner.getAllPreprocessorDefs(); 1753 options.documentExtensions = owner.getDocumentExtensionsString(); 1754 options.fileSharingEnabled = owner.isFileSharingEnabled(); 1755 options.documentBrowserEnabled = owner.isDocumentBrowserEnabled(); 1756 options.statusBarHidden = owner.isStatusBarHidden(); 1757 options.requiresFullScreen = owner.requiresFullScreen(); 1758 options.backgroundAudioEnabled = owner.isBackgroundAudioEnabled(); 1759 options.backgroundBleEnabled = owner.isBackgroundBleEnabled(); 1760 options.pushNotificationsEnabled = owner.isPushNotificationsEnabled(); 1761 options.enableIAA = owner.project.shouldEnableIAA(); 1762 options.IAAPluginName = owner.project.getIAAPluginName(); 1763 options.pluginManufacturerCode = owner.project.getPluginManufacturerCodeString(); 1764 options.IAATypeCode = owner.project.getIAATypeCode(); 1765 options.pluginCode = owner.project.getPluginCodeString(); 1766 options.versionAsHex = owner.project.getVersionAsHexInteger(); 1767 options.iPhoneScreenOrientations = owner.getiPhoneScreenOrientations(); 1768 options.iPadScreenOrientations = owner.getiPadScreenOrientations(); 1769 1770 options.storyboardName = [&] 1771 { 1772 const auto customLaunchStoryboard = owner.getCustomLaunchStoryboardString(); 1773 1774 if (customLaunchStoryboard.isEmpty()) 1775 return owner.getDefaultLaunchStoryboardName(); 1776 1777 return customLaunchStoryboard.fromLastOccurrenceOf ("/", false, false) 1778 .upToLastOccurrenceOf (".storyboard", false, false); 1779 }(); 1780 1781 options.pluginName = owner.project.getPluginNameString(); 1782 options.pluginManufacturer = owner.project.getPluginManufacturerString(); 1783 options.pluginDescription = owner.project.getPluginDescriptionString(); 1784 options.pluginAUExportPrefix = owner.project.getPluginAUExportPrefixString(); 1785 options.auMainType = owner.project.getAUMainTypeString(); 1786 options.isAuSandboxSafe = owner.project.isAUSandBoxSafe(); 1787 options.isPluginSynth = owner.project.isPluginSynth(); 1788 options.suppressResourceUsage = owner.getSuppressPlistResourceUsage(); 1789 1790 options.write (infoPlistFile); 1791 } 1792 1793 //============================================================================== addShellScriptBuildPhaseXcodeTarget1794 void addShellScriptBuildPhase (const String& phaseName, const String& script) 1795 { 1796 if (script.trim().isNotEmpty()) 1797 { 1798 auto v = addBuildPhase ("PBXShellScriptBuildPhase", {}); 1799 v.setProperty (Ids::name, phaseName, nullptr); 1800 v.setProperty ("shellPath", "/bin/sh", nullptr); 1801 v.setProperty ("shellScript", script.replace ("\\", "\\\\") 1802 .replace ("\"", "\\\"") 1803 .replace ("\r\n", "\\n") 1804 .replace ("\n", "\\n"), nullptr); 1805 } 1806 } 1807 addCopyFilesPhaseXcodeTarget1808 void addCopyFilesPhase (const String& phaseName, const StringArray& files, XcodeCopyFilesDestinationIDs dst) 1809 { 1810 auto v = addBuildPhase ("PBXCopyFilesBuildPhase", files, phaseName); 1811 v.setProperty ("dstPath", "", nullptr); 1812 v.setProperty ("dstSubfolderSpec", (int) dst, nullptr); 1813 } 1814 1815 //============================================================================== sanitiseAndEscapeSearchPathsXcodeTarget1816 void sanitiseAndEscapeSearchPaths (const BuildConfiguration& config, StringArray& paths) const 1817 { 1818 paths = getCleanedStringArray (paths); 1819 1820 for (auto& path : paths) 1821 { 1822 path = owner.replacePreprocessorTokens (config, expandPath (path)); 1823 1824 if (path.containsChar (' ')) 1825 path = "\"\\\"" + path + "\\\"\""; // crazy double quotes required when there are spaces.. 1826 else 1827 path = "\"" + path + "\""; 1828 } 1829 } 1830 getHeaderSearchPathsXcodeTarget1831 StringArray getHeaderSearchPaths (const BuildConfiguration& config) const 1832 { 1833 StringArray paths (owner.extraSearchPaths); 1834 paths.addArray (config.getHeaderSearchPaths()); 1835 paths.addArray (getTargetExtraHeaderSearchPaths()); 1836 1837 if (owner.project.getEnabledModules().isModuleEnabled ("juce_audio_plugin_client")) 1838 { 1839 // Needed to compile .r files 1840 paths.add (owner.getModuleFolderRelativeToProject ("juce_audio_plugin_client") 1841 .rebased (owner.projectFolder, owner.getTargetFolder(), build_tools::RelativePath::buildTargetFolder) 1842 .toUnixStyle()); 1843 } 1844 1845 sanitiseAndEscapeSearchPaths (config, paths); 1846 return paths; 1847 } 1848 getFrameworkSearchPathsXcodeTarget1849 StringArray getFrameworkSearchPaths (const BuildConfiguration& config) const 1850 { 1851 auto paths = getSearchPathsFromString (owner.getFrameworkSearchPathsString()); 1852 sanitiseAndEscapeSearchPaths (config, paths); 1853 return paths; 1854 } 1855 1856 private: 1857 //============================================================================== addExtraAudioUnitTargetSettingsXcodeTarget1858 void addExtraAudioUnitTargetSettings() 1859 { 1860 xcodeOtherRezFlags = "-d ppc_$ppc -d i386_$i386 -d ppc64_$ppc64 -d x86_64_$x86_64" 1861 " -I /System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework/Versions/A/Headers" 1862 " -I \\\"$(DEVELOPER_DIR)/Extras/CoreAudio/AudioUnits/AUPublic/AUBase\\\"" 1863 " -I \\\"$(DEVELOPER_DIR)/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/AudioUnit.framework/Headers\\\""; 1864 1865 xcodeFrameworks.addArray ({ "AudioUnit", "CoreAudioKit" }); 1866 } 1867 addExtraAudioUnitv3PlugInTargetSettingsXcodeTarget1868 void addExtraAudioUnitv3PlugInTargetSettings() 1869 { 1870 xcodeFrameworks.addArray ({ "AVFoundation", "CoreAudioKit" }); 1871 1872 if (owner.isOSX()) 1873 xcodeFrameworks.add ("AudioUnit"); 1874 } 1875 addExtraLibsForTargetTypeXcodeTarget1876 void addExtraLibsForTargetType (const BuildConfiguration& config, Array<build_tools::RelativePath>& extraLibs) const 1877 { 1878 if (type == AAXPlugIn) 1879 { 1880 auto aaxLibsFolder = build_tools::RelativePath (owner.getAAXPathString(), build_tools::RelativePath::projectFolder).getChildFile ("Libs"); 1881 1882 String libraryPath (config.isDebug() ? "Debug" : "Release"); 1883 libraryPath += "/libAAXLibrary_libcpp.a"; 1884 1885 extraLibs.add (aaxLibsFolder.getChildFile (libraryPath)); 1886 } 1887 else if (type == RTASPlugIn) 1888 { 1889 build_tools::RelativePath rtasFolder (owner.getRTASPathString(), build_tools::RelativePath::projectFolder); 1890 1891 extraLibs.add (rtasFolder.getChildFile ("MacBag/Libs/Debug/libPluginLibrary.a")); 1892 extraLibs.add (rtasFolder.getChildFile ("MacBag/Libs/Release/libPluginLibrary.a")); 1893 } 1894 } 1895 getTargetExtraHeaderSearchPathsXcodeTarget1896 StringArray getTargetExtraHeaderSearchPaths() const 1897 { 1898 StringArray targetExtraSearchPaths; 1899 1900 if (type == RTASPlugIn) 1901 { 1902 build_tools::RelativePath rtasFolder (owner.getRTASPathString(), build_tools::RelativePath::projectFolder); 1903 1904 targetExtraSearchPaths.add ("$(DEVELOPER_DIR)/Headers/FlatCarbon"); 1905 targetExtraSearchPaths.add ("$(SDKROOT)/Developer/Headers/FlatCarbon"); 1906 1907 static const char* p[] = { "AlturaPorts/TDMPlugIns/PlugInLibrary/Controls", 1908 "AlturaPorts/TDMPlugIns/PlugInLibrary/CoreClasses", 1909 "AlturaPorts/TDMPlugIns/PlugInLibrary/DSPClasses", 1910 "AlturaPorts/TDMPlugIns/PlugInLibrary/EffectClasses", 1911 "AlturaPorts/TDMPlugIns/PlugInLibrary/MacBuild", 1912 "AlturaPorts/TDMPlugIns/PlugInLibrary/Meters", 1913 "AlturaPorts/TDMPlugIns/PlugInLibrary/ProcessClasses", 1914 "AlturaPorts/TDMPlugIns/PlugInLibrary/ProcessClasses/Interfaces", 1915 "AlturaPorts/TDMPlugIns/PlugInLibrary/RTASP_Adapt", 1916 "AlturaPorts/TDMPlugIns/PlugInLibrary/Utilities", 1917 "AlturaPorts/TDMPlugIns/PlugInLibrary/ViewClasses", 1918 "AlturaPorts/TDMPlugIns/DSPManager/**", 1919 "AlturaPorts/TDMPlugIns/SupplementalPlugInLib/Encryption", 1920 "AlturaPorts/TDMPlugIns/SupplementalPlugInLib/GraphicsExtensions", 1921 "AlturaPorts/TDMPlugIns/common/**", 1922 "AlturaPorts/TDMPlugIns/common/PI_LibInterface", 1923 "AlturaPorts/TDMPlugIns/PACEProtection/**", 1924 "AlturaPorts/TDMPlugIns/SignalProcessing/**", 1925 "AlturaPorts/OMS/Headers", 1926 "AlturaPorts/Fic/Interfaces/**", 1927 "AlturaPorts/Fic/Source/SignalNets", 1928 "AlturaPorts/DSIPublicInterface/PublicHeaders", 1929 "DAEWin/Include", 1930 "AlturaPorts/DigiPublic/Interfaces", 1931 "AlturaPorts/DigiPublic", 1932 "AlturaPorts/NewFileLibs/DOA", 1933 "AlturaPorts/NewFileLibs/Cmn", 1934 "xplat/AVX/avx2/avx2sdk/inc", 1935 "xplat/AVX/avx2/avx2sdk/utils" }; 1936 1937 for (auto* path : p) 1938 owner.addProjectPathToBuildPathList (targetExtraSearchPaths, rtasFolder.getChildFile (path)); 1939 } 1940 1941 return targetExtraSearchPaths; 1942 } 1943 getOSXDeploymentTargetXcodeTarget1944 String getOSXDeploymentTarget (const String& deploymentTarget) const 1945 { 1946 auto minVersion = (type == Target::AudioUnitv3PlugIn ? minimumAUv3SDKVersion 1947 : oldestDeploymentTarget); 1948 1949 for (auto v = minVersion; v != nextMacOSVersion; ++v) 1950 if (deploymentTarget == getDisplayName (v)) 1951 return ::getName (v); 1952 1953 return ::getName (minVersion); 1954 } 1955 1956 //============================================================================== 1957 const XcodeProjectExporter& owner; 1958 1959 Target& operator= (const Target&) = delete; 1960 }; 1961 1962 mutable StringArray xcodeFrameworks; 1963 StringArray xcodeLibs; 1964 1965 private: 1966 //============================================================================== expandPath(const String & path)1967 static String expandPath (const String& path) 1968 { 1969 if (! File::isAbsolutePath (path)) return "$(SRCROOT)/" + path; 1970 if (path.startsWithChar ('~')) return "$(HOME)" + path.substring (1); 1971 1972 return path; 1973 } 1974 addQuotesIfRequired(const String & s)1975 static String addQuotesIfRequired (const String& s) 1976 { 1977 return s.containsAnyOf (" $") ? s.quoted() : s; 1978 } 1979 getProjectBundle()1980 File getProjectBundle() const { return getTargetFolder().getChildFile (project.getProjectFilenameRootString()).withFileExtension (".xcodeproj"); } 1981 1982 //============================================================================== createObjects()1983 void createObjects() const 1984 { 1985 prepareTargets(); 1986 1987 // Must be called before adding embedded frameworks, as we want to 1988 // embed any frameworks found in subprojects. 1989 addSubprojects(); 1990 1991 addFrameworks(); 1992 addCustomFrameworks(); 1993 addEmbeddedFrameworks(); 1994 1995 addCustomResourceFolders(); 1996 addPlistFileReferences(); 1997 1998 if (iOS && ! projectType.isStaticLibrary()) 1999 { 2000 addXcassets(); 2001 2002 if (shouldAddStoryboardToProject()) 2003 { 2004 auto customLaunchStoryboard = getCustomLaunchStoryboardString(); 2005 2006 if (customLaunchStoryboard.isEmpty()) 2007 writeDefaultLaunchStoryboardFile(); 2008 else if (getProject().getProjectFolder().getChildFile (customLaunchStoryboard).existsAsFile()) 2009 addLaunchStoryboardFileReference (build_tools::RelativePath (customLaunchStoryboard, build_tools::RelativePath::projectFolder) 2010 .rebased (getProject().getProjectFolder(), getTargetFolder(), build_tools::RelativePath::buildTargetFolder)); 2011 } 2012 } 2013 else 2014 { 2015 addNibFiles(); 2016 } 2017 2018 addIcons(); 2019 addBuildConfigurations(); 2020 2021 addProjectConfigList (createID ("__projList")); 2022 2023 { 2024 StringArray topLevelGroupIDs; 2025 2026 addFilesAndGroupsToProject (topLevelGroupIDs); 2027 addBuildPhases(); 2028 addExtraGroupsToProject (topLevelGroupIDs); 2029 2030 addGroup (createID ("__mainsourcegroup"), "Source", topLevelGroupIDs); 2031 } 2032 2033 addProjectObject(); 2034 removeMismatchedXcuserdata(); 2035 } 2036 prepareTargets()2037 void prepareTargets() const 2038 { 2039 for (auto* target : targets) 2040 { 2041 target->addDependencies(); 2042 2043 if (target->type == XcodeTarget::AggregateTarget) 2044 continue; 2045 2046 target->addMainBuildProduct(); 2047 2048 auto targetName = String (target->getName()); 2049 auto fileID = createID (targetName + "__targetbuildref"); 2050 auto fileRefID = createID ("__productFileID" + targetName); 2051 2052 ValueTree v (fileID + " /* " + targetName + " */"); 2053 v.setProperty ("isa", "PBXBuildFile", nullptr); 2054 v.setProperty ("fileRef", fileRefID, nullptr); 2055 2056 target->mainBuildProductID = fileID; 2057 2058 addObject (v); 2059 } 2060 } 2061 addPlistFileReferences()2062 void addPlistFileReferences() const 2063 { 2064 for (auto* target : targets) 2065 { 2066 if (target->type == XcodeTarget::AggregateTarget) 2067 continue; 2068 2069 if (target->shouldCreatePList()) 2070 { 2071 build_tools::RelativePath plistPath (target->infoPlistFile, getTargetFolder(), build_tools::RelativePath::buildTargetFolder); 2072 addFileReference (plistPath.toUnixStyle()); 2073 resourceFileRefs.add (createFileRefID (plistPath)); 2074 } 2075 } 2076 } 2077 addNibFiles()2078 void addNibFiles() const 2079 { 2080 build_tools::writeStreamToFile (menuNibFile, [&] (MemoryOutputStream& mo) 2081 { 2082 mo.write (BinaryData::RecentFilesMenuTemplate_nib, BinaryData::RecentFilesMenuTemplate_nibSize); 2083 }); 2084 2085 build_tools::RelativePath menuNibPath (menuNibFile, getTargetFolder(), build_tools::RelativePath::buildTargetFolder); 2086 addFileReference (menuNibPath.toUnixStyle()); 2087 resourceIDs.add (addBuildFile (FileOptions().withRelativePath (menuNibPath))); 2088 resourceFileRefs.add (createFileRefID (menuNibPath)); 2089 } 2090 addIcons()2091 void addIcons() const 2092 { 2093 if (iconFile.exists()) 2094 { 2095 build_tools::RelativePath iconPath (iconFile, getTargetFolder(), build_tools::RelativePath::buildTargetFolder); 2096 addFileReference (iconPath.toUnixStyle()); 2097 resourceIDs.add (addBuildFile (FileOptions().withRelativePath (iconPath))); 2098 resourceFileRefs.add (createFileRefID (iconPath)); 2099 } 2100 } 2101 addBuildConfigurations()2102 void addBuildConfigurations() const 2103 { 2104 for (ConstConfigIterator config (*this); config.next();) 2105 { 2106 auto& xcodeConfig = dynamic_cast<const XcodeBuildConfiguration&> (*config); 2107 StringArray settingsLines; 2108 auto configSettings = getProjectSettings (xcodeConfig); 2109 auto keys = configSettings.getAllKeys(); 2110 keys.sort (false); 2111 2112 for (auto& key : keys) 2113 settingsLines.add (key + " = " + configSettings[key]); 2114 2115 addProjectConfig (config->getName(), settingsLines); 2116 } 2117 } 2118 addFilesAndGroupsToProject(StringArray & topLevelGroupIDs)2119 void addFilesAndGroupsToProject (StringArray& topLevelGroupIDs) const 2120 { 2121 for (auto* target : targets) 2122 if (target->shouldAddEntitlements()) 2123 addEntitlementsFile (*target); 2124 2125 for (auto& group : getAllGroups()) 2126 { 2127 if (group.getNumChildren() > 0) 2128 { 2129 auto groupID = addProjectItem (group); 2130 2131 if (groupID.isNotEmpty()) 2132 topLevelGroupIDs.add (groupID); 2133 } 2134 } 2135 } 2136 addExtraGroupsToProject(StringArray & topLevelGroupIDs)2137 void addExtraGroupsToProject (StringArray& topLevelGroupIDs) const 2138 { 2139 { 2140 auto resourcesGroupID = createID ("__resources"); 2141 addGroup (resourcesGroupID, "Resources", resourceFileRefs); 2142 topLevelGroupIDs.add (resourcesGroupID); 2143 } 2144 2145 { 2146 auto frameworksGroupID = createID ("__frameworks"); 2147 addGroup (frameworksGroupID, "Frameworks", frameworkFileIDs); 2148 topLevelGroupIDs.add (frameworksGroupID); 2149 } 2150 2151 { 2152 auto productsGroupID = createID ("__products"); 2153 addGroup (productsGroupID, "Products", buildProducts); 2154 topLevelGroupIDs.add (productsGroupID); 2155 } 2156 2157 if (! subprojectFileIDs.isEmpty()) 2158 { 2159 auto subprojectLibrariesGroupID = createID ("__subprojects"); 2160 addGroup (subprojectLibrariesGroupID, "Subprojects", subprojectFileIDs); 2161 topLevelGroupIDs.add (subprojectLibrariesGroupID); 2162 } 2163 } 2164 addBuildPhases()2165 void addBuildPhases() const 2166 { 2167 // add build phases 2168 for (auto* target : targets) 2169 { 2170 if (target->type != XcodeTarget::AggregateTarget) 2171 buildProducts.add (createID (String ("__productFileID") + String (target->getName()))); 2172 2173 for (ConstConfigIterator config (*this); config.next();) 2174 { 2175 auto& xcodeConfig = dynamic_cast<const XcodeBuildConfiguration&> (*config); 2176 2177 auto configSettings = target->getTargetSettings (xcodeConfig); 2178 StringArray settingsLines; 2179 auto keys = configSettings.getAllKeys(); 2180 keys.sort (false); 2181 2182 for (auto& key : keys) 2183 settingsLines.add (key + " = " + configSettings.getValue (key, "\"\"")); 2184 2185 target->addTargetConfig (config->getName(), settingsLines); 2186 } 2187 2188 addConfigList (*target, createID (String ("__configList") + target->getName())); 2189 2190 target->addShellScriptBuildPhase ("Pre-build script", getPreBuildScript()); 2191 2192 if (target->type != XcodeTarget::AggregateTarget) 2193 { 2194 auto skipAUv3 = (target->type == XcodeTarget::AudioUnitv3PlugIn && ! shouldDuplicateAppExResourcesFolder()); 2195 2196 if (! projectType.isStaticLibrary() && target->type != XcodeTarget::SharedCodeTarget && ! skipAUv3) 2197 target->addBuildPhase ("PBXResourcesBuildPhase", resourceIDs); 2198 2199 auto rezFiles = rezFileIDs; 2200 rezFiles.addArray (target->rezFileIDs); 2201 2202 if (rezFiles.size() > 0) 2203 target->addBuildPhase ("PBXRezBuildPhase", rezFiles); 2204 2205 auto sourceFiles = target->sourceIDs; 2206 2207 if (target->type == XcodeTarget::SharedCodeTarget 2208 || (! project.isAudioPluginProject())) 2209 sourceFiles.addArray (sourceIDs); 2210 2211 target->addBuildPhase ("PBXSourcesBuildPhase", sourceFiles); 2212 2213 if (! projectType.isStaticLibrary() && target->type != XcodeTarget::SharedCodeTarget) 2214 target->addBuildPhase ("PBXFrameworksBuildPhase", target->frameworkIDs); 2215 } 2216 2217 target->addShellScriptBuildPhase ("Post-build script", getPostBuildScript()); 2218 2219 if (project.isAudioPluginProject() && project.shouldBuildAUv3() 2220 && project.shouldBuildStandalonePlugin() && target->type == XcodeTarget::StandalonePlugIn) 2221 embedAppExtension(); 2222 2223 if (project.isAudioPluginProject() && project.shouldBuildUnityPlugin() 2224 && target->type == XcodeTarget::UnityPlugIn) 2225 embedUnityScript(); 2226 2227 addTargetObject (*target); 2228 } 2229 } 2230 embedAppExtension()2231 void embedAppExtension() const 2232 { 2233 if (auto* standaloneTarget = getTargetOfType (XcodeTarget::StandalonePlugIn)) 2234 { 2235 if (auto* auv3Target = getTargetOfType (XcodeTarget::AudioUnitv3PlugIn)) 2236 { 2237 StringArray files; 2238 files.add (auv3Target->mainBuildProductID); 2239 standaloneTarget->addCopyFilesPhase ("Embed App Extensions", files, kPluginsFolder); 2240 } 2241 } 2242 } 2243 embedUnityScript()2244 void embedUnityScript() const 2245 { 2246 if (auto* unityTarget = getTargetOfType (XcodeTarget::UnityPlugIn)) 2247 { 2248 build_tools::RelativePath scriptPath (getProject().getGeneratedCodeFolder().getChildFile (getProject().getUnityScriptName()), 2249 getTargetFolder(), 2250 build_tools::RelativePath::buildTargetFolder); 2251 2252 auto path = scriptPath.toUnixStyle(); 2253 auto refID = addFileReference (path); 2254 auto fileID = addBuildFile (FileOptions().withPath (path) 2255 .withFileRefID (refID)); 2256 2257 resourceIDs.add (fileID); 2258 resourceFileRefs.add (refID); 2259 2260 unityTarget->addCopyFilesPhase ("Embed Unity Script", fileID, kResourcesFolder); 2261 } 2262 } 2263 2264 //============================================================================== getTargetOfType(build_tools::ProjectType::Target::Type type)2265 XcodeTarget* getTargetOfType (build_tools::ProjectType::Target::Type type) const 2266 { 2267 for (auto& target : targets) 2268 if (target->type == type) 2269 return target; 2270 2271 return nullptr; 2272 } 2273 addTargetObject(XcodeTarget & target)2274 void addTargetObject (XcodeTarget& target) const 2275 { 2276 auto targetName = target.getName(); 2277 2278 auto targetID = target.getID(); 2279 ValueTree v (targetID); 2280 v.setProperty ("isa", target.type == XcodeTarget::AggregateTarget ? "PBXAggregateTarget" : "PBXNativeTarget", nullptr); 2281 v.setProperty ("buildConfigurationList", createID (String ("__configList") + targetName), nullptr); 2282 2283 v.setProperty ("buildPhases", indentParenthesisedList (target.buildPhaseIDs), nullptr); 2284 2285 if (target.type != XcodeTarget::AggregateTarget) 2286 v.setProperty ("buildRules", indentParenthesisedList ({}), nullptr); 2287 2288 StringArray allDependencyIDs { subprojectDependencyIDs }; 2289 allDependencyIDs.addArray (target.dependencyIDs); 2290 v.setProperty ("dependencies", indentParenthesisedList (allDependencyIDs), nullptr); 2291 2292 v.setProperty (Ids::name, target.getXcodeSchemeName(), nullptr); 2293 v.setProperty ("productName", projectName, nullptr); 2294 2295 if (target.type != XcodeTarget::AggregateTarget) 2296 { 2297 v.setProperty ("productReference", createID (String ("__productFileID") + targetName), nullptr); 2298 2299 jassert (target.xcodeProductType.isNotEmpty()); 2300 v.setProperty ("productType", target.xcodeProductType, nullptr); 2301 } 2302 2303 targetIDs.add (targetID); 2304 addObject (v); 2305 } 2306 createIconFile()2307 void createIconFile() const 2308 { 2309 const auto icons = getIcons(); 2310 2311 if (! build_tools::asArray (icons).isEmpty()) 2312 { 2313 iconFile = getTargetFolder().getChildFile ("Icon.icns"); 2314 build_tools::writeMacIcon (icons, iconFile); 2315 } 2316 } 2317 writeWorkspaceSettings()2318 void writeWorkspaceSettings() const 2319 { 2320 const auto settingsFile = getProjectBundle().getChildFile ("project.xcworkspace") 2321 .getChildFile ("xcshareddata") 2322 .getChildFile ("WorkspaceSettings.xcsettings"); 2323 2324 if (shouldUseLegacyBuildSystem()) 2325 { 2326 build_tools::writeStreamToFile (settingsFile, [this] (MemoryOutputStream& mo) 2327 { 2328 mo.setNewLineString (getNewLineString()); 2329 2330 mo << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << newLine 2331 << "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">" << newLine 2332 << "<plist version=\"1.0\">" << newLine 2333 << "<dict>" << newLine 2334 << "\t" << "<key>BuildSystemType</key>" << newLine 2335 << "\t" << "<string>Original</string>" << newLine 2336 << "\t" << "<key>DisableBuildSystemDeprecationWarning</key>" << newLine 2337 << "\t" << "<true/>" << newLine 2338 << "</dict>" << newLine 2339 << "</plist>" << newLine; 2340 }); 2341 } 2342 else 2343 { 2344 settingsFile.deleteFile(); 2345 } 2346 } 2347 writeInfoPlistFiles()2348 void writeInfoPlistFiles() const 2349 { 2350 for (auto& target : targets) 2351 target->writeInfoPlistFile(); 2352 } 2353 2354 // Delete .rsrc files in folder but don't follow sym-links deleteRsrcFiles(const File & folder)2355 void deleteRsrcFiles (const File& folder) const 2356 { 2357 for (const auto& di : RangedDirectoryIterator (folder, false, "*", File::findFilesAndDirectories)) 2358 { 2359 const auto& entry = di.getFile(); 2360 2361 if (! entry.isSymbolicLink()) 2362 { 2363 if (entry.existsAsFile() && entry.getFileExtension().toLowerCase() == ".rsrc") 2364 entry.deleteFile(); 2365 else if (entry.isDirectory()) 2366 deleteRsrcFiles (entry); 2367 } 2368 } 2369 } 2370 getLinkerFlagForLib(String library)2371 static String getLinkerFlagForLib (String library) 2372 { 2373 if (library.substring (0, 3) == "lib") 2374 library = library.substring (3); 2375 2376 return "-l" + library.replace (" ", "\\\\ ").replace ("\"", "\\\\\"").replace ("\'", "\\\\\'").upToLastOccurrenceOf (".", false, false); 2377 } 2378 getSearchPathForStaticLibrary(const build_tools::RelativePath & library)2379 String getSearchPathForStaticLibrary (const build_tools::RelativePath& library) const 2380 { 2381 auto searchPath = library.toUnixStyle().upToLastOccurrenceOf ("/", false, false); 2382 2383 if (! library.isAbsolute()) 2384 { 2385 auto srcRoot = rebaseFromProjectFolderToBuildTarget (build_tools::RelativePath (".", build_tools::RelativePath::projectFolder)).toUnixStyle(); 2386 2387 if (srcRoot.endsWith ("/.")) srcRoot = srcRoot.dropLastCharacters (2); 2388 if (! srcRoot.endsWithChar ('/')) srcRoot << '/'; 2389 2390 searchPath = srcRoot + searchPath; 2391 } 2392 2393 return expandPath (searchPath); 2394 } 2395 getCodeSigningIdentity(const XcodeBuildConfiguration & config)2396 String getCodeSigningIdentity (const XcodeBuildConfiguration& config) const 2397 { 2398 auto identity = config.getCodeSignIdentityString(); 2399 2400 if (identity.isEmpty() && getDevelopmentTeamIDString().isNotEmpty()) 2401 return iOS ? "iPhone Developer" : "Mac Developer"; 2402 2403 return identity; 2404 } 2405 getProjectSettings(const XcodeBuildConfiguration & config)2406 StringPairArray getProjectSettings (const XcodeBuildConfiguration& config) const 2407 { 2408 StringPairArray s; 2409 2410 s.set ("ALWAYS_SEARCH_USER_PATHS", "NO"); 2411 s.set ("ENABLE_STRICT_OBJC_MSGSEND", "YES"); 2412 s.set ("GCC_C_LANGUAGE_STANDARD", "c11"); 2413 s.set ("GCC_NO_COMMON_BLOCKS", "YES"); 2414 s.set ("GCC_MODEL_TUNING", "G5"); 2415 s.set ("GCC_WARN_ABOUT_RETURN_TYPE", "YES"); 2416 s.set ("GCC_WARN_CHECK_SWITCH_STATEMENTS", "YES"); 2417 s.set ("GCC_WARN_UNUSED_VARIABLE", "YES"); 2418 s.set ("GCC_WARN_MISSING_PARENTHESES", "YES"); 2419 s.set ("GCC_WARN_NON_VIRTUAL_DESTRUCTOR", "YES"); 2420 s.set ("GCC_WARN_TYPECHECK_CALLS_TO_PRINTF", "YES"); 2421 s.set ("GCC_WARN_64_TO_32_BIT_CONVERSION", "YES"); 2422 s.set ("GCC_WARN_UNDECLARED_SELECTOR", "YES"); 2423 s.set ("GCC_WARN_UNINITIALIZED_AUTOS", "YES"); 2424 s.set ("GCC_WARN_UNUSED_FUNCTION", "YES"); 2425 s.set ("CLANG_ENABLE_OBJC_WEAK", "YES"); 2426 s.set ("CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING", "YES"); 2427 s.set ("CLANG_WARN_BOOL_CONVERSION", "YES"); 2428 s.set ("CLANG_WARN_COMMA", "YES"); 2429 s.set ("CLANG_WARN_CONSTANT_CONVERSION", "YES"); 2430 s.set ("CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS", "YES"); 2431 s.set ("CLANG_WARN_EMPTY_BODY", "YES"); 2432 s.set ("CLANG_WARN_ENUM_CONVERSION", "YES"); 2433 s.set ("CLANG_WARN_INFINITE_RECURSION", "YES"); 2434 s.set ("CLANG_WARN_INT_CONVERSION", "YES"); 2435 s.set ("CLANG_WARN_NON_LITERAL_NULL_CONVERSION", "YES"); 2436 s.set ("CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF", "YES"); 2437 s.set ("CLANG_WARN_OBJC_LITERAL_CONVERSION", "YES"); 2438 s.set ("CLANG_WARN_RANGE_LOOP_ANALYSIS", "YES"); 2439 s.set ("CLANG_WARN_STRICT_PROTOTYPES", "YES"); 2440 s.set ("CLANG_WARN_SUSPICIOUS_MOVE", "YES"); 2441 s.set ("CLANG_WARN_UNREACHABLE_CODE", "YES"); 2442 s.set ("CLANG_WARN__DUPLICATE_METHOD_MATCH", "YES"); 2443 s.set ("WARNING_CFLAGS", "\"-Wreorder\""); 2444 s.set ("GCC_INLINES_ARE_PRIVATE_EXTERN", projectType.isStaticLibrary() ? "NO" : "YES"); 2445 2446 // GCC_SYMBOLS_PRIVATE_EXTERN only takes effect if ENABLE_TESTABILITY is off 2447 s.set ("ENABLE_TESTABILITY", "NO"); 2448 s.set ("GCC_SYMBOLS_PRIVATE_EXTERN", "YES"); 2449 2450 if (config.isDebug()) 2451 { 2452 if (config.getOSXArchitectureString() == osxArch_Default) 2453 s.set ("ONLY_ACTIVE_ARCH", "YES"); 2454 } 2455 2456 s.set (iOS ? "\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\"" : "CODE_SIGN_IDENTITY", 2457 getCodeSigningIdentity (config).quoted()); 2458 2459 if (iOS) 2460 { 2461 s.set ("SDKROOT", "iphoneos"); 2462 s.set ("TARGETED_DEVICE_FAMILY", getDeviceFamilyString().quoted()); 2463 s.set ("IPHONEOS_DEPLOYMENT_TARGET", config.getiOSDeploymentTargetString()); 2464 } 2465 else 2466 { 2467 s.set ("SDKROOT", getOSXSDKVersion (config.getOSXSDKVersionString())); 2468 } 2469 2470 s.set ("ZERO_LINK", "NO"); 2471 2472 if (xcodeCanUseDwarf) 2473 s.set ("DEBUG_INFORMATION_FORMAT", "dwarf"); 2474 2475 s.set ("PRODUCT_NAME", replacePreprocessorTokens (config, config.getTargetBinaryNameString()).quoted()); 2476 2477 return s; 2478 } 2479 addFrameworks()2480 void addFrameworks() const 2481 { 2482 if (! projectType.isStaticLibrary()) 2483 { 2484 if (isInAppPurchasesEnabled()) 2485 xcodeFrameworks.addIfNotAlreadyThere ("StoreKit"); 2486 2487 if (iOS && isPushNotificationsEnabled()) 2488 xcodeFrameworks.addIfNotAlreadyThere ("UserNotifications"); 2489 2490 if (iOS 2491 && project.getEnabledModules().isModuleEnabled ("juce_video") 2492 && project.isConfigFlagEnabled ("JUCE_USE_CAMERA", false)) 2493 { 2494 xcodeFrameworks.addIfNotAlreadyThere ("ImageIO"); 2495 } 2496 2497 xcodeFrameworks.addTokens (getExtraFrameworksString(), ",;", "\"'"); 2498 xcodeFrameworks.trim(); 2499 2500 auto s = xcodeFrameworks; 2501 2502 for (auto& target : targets) 2503 s.addArray (target->xcodeFrameworks); 2504 2505 if (! project.getConfigFlag ("JUCE_QUICKTIME").get()) 2506 s.removeString ("QuickTime"); 2507 2508 s.trim(); 2509 s.removeDuplicates (true); 2510 s.sort (true); 2511 2512 // When building against the 10.15 SDK we need to make sure the 2513 // AudioUnit framework is linked before the AudioToolbox framework. 2514 auto audioUnitIndex = s.indexOf ("AudioUnit", false, 1); 2515 2516 if (audioUnitIndex != -1) 2517 { 2518 s.remove (audioUnitIndex); 2519 s.insert (0, "AudioUnit"); 2520 } 2521 2522 for (auto& framework : s) 2523 { 2524 auto frameworkID = addFramework (framework); 2525 2526 // find all the targets that are referring to this object 2527 for (auto& target : targets) 2528 { 2529 if (xcodeFrameworks.contains (framework) || target->xcodeFrameworks.contains (framework)) 2530 { 2531 target->frameworkIDs.add (frameworkID); 2532 target->frameworkNames.add (framework); 2533 } 2534 } 2535 } 2536 } 2537 } 2538 addCustomFrameworks()2539 void addCustomFrameworks() const 2540 { 2541 StringArray customFrameworks; 2542 customFrameworks.addTokens (getExtraCustomFrameworksString(), true); 2543 customFrameworks.trim(); 2544 2545 for (auto& framework : customFrameworks) 2546 { 2547 auto frameworkID = addCustomFramework (framework); 2548 2549 for (auto& target : targets) 2550 { 2551 target->frameworkIDs.add (frameworkID); 2552 target->frameworkNames.add (framework); 2553 } 2554 } 2555 } 2556 addEmbeddedFrameworks()2557 void addEmbeddedFrameworks() const 2558 { 2559 StringArray frameworks; 2560 frameworks.addTokens (getEmbeddedFrameworksString(), true); 2561 frameworks.trim(); 2562 2563 for (auto& framework : frameworks) 2564 { 2565 auto frameworkID = addEmbeddedFramework (framework); 2566 embeddedFrameworkIDs.add (frameworkID); 2567 2568 for (auto& target : targets) 2569 { 2570 target->frameworkIDs.add (frameworkID); 2571 target->frameworkNames.add (framework); 2572 } 2573 } 2574 2575 if (! embeddedFrameworkIDs.isEmpty()) 2576 for (auto& target : targets) 2577 target->addCopyFilesPhase ("Embed Frameworks", embeddedFrameworkIDs, kFrameworksFolder); 2578 } 2579 addCustomResourceFolders()2580 void addCustomResourceFolders() const 2581 { 2582 StringArray folders; 2583 2584 folders.addTokens (getCustomResourceFoldersString(), ":", ""); 2585 folders.trim(); 2586 folders.removeEmptyStrings(); 2587 2588 for (auto& crf : folders) 2589 addCustomResourceFolder (crf); 2590 } 2591 addSubprojects()2592 void addSubprojects() const 2593 { 2594 auto subprojectLines = StringArray::fromLines (getSubprojectsString()); 2595 subprojectLines.removeEmptyStrings (true); 2596 2597 struct SubprojectInfo 2598 { 2599 String path; 2600 StringArray buildProducts; 2601 }; 2602 2603 std::vector<SubprojectInfo> subprojects; 2604 2605 for (auto& line : subprojectLines) 2606 { 2607 String subprojectPath (line.upToFirstOccurrenceOf (":", false, false)); 2608 2609 if (! subprojectPath.endsWith (".xcodeproj")) 2610 subprojectPath << ".xcodeproj"; 2611 2612 StringArray requestedBuildProducts (StringArray::fromTokens (line.fromFirstOccurrenceOf (":", false, false), ",;|", "\"'")); 2613 requestedBuildProducts.trim(); 2614 subprojects.push_back ({ subprojectPath, requestedBuildProducts }); 2615 } 2616 2617 for (const auto& subprojectInfo : subprojects) 2618 { 2619 auto subprojectFile = getTargetFolder().getChildFile (subprojectInfo.path); 2620 2621 if (! subprojectFile.isDirectory()) 2622 continue; 2623 2624 auto availableBuildProducts = XcodeProjectParser::parseBuildProducts (subprojectFile); 2625 2626 if (! subprojectInfo.buildProducts.isEmpty()) 2627 { 2628 auto newEnd = std::remove_if (availableBuildProducts.begin(), availableBuildProducts.end(), 2629 [&subprojectInfo] (const XcodeProjectParser::BuildProduct& item) 2630 { 2631 return ! subprojectInfo.buildProducts.contains (item.name); 2632 }); 2633 availableBuildProducts.erase (newEnd, availableBuildProducts.end()); 2634 } 2635 2636 if (availableBuildProducts.empty()) 2637 continue; 2638 2639 auto subprojectPath = build_tools::RelativePath (subprojectFile, 2640 getTargetFolder(), 2641 build_tools::RelativePath::buildTargetFolder).toUnixStyle(); 2642 2643 auto subprojectFileType = getFileType (subprojectPath); 2644 auto subprojectFileID = addFileOrFolderReference (subprojectPath, "<group>", subprojectFileType); 2645 subprojectFileIDs.add (subprojectFileID); 2646 2647 StringArray productIDs; 2648 2649 for (auto& buildProduct : availableBuildProducts) 2650 { 2651 auto buildProductFileType = getFileType (buildProduct.path); 2652 2653 auto dependencyProxyID = addContainerItemProxy (subprojectFileID, buildProduct.name, "1"); 2654 auto dependencyID = addTargetDependency (dependencyProxyID, buildProduct.name); 2655 subprojectDependencyIDs.add (dependencyID); 2656 2657 auto containerItemProxyReferenceID = addContainerItemProxy (subprojectFileID, buildProduct.name, "2"); 2658 auto proxyID = addReferenceProxy (containerItemProxyReferenceID, buildProduct.path, buildProductFileType); 2659 productIDs.add (proxyID); 2660 2661 if (StringArray { "archive.ar", "compiled.mach-o.dylib", "wrapper.framework" }.contains (buildProductFileType)) 2662 { 2663 auto buildFileID = addBuildFile (FileOptions().withPath (buildProduct.path) 2664 .withFileRefID (proxyID) 2665 .withInhibitWarningsEnabled (true)); 2666 2667 for (auto& target : targets) 2668 target->frameworkIDs.add (buildFileID); 2669 2670 if (buildProductFileType == "wrapper.framework") 2671 { 2672 auto fileID = createID (subprojectPath + "_" + buildProduct.path + "_framework_buildref"); 2673 2674 ValueTree v (fileID + " /* " + buildProduct.path + " */"); 2675 v.setProperty ("isa", "PBXBuildFile", nullptr); 2676 v.setProperty ("fileRef", proxyID, nullptr); 2677 v.setProperty ("settings", "{ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }", nullptr); 2678 2679 addObject (v); 2680 2681 embeddedFrameworkIDs.add (fileID); 2682 } 2683 } 2684 } 2685 2686 auto productGroupID = createFileRefID (subprojectFile.getFullPathName() + "_products"); 2687 addGroup (productGroupID, "Products", productIDs); 2688 2689 subprojectReferences.add ({ productGroupID, subprojectFileID }); 2690 } 2691 } 2692 addXcassets()2693 void addXcassets() const 2694 { 2695 auto customXcassetsPath = getCustomXcassetsFolderString(); 2696 2697 if (customXcassetsPath.isEmpty()) 2698 addDefaultXcassetsFolders(); 2699 else 2700 addCustomResourceFolder (customXcassetsPath, "folder.assetcatalog"); 2701 } 2702 2703 void addCustomResourceFolder (String folderPathRelativeToProjectFolder, const String fileType = "folder") const 2704 { 2705 auto folderPath = build_tools::RelativePath (folderPathRelativeToProjectFolder, build_tools::RelativePath::projectFolder) 2706 .rebased (projectFolder, getTargetFolder(), build_tools::RelativePath::buildTargetFolder) 2707 .toUnixStyle(); 2708 2709 auto fileRefID = createFileRefID (folderPath); 2710 2711 addFileOrFolderReference (folderPath, "<group>", fileType); 2712 2713 resourceIDs.add (addBuildFile (FileOptions().withPath (folderPath) 2714 .withFileRefID (fileRefID))); 2715 2716 resourceFileRefs.add (createFileRefID (folderPath)); 2717 } 2718 2719 //============================================================================== writeProjectFile(OutputStream & output)2720 void writeProjectFile (OutputStream& output) const 2721 { 2722 output << "// !$*UTF8*$!\n{\n" 2723 "\tarchiveVersion = 1;\n" 2724 "\tclasses = {\n\t};\n" 2725 "\tobjectVersion = 46;\n" 2726 "\tobjects = {\n"; 2727 2728 StringArray objectTypes; 2729 2730 for (auto it : objects) 2731 objectTypes.add (it.getType().toString()); 2732 2733 objectTypes.sort (false); 2734 2735 for (const auto& objectType : objectTypes) 2736 { 2737 auto objectsWithType = objects.getChildWithName (objectType); 2738 auto requiresSingleLine = objectType == "PBXBuildFile" || objectType == "PBXFileReference"; 2739 2740 output << "\n/* Begin " << objectType << " section */\n"; 2741 2742 for (const auto& o : objectsWithType) 2743 { 2744 auto label = [&o]() -> String 2745 { 2746 if (auto* objName = o.getPropertyPointer ("name")) 2747 return " /* " + objName->toString() + " */"; 2748 2749 return {}; 2750 }(); 2751 2752 output << "\t\t" << o.getType().toString() << label << " = {"; 2753 2754 if (! requiresSingleLine) 2755 output << "\n"; 2756 2757 for (int j = 0; j < o.getNumProperties(); ++j) 2758 { 2759 auto propertyName = o.getPropertyName (j); 2760 auto val = o.getProperty (propertyName).toString(); 2761 2762 if (val.isEmpty() || (val.containsAnyOf (" \t;<>()=,&+-@~\r\n\\#%^`*") 2763 && ! (val.trimStart().startsWithChar ('(') 2764 || val.trimStart().startsWithChar ('{')))) 2765 val = val.quoted(); 2766 2767 auto content = propertyName.toString() + " = " + val + ";"; 2768 2769 if (requiresSingleLine) 2770 content = content + " "; 2771 else 2772 content = "\t\t\t" + content + "\n"; 2773 2774 output << content; 2775 } 2776 2777 if (! requiresSingleLine) 2778 output << "\t\t"; 2779 2780 output << "};\n"; 2781 } 2782 2783 output << "/* End " << objectType << " section */\n"; 2784 } 2785 2786 output << "\t};\n\trootObject = " << createID ("__root") << " /* Project object */;\n}\n"; 2787 } 2788 2789 String addFileReference (String pathString, String fileType = {}) const 2790 { 2791 String sourceTree ("SOURCE_ROOT"); 2792 build_tools::RelativePath path (pathString, build_tools::RelativePath::unknown); 2793 2794 if (pathString.startsWith ("${")) 2795 { 2796 sourceTree = pathString.substring (2).upToFirstOccurrenceOf ("}", false, false); 2797 pathString = pathString.fromFirstOccurrenceOf ("}/", false, false); 2798 } 2799 else if (path.isAbsolute()) 2800 { 2801 sourceTree = "<absolute>"; 2802 } 2803 2804 return addFileOrFolderReference (pathString, sourceTree, fileType.isEmpty() ? getFileType (pathString) : fileType); 2805 } 2806 addFileOrFolderReference(const String & pathString,String sourceTree,String fileType)2807 String addFileOrFolderReference (const String& pathString, String sourceTree, String fileType) const 2808 { 2809 auto fileRefID = createFileRefID (pathString); 2810 auto filename = File::createFileWithoutCheckingPath (pathString).getFileName(); 2811 2812 ValueTree v (fileRefID + " /* " + filename + " */"); 2813 v.setProperty ("isa", "PBXFileReference", nullptr); 2814 v.setProperty ("lastKnownFileType", fileType, nullptr); 2815 v.setProperty (Ids::name, pathString.fromLastOccurrenceOf ("/", false, false), nullptr); 2816 v.setProperty ("path", pathString, nullptr); 2817 v.setProperty ("sourceTree", sourceTree, nullptr); 2818 2819 addObject (v); 2820 2821 return fileRefID; 2822 } 2823 addContainerItemProxy(const String & subprojectID,const String & itemName,const String & proxyType)2824 String addContainerItemProxy (const String& subprojectID, const String& itemName, const String& proxyType) const 2825 { 2826 auto uniqueString = subprojectID + "_" + itemName + "_" + proxyType; 2827 auto objectID = createFileRefID (uniqueString); 2828 2829 ValueTree v (objectID + " /* PBXContainerItemProxy */"); 2830 v.setProperty ("isa", "PBXContainerItemProxy", nullptr); 2831 v.setProperty ("containerPortal", subprojectID, nullptr); 2832 v.setProperty ("proxyType", proxyType, nullptr); 2833 v.setProperty ("remoteGlobalIDString", createFileRefID (uniqueString + "_global"), nullptr); 2834 v.setProperty ("remoteInfo", itemName, nullptr); 2835 2836 addObject (v); 2837 2838 return objectID; 2839 } 2840 addTargetDependency(const String & proxyID,const String & itemName)2841 String addTargetDependency (const String& proxyID, const String& itemName) const 2842 { 2843 auto objectID = createFileRefID (proxyID + "_" + itemName + "_PBXTargetDependency"); 2844 2845 ValueTree v (objectID); 2846 v.setProperty ("isa", "PBXTargetDependency", nullptr); 2847 v.setProperty ("name", itemName, nullptr); 2848 v.setProperty ("targetProxy", proxyID, nullptr); 2849 2850 addObject (v); 2851 2852 return objectID; 2853 } 2854 addReferenceProxy(const String & remoteRef,const String & path,const String & fileType)2855 String addReferenceProxy (const String& remoteRef, const String& path, const String& fileType) const 2856 { 2857 auto objectID = createFileRefID (remoteRef + "_" + path); 2858 2859 ValueTree v (objectID + " /* " + path + " */"); 2860 v.setProperty ("isa", "PBXReferenceProxy", nullptr); 2861 v.setProperty ("fileType", fileType, nullptr); 2862 v.setProperty ("path", path, nullptr); 2863 v.setProperty ("remoteRef", remoteRef, nullptr); 2864 v.setProperty ("sourceTree", "BUILT_PRODUCTS_DIR", nullptr); 2865 2866 addObject (v); 2867 2868 return objectID; 2869 } 2870 2871 private: 2872 struct FileOptions 2873 { withPathFileOptions2874 FileOptions& withPath (const String& p) { path = p; return *this; } withRelativePathFileOptions2875 FileOptions& withRelativePath (const build_tools::RelativePath& p) { path = p.toUnixStyle(); return *this; } withFileRefIDFileOptions2876 FileOptions& withFileRefID (const String& fid) { fileRefID = fid; return *this; } withCompilerFlagsFileOptions2877 FileOptions& withCompilerFlags (const String& f) { compilerFlags = f; return *this; } withCompilationEnabledFileOptions2878 FileOptions& withCompilationEnabled (bool e) { compile = e; return *this; } withAddToBinaryResourcesEnabledFileOptions2879 FileOptions& withAddToBinaryResourcesEnabled (bool e) { addToBinaryResources = e; return *this; } withAddToXcodeResourcesEnabledFileOptions2880 FileOptions& withAddToXcodeResourcesEnabled (bool e) { addToXcodeResources = e; return *this; } withInhibitWarningsEnabledFileOptions2881 FileOptions& withInhibitWarningsEnabled (bool e) { inhibitWarnings = e; return *this; } withSkipPCHEnabledFileOptions2882 FileOptions& withSkipPCHEnabled (bool e) { skipPCH = e; return *this; } withXcodeTargetFileOptions2883 FileOptions& withXcodeTarget (XcodeTarget* t) { xcodeTarget = t; return *this; } 2884 2885 String path; 2886 String fileRefID; 2887 String compilerFlags; 2888 bool compile = false; 2889 bool addToBinaryResources = false; 2890 bool addToXcodeResources = false; 2891 bool inhibitWarnings = false; 2892 bool skipPCH = false; 2893 XcodeTarget* xcodeTarget = nullptr; 2894 }; 2895 getFileType(const String & filePath)2896 static String getFileType (const String& filePath) 2897 { 2898 build_tools::RelativePath file (filePath, build_tools::RelativePath::unknown); 2899 2900 if (file.hasFileExtension (cppFileExtensions)) return "sourcecode.cpp.cpp"; 2901 if (file.hasFileExtension (".mm")) return "sourcecode.cpp.objcpp"; 2902 if (file.hasFileExtension (".m")) return "sourcecode.c.objc"; 2903 if (file.hasFileExtension (".c")) return "sourcecode.c.c"; 2904 if (file.hasFileExtension (headerFileExtensions)) return "sourcecode.c.h"; 2905 if (file.hasFileExtension (asmFileExtensions)) return "sourcecode.c.asm"; 2906 if (file.hasFileExtension (".framework")) return "wrapper.framework"; 2907 if (file.hasFileExtension (".jpeg;.jpg")) return "image.jpeg"; 2908 if (file.hasFileExtension ("png;gif")) return "image" + file.getFileExtension(); 2909 if (file.hasFileExtension ("html;htm")) return "text.html"; 2910 if (file.hasFileExtension ("xml;zip;wav")) return "file" + file.getFileExtension(); 2911 if (file.hasFileExtension ("txt;rtf")) return "text" + file.getFileExtension(); 2912 if (file.hasFileExtension ("plist")) return "text.plist.xml"; 2913 if (file.hasFileExtension ("entitlements")) return "text.plist.xml"; 2914 if (file.hasFileExtension ("app")) return "wrapper.application"; 2915 if (file.hasFileExtension ("component;vst;plugin")) return "wrapper.cfbundle"; 2916 if (file.hasFileExtension ("xcodeproj")) return "wrapper.pb-project"; 2917 if (file.hasFileExtension ("a")) return "archive.ar"; 2918 if (file.hasFileExtension ("dylib")) return "compiled.mach-o.dylib"; 2919 if (file.hasFileExtension ("xcassets")) return "folder.assetcatalog"; 2920 2921 return "file" + file.getFileExtension(); 2922 } 2923 addFile(const FileOptions & opts)2924 String addFile (const FileOptions& opts) const 2925 { 2926 auto refID = addFileReference (opts.path); 2927 2928 if (opts.compile || opts.addToXcodeResources) 2929 { 2930 auto fileID = addBuildFile (FileOptions (opts).withFileRefID (refID)); 2931 2932 if (opts.addToXcodeResources) 2933 { 2934 resourceIDs.add (fileID); 2935 resourceFileRefs.add (refID); 2936 } 2937 } 2938 2939 return refID; 2940 } 2941 addBuildFile(const FileOptions & opts)2942 String addBuildFile (const FileOptions& opts) const 2943 { 2944 auto fileID = createID (opts.path + "buildref"); 2945 auto filename = File::createFileWithoutCheckingPath (opts.path).getFileName(); 2946 2947 if (opts.compile) 2948 { 2949 if (opts.xcodeTarget != nullptr) 2950 opts.xcodeTarget->sourceIDs.add (fileID); 2951 else 2952 sourceIDs.add (fileID); 2953 } 2954 2955 ValueTree v (fileID + " /* " + filename + " */"); 2956 v.setProperty ("isa", "PBXBuildFile", nullptr); 2957 auto fileRefID = opts.fileRefID.isEmpty() ? createFileRefID (opts.path) 2958 : opts.fileRefID; 2959 v.setProperty ("fileRef", fileRefID, nullptr); 2960 2961 auto compilerFlags = [&opts] 2962 { 2963 return (opts.compilerFlags 2964 + (opts.inhibitWarnings ? " -w" : String()) 2965 + (opts.skipPCH ? " -D" + BuildConfiguration::getSkipPrecompiledHeaderDefine() : String())).trim(); 2966 }(); 2967 2968 if (compilerFlags.isNotEmpty()) 2969 v.setProperty ("settings", "{ COMPILER_FLAGS = \"" + compilerFlags + "\"; }", nullptr); 2970 2971 addObject (v); 2972 2973 return fileID; 2974 } 2975 addRezFile(const Project::Item & projectItem,const build_tools::RelativePath & path)2976 String addRezFile (const Project::Item& projectItem, const build_tools::RelativePath& path) const 2977 { 2978 auto refID = addFileReference (path.toUnixStyle()); 2979 2980 if (projectItem.isModuleCode()) 2981 { 2982 if (auto* xcodeTarget = getTargetOfType (getProject().getTargetTypeFromFilePath (projectItem.getFile(), false))) 2983 { 2984 auto rezFileID = addBuildFile (FileOptions().withRelativePath (path) 2985 .withFileRefID (refID) 2986 .withXcodeTarget (xcodeTarget)); 2987 2988 xcodeTarget->rezFileIDs.add (rezFileID); 2989 2990 return refID; 2991 } 2992 } 2993 2994 return {}; 2995 } 2996 addEntitlementsFile(XcodeTarget & target)2997 void addEntitlementsFile (XcodeTarget& target) const 2998 { 2999 build_tools::EntitlementOptions options; 3000 3001 options.type = target.type; 3002 options.isiOS = isiOS(); 3003 options.isAudioPluginProject = project.isAudioPluginProject(); 3004 options.shouldEnableIAA = project.shouldEnableIAA(); 3005 options.isiCloudPermissionsEnabled = isiCloudPermissionsEnabled(); 3006 options.isPushNotificationsEnabled = isPushNotificationsEnabled(); 3007 options.isAppGroupsEnabled = isAppGroupsEnabled(); 3008 options.isHardenedRuntimeEnabled = isHardenedRuntimeEnabled(); 3009 options.isAppSandboxEnabled = isAppSandboxEnabled(); 3010 options.isAppSandboxInhertianceEnabled = isAppSandboxInhertianceEnabled(); 3011 options.appGroupIdString = getAppGroupIdString(); 3012 options.hardenedRuntimeOptions = getHardenedRuntimeOptions(); 3013 options.appSandboxOptions = getAppSandboxOptions(); 3014 3015 const auto entitlementsFile = getTargetFolder().getChildFile (target.getEntitlementsFilename()); 3016 build_tools::overwriteFileIfDifferentOrThrow (entitlementsFile, options.getEntitlementsFileContent()); 3017 3018 build_tools::RelativePath entitlementsPath (entitlementsFile, getTargetFolder(), build_tools::RelativePath::buildTargetFolder); 3019 addFile (FileOptions().withRelativePath (entitlementsPath)); 3020 } 3021 addProjectItem(const Project::Item & projectItem)3022 String addProjectItem (const Project::Item& projectItem) const 3023 { 3024 if (modulesGroup != nullptr && projectItem.getParent() == *modulesGroup) 3025 return addFileReference (rebaseFromProjectFolderToBuildTarget (getModuleFolderRelativeToProject (projectItem.getName())).toUnixStyle(), 3026 "folder"); 3027 3028 if (projectItem.isGroup()) 3029 { 3030 StringArray childIDs; 3031 for (int i = 0; i < projectItem.getNumChildren(); ++i) 3032 { 3033 auto child = projectItem.getChild (i); 3034 3035 auto childID = addProjectItem (child); 3036 3037 if (childID.isNotEmpty() && ! child.shouldBeAddedToXcodeResources()) 3038 childIDs.add (childID); 3039 } 3040 3041 if (childIDs.isEmpty()) 3042 return {}; 3043 3044 return addGroup (projectItem, childIDs); 3045 } 3046 3047 if (projectItem.shouldBeAddedToTargetProject() && projectItem.shouldBeAddedToTargetExporter (*this)) 3048 { 3049 auto itemPath = projectItem.getFilePath(); 3050 build_tools::RelativePath path; 3051 3052 if (itemPath.startsWith ("${")) 3053 path = build_tools::RelativePath (itemPath, build_tools::RelativePath::unknown); 3054 else 3055 path = build_tools::RelativePath (projectItem.getFile(), getTargetFolder(), build_tools::RelativePath::buildTargetFolder); 3056 3057 if (path.hasFileExtension (".r")) 3058 return addRezFile (projectItem, path); 3059 3060 XcodeTarget* xcodeTarget = nullptr; 3061 if (projectItem.isModuleCode() && projectItem.shouldBeCompiled()) 3062 xcodeTarget = getTargetOfType (project.getTargetTypeFromFilePath (projectItem.getFile(), false)); 3063 3064 return addFile (FileOptions().withRelativePath (path) 3065 .withCompilerFlags (compilerFlagSchemesMap[projectItem.getCompilerFlagSchemeString()].get()) 3066 .withCompilationEnabled (projectItem.shouldBeCompiled()) 3067 .withAddToBinaryResourcesEnabled (projectItem.shouldBeAddedToBinaryResources()) 3068 .withAddToXcodeResourcesEnabled (projectItem.shouldBeAddedToXcodeResources()) 3069 .withInhibitWarningsEnabled (projectItem.shouldInhibitWarnings()) 3070 .withSkipPCHEnabled (isPCHEnabledForAnyConfigurations() && projectItem.shouldSkipPCH()) 3071 .withXcodeTarget (xcodeTarget)); 3072 } 3073 3074 return {}; 3075 } 3076 addFramework(const String & frameworkName)3077 String addFramework (const String& frameworkName) const 3078 { 3079 auto path = frameworkName; 3080 auto isRelativePath = path.startsWith ("../"); 3081 3082 if (! File::isAbsolutePath (path) && ! isRelativePath) 3083 path = "System/Library/Frameworks/" + path; 3084 3085 if (! path.endsWithIgnoreCase (".framework")) 3086 path << ".framework"; 3087 3088 auto fileRefID = createFileRefID (path); 3089 3090 addFileReference (((File::isAbsolutePath (frameworkName) || isRelativePath) ? "" : "${SDKROOT}/") + path); 3091 frameworkFileIDs.add (fileRefID); 3092 3093 return addBuildFile (FileOptions().withPath (path) 3094 .withFileRefID (fileRefID)); 3095 } 3096 addCustomFramework(String frameworkPath)3097 String addCustomFramework (String frameworkPath) const 3098 { 3099 if (! frameworkPath.endsWithIgnoreCase (".framework")) 3100 frameworkPath << ".framework"; 3101 3102 auto fileRefID = createFileRefID (frameworkPath); 3103 3104 auto fileType = getFileType (frameworkPath); 3105 addFileOrFolderReference (frameworkPath, "<group>", fileType); 3106 3107 frameworkFileIDs.add (fileRefID); 3108 3109 return addBuildFile (FileOptions().withPath (frameworkPath) 3110 .withFileRefID (fileRefID)); 3111 } 3112 addEmbeddedFramework(const String & path)3113 String addEmbeddedFramework (const String& path) const 3114 { 3115 auto fileRefID = createFileRefID (path); 3116 auto filename = File::createFileWithoutCheckingPath (path).getFileName(); 3117 3118 auto fileType = getFileType (path); 3119 addFileOrFolderReference (path, "<group>", fileType); 3120 3121 auto fileID = createID (path + "buildref"); 3122 3123 ValueTree v (fileID + " /* " + filename + " */"); 3124 v.setProperty ("isa", "PBXBuildFile", nullptr); 3125 v.setProperty ("fileRef", fileRefID, nullptr); 3126 v.setProperty ("settings", "{ ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }", nullptr); 3127 3128 addObject (v); 3129 3130 frameworkFileIDs.add (fileRefID); 3131 3132 return fileID; 3133 } 3134 addGroup(const String & groupID,const String & groupName,const StringArray & childIDs)3135 void addGroup (const String& groupID, const String& groupName, const StringArray& childIDs) const 3136 { 3137 ValueTree v (groupID); 3138 v.setProperty ("isa", "PBXGroup", nullptr); 3139 v.setProperty ("children", indentParenthesisedList (childIDs), nullptr); 3140 v.setProperty (Ids::name, groupName, nullptr); 3141 v.setProperty ("sourceTree", "<group>", nullptr); 3142 3143 addObject (v); 3144 } 3145 addGroup(const Project::Item & item,StringArray & childIDs)3146 String addGroup (const Project::Item& item, StringArray& childIDs) const 3147 { 3148 auto groupName = item.getName(); 3149 auto groupID = getIDForGroup (item); 3150 addGroup (groupID, groupName, childIDs); 3151 return groupID; 3152 } 3153 addProjectConfig(const String & configName,const StringArray & buildSettings)3154 void addProjectConfig (const String& configName, const StringArray& buildSettings) const 3155 { 3156 ValueTree v (createID ("projectconfigid_" + configName)); 3157 v.setProperty ("isa", "XCBuildConfiguration", nullptr); 3158 v.setProperty ("buildSettings", indentBracedList (buildSettings), nullptr); 3159 v.setProperty (Ids::name, configName, nullptr); 3160 3161 addObject (v); 3162 } 3163 addConfigList(XcodeTarget & target,const String & listID)3164 void addConfigList (XcodeTarget& target, const String& listID) const 3165 { 3166 ValueTree v (listID); 3167 v.setProperty ("isa", "XCConfigurationList", nullptr); 3168 v.setProperty ("buildConfigurations", indentParenthesisedList (target.configIDs), nullptr); 3169 v.setProperty ("defaultConfigurationIsVisible", (int) 0, nullptr); 3170 v.setProperty ("defaultConfigurationName", getConfiguration (0)->getName(), nullptr); 3171 3172 addObject (v); 3173 } 3174 addProjectConfigList(const String & listID)3175 void addProjectConfigList (const String& listID) const 3176 { 3177 auto buildConfigs = objects.getChildWithName ("XCBuildConfiguration"); 3178 jassert (buildConfigs.isValid()); 3179 3180 StringArray configIDs; 3181 3182 for (const auto& child : buildConfigs) 3183 configIDs.add (child.getType().toString()); 3184 3185 ValueTree v (listID); 3186 v.setProperty ("isa", "XCConfigurationList", nullptr); 3187 v.setProperty ("buildConfigurations", indentParenthesisedList (configIDs), nullptr); 3188 v.setProperty ("defaultConfigurationIsVisible", (int) 0, nullptr); 3189 v.setProperty ("defaultConfigurationName", getConfiguration (0)->getName(), nullptr); 3190 3191 addObject (v); 3192 } 3193 addProjectObject()3194 void addProjectObject() const 3195 { 3196 ValueTree v (createID ("__root")); 3197 v.setProperty ("isa", "PBXProject", nullptr); 3198 v.setProperty ("attributes", indentBracedList (getProjectObjectAttributes()), nullptr); 3199 v.setProperty ("buildConfigurationList", createID ("__projList"), nullptr); 3200 v.setProperty ("compatibilityVersion", "Xcode 3.2", nullptr); 3201 v.setProperty ("hasScannedForEncodings", (int) 0, nullptr); 3202 v.setProperty ("knownRegions", indentParenthesisedList ({ "en", "Base" }), nullptr); 3203 v.setProperty ("mainGroup", createID ("__mainsourcegroup"), nullptr); 3204 v.setProperty ("projectDirPath", "\"\"", nullptr); 3205 3206 if (! subprojectReferences.isEmpty()) 3207 { 3208 StringArray projectReferences; 3209 3210 for (auto& reference : subprojectReferences) 3211 projectReferences.add (indentBracedList ({ "ProductGroup = " + reference.productGroup, "ProjectRef = " + reference.projectRef }, 1)); 3212 3213 v.setProperty ("projectReferences", indentParenthesisedList (projectReferences), nullptr); 3214 } 3215 3216 v.setProperty ("projectRoot", "\"\"", nullptr); 3217 3218 v.setProperty ("targets", indentParenthesisedList (targetIDs), nullptr); 3219 3220 addObject (v); 3221 } 3222 3223 //============================================================================== removeMismatchedXcuserdata()3224 void removeMismatchedXcuserdata() const 3225 { 3226 if (shouldKeepCustomXcodeSchemes()) 3227 return; 3228 3229 auto xcuserdata = getProjectBundle().getChildFile ("xcuserdata"); 3230 3231 if (! xcuserdata.exists()) 3232 return; 3233 3234 if (! xcuserdataMatchesTargets (xcuserdata)) 3235 { 3236 xcuserdata.deleteRecursively(); 3237 getProjectBundle().getChildFile ("xcshareddata").getChildFile ("xcschemes").deleteRecursively(); 3238 getProjectBundle().getChildFile ("project.xcworkspace").deleteRecursively(); 3239 } 3240 } 3241 xcuserdataMatchesTargets(const File & xcuserdata)3242 bool xcuserdataMatchesTargets (const File& xcuserdata) const 3243 { 3244 for (auto& plist : xcuserdata.findChildFiles (File::findFiles, true, "xcschememanagement.plist")) 3245 if (! xcschemeManagementPlistMatchesTargets (plist)) 3246 return false; 3247 3248 return true; 3249 } 3250 parseNamesOfTargetsFromPlist(const XmlElement & dictXML)3251 static StringArray parseNamesOfTargetsFromPlist (const XmlElement& dictXML) 3252 { 3253 for (auto* schemesKey : dictXML.getChildWithTagNameIterator ("key")) 3254 { 3255 if (schemesKey->getAllSubText().trim().equalsIgnoreCase ("SchemeUserState")) 3256 { 3257 if (auto* dict = schemesKey->getNextElement()) 3258 { 3259 if (dict->hasTagName ("dict")) 3260 { 3261 StringArray names; 3262 3263 for (auto* key : dict->getChildWithTagNameIterator ("key")) 3264 names.add (key->getAllSubText().upToLastOccurrenceOf (".xcscheme", false, false).trim()); 3265 3266 names.sort (false); 3267 return names; 3268 } 3269 } 3270 } 3271 } 3272 3273 return {}; 3274 } 3275 getNamesOfTargets()3276 StringArray getNamesOfTargets() const 3277 { 3278 StringArray names; 3279 3280 for (auto& target : targets) 3281 names.add (target->getXcodeSchemeName()); 3282 3283 names.sort (false); 3284 return names; 3285 } 3286 xcschemeManagementPlistMatchesTargets(const File & plist)3287 bool xcschemeManagementPlistMatchesTargets (const File& plist) const 3288 { 3289 if (auto xml = parseXML (plist)) 3290 if (auto* dict = xml->getChildByName ("dict")) 3291 return parseNamesOfTargetsFromPlist (*dict) == getNamesOfTargets(); 3292 3293 return false; 3294 } 3295 getProjectObjectAttributes()3296 StringArray getProjectObjectAttributes() const 3297 { 3298 std::map<String, String> attributes; 3299 3300 attributes["LastUpgradeCheck"] = "1240"; 3301 attributes["ORGANIZATIONNAME"] = getProject().getCompanyNameString().quoted(); 3302 3303 if (projectType.isGUIApplication() || projectType.isAudioPlugin()) 3304 { 3305 StringArray targetAttributes; 3306 3307 for (auto& target : targets) 3308 targetAttributes.add (target->getTargetAttributes()); 3309 3310 attributes["TargetAttributes"] = indentBracedList (targetAttributes, 1); 3311 } 3312 3313 StringArray result; 3314 3315 for (const auto& attrib : attributes) 3316 result.add (attrib.first + " = " + attrib.second); 3317 3318 return result; 3319 } 3320 3321 //============================================================================== writeDefaultLaunchStoryboardFile()3322 void writeDefaultLaunchStoryboardFile() const 3323 { 3324 const auto storyboardFile = getTargetFolder().getChildFile (getDefaultLaunchStoryboardName() + ".storyboard"); 3325 3326 build_tools::writeStreamToFile (storyboardFile, [&] (MemoryOutputStream& mo) 3327 { 3328 mo << String (BinaryData::LaunchScreen_storyboard); 3329 }); 3330 3331 addLaunchStoryboardFileReference (build_tools::RelativePath (storyboardFile, 3332 getTargetFolder(), 3333 build_tools::RelativePath::buildTargetFolder)); 3334 } 3335 addLaunchStoryboardFileReference(const build_tools::RelativePath & relativePath)3336 void addLaunchStoryboardFileReference (const build_tools::RelativePath& relativePath) const 3337 { 3338 auto path = relativePath.toUnixStyle(); 3339 3340 auto refID = addFileReference (path); 3341 auto fileID = addBuildFile (FileOptions().withPath (path) 3342 .withFileRefID (refID)); 3343 3344 resourceIDs.add (fileID); 3345 resourceFileRefs.add (refID); 3346 } 3347 addDefaultXcassetsFolders()3348 void addDefaultXcassetsFolders() const 3349 { 3350 const auto assetsPath = build_tools::createXcassetsFolderFromIcons (getIcons(), 3351 getTargetFolder(), 3352 project.getProjectFilenameRootString()); 3353 addFileReference (assetsPath.toUnixStyle()); 3354 resourceIDs.add (addBuildFile (FileOptions().withRelativePath (assetsPath))); 3355 resourceFileRefs.add (createFileRefID (assetsPath)); 3356 } 3357 3358 //============================================================================== 3359 static String indentBracedList (const StringArray& list, int depth = 0) { return indentList (list, '{', '}', ";", depth, true); } 3360 static String indentParenthesisedList (const StringArray& list, int depth = 0) { return indentList (list, '(', ')', ",", depth, false); } 3361 indentList(StringArray list,char openBracket,char closeBracket,const String & separator,int extraTabs,bool shouldSort)3362 static String indentList (StringArray list, char openBracket, char closeBracket, const String& separator, int extraTabs, bool shouldSort) 3363 { 3364 auto content = [extraTabs, shouldSort, &list, &separator] () -> String 3365 { 3366 if (list.isEmpty()) 3367 return ""; 3368 3369 if (shouldSort) 3370 list.sort (true); 3371 3372 auto tabs = String::repeatedString ("\t", extraTabs + 4); 3373 return tabs + list.joinIntoString (separator + "\n" + tabs) + separator + "\n"; 3374 }(); 3375 3376 return openBracket + String ("\n") 3377 + content 3378 + String::repeatedString ("\t", extraTabs + 3) + closeBracket; 3379 } 3380 createID(String rootString)3381 String createID (String rootString) const 3382 { 3383 if (rootString.startsWith ("${")) 3384 rootString = rootString.fromFirstOccurrenceOf ("}/", false, false); 3385 3386 rootString += project.getProjectUIDString(); 3387 3388 return MD5 (rootString.toUTF8()).toHexString().substring (0, 24).toUpperCase(); 3389 } 3390 createFileRefID(const build_tools::RelativePath & path)3391 String createFileRefID (const build_tools::RelativePath& path) const { return createFileRefID (path.toUnixStyle()); } createFileRefID(const String & path)3392 String createFileRefID (const String& path) const { return createID ("__fileref_" + path); } getIDForGroup(const Project::Item & item)3393 String getIDForGroup (const Project::Item& item) const { return createID (item.getID()); } 3394 shouldFileBeCompiledByDefault(const File & file)3395 bool shouldFileBeCompiledByDefault (const File& file) const override 3396 { 3397 return file.hasFileExtension (sourceFileExtensions); 3398 } 3399 3400 //============================================================================== updateOldOrientationSettings()3401 void updateOldOrientationSettings() 3402 { 3403 jassert (iOS); 3404 3405 StringArray orientationSettingStrings { getSetting (Ids::iPhoneScreenOrientation).getValue().toString(), 3406 getSetting (Ids::iPadScreenOrientation).getValue().toString() }; 3407 3408 for (int i = 0; i < 2; ++i) 3409 { 3410 auto& settingsString = orientationSettingStrings[i]; 3411 3412 if (settingsString.isNotEmpty()) 3413 { 3414 Array<var> orientations; 3415 3416 if (settingsString.contains ("portrait")) orientations.add ("UIInterfaceOrientationPortrait"); 3417 if (settingsString.contains ("landscape")) orientations.addArray ({ "UIInterfaceOrientationLandscapeLeft", 3418 "UIInterfaceOrientationLandscapeRight" }); 3419 3420 if (! orientations.isEmpty()) 3421 { 3422 if (i == 0) 3423 iPhoneScreenOrientationValue = orientations; 3424 else 3425 iPadScreenOrientationValue = orientations; 3426 } 3427 } 3428 } 3429 } 3430 addObject(ValueTree data)3431 void addObject (ValueTree data) const 3432 { 3433 if (auto* type = data.getPropertyPointer ("isa")) 3434 { 3435 auto objs = objects.getOrCreateChildWithName (type->toString(), nullptr); 3436 auto objectID = data.getType(); 3437 auto numChildren = objs.getNumChildren(); 3438 3439 for (int i = 0; i < numChildren; ++i) 3440 { 3441 auto obj = objs.getChild (i); 3442 auto childID = obj.getType(); 3443 3444 if (objectID < childID) 3445 { 3446 objs.addChild (data, i, nullptr); 3447 return; 3448 } 3449 3450 if (objectID == childID) 3451 { 3452 jassert (obj.isEquivalentTo (data)); 3453 return; 3454 } 3455 } 3456 3457 objs.appendChild (data, nullptr); 3458 return; 3459 } 3460 3461 jassertfalse; 3462 } 3463 3464 //============================================================================== 3465 friend class CLionProjectExporter; 3466 3467 bool xcodeCanUseDwarf; 3468 OwnedArray<XcodeTarget> targets; 3469 3470 mutable ValueTree objects { "objects" }; 3471 3472 mutable StringArray resourceIDs, sourceIDs, targetIDs, frameworkFileIDs, embeddedFrameworkIDs, 3473 rezFileIDs, resourceFileRefs, subprojectFileIDs, subprojectDependencyIDs; 3474 3475 struct SubprojectReferenceInfo 3476 { 3477 String productGroup, projectRef; 3478 }; 3479 3480 mutable Array<SubprojectReferenceInfo> subprojectReferences; 3481 mutable File menuNibFile, iconFile; 3482 mutable StringArray buildProducts; 3483 3484 const bool iOS; 3485 3486 ValueWithDefault customPListValue, pListPrefixHeaderValue, pListPreprocessValue, 3487 subprojectsValue, 3488 validArchsValue, 3489 extraFrameworksValue, frameworkSearchPathsValue, extraCustomFrameworksValue, embeddedFrameworksValue, 3490 postbuildCommandValue, prebuildCommandValue, 3491 duplicateAppExResourcesFolderValue, iosDeviceFamilyValue, iPhoneScreenOrientationValue, 3492 iPadScreenOrientationValue, customXcodeResourceFoldersValue, customXcassetsFolderValue, 3493 appSandboxValue, appSandboxInheritanceValue, appSandboxOptionsValue, 3494 hardenedRuntimeValue, hardenedRuntimeOptionsValue, 3495 microphonePermissionNeededValue, microphonePermissionsTextValue, 3496 cameraPermissionNeededValue, cameraPermissionTextValue, 3497 bluetoothPermissionNeededValue, bluetoothPermissionTextValue, 3498 sendAppleEventsPermissionNeededValue, sendAppleEventsPermissionTextValue, 3499 uiFileSharingEnabledValue, uiSupportsDocumentBrowserValue, uiStatusBarHiddenValue, uiRequiresFullScreenValue, documentExtensionsValue, iosInAppPurchasesValue, 3500 iosContentSharingValue, iosBackgroundAudioValue, iosBackgroundBleValue, iosPushNotificationsValue, iosAppGroupsValue, iCloudPermissionsValue, 3501 iosDevelopmentTeamIDValue, iosAppGroupsIDValue, keepCustomXcodeSchemesValue, useHeaderMapValue, customLaunchStoryboardValue, 3502 exporterBundleIdentifierValue, suppressPlistResourceUsageValue, useLegacyBuildSystemValue; 3503 3504 JUCE_DECLARE_NON_COPYABLE (XcodeProjectExporter) 3505 }; 3506