diff --git a/extras/Build/juce_build_tools/utils/juce_Entitlements.cpp b/extras/Build/juce_build_tools/utils/juce_Entitlements.cpp
index 3696035050..5835a7e2ec 100644
--- a/extras/Build/juce_build_tools/utils/juce_Entitlements.cpp
+++ b/extras/Build/juce_build_tools/utils/juce_Entitlements.cpp
@@ -123,6 +123,17 @@ namespace juce:: build_tools
paths += "\n\t";
entitlements.set (option.key, paths);
}
+
+ if (! appSandboxExceptionIOKit.isEmpty())
+ {
+ String ioKitClasses = "";
+
+ for (const auto& c : appSandboxExceptionIOKit)
+ ioKitClasses += "\n\t\t" + c + "";
+
+ ioKitClasses += "\n\t";
+ entitlements.set ("com.apple.security.temporary-exception.iokit-user-client-class", ioKitClasses);
+ }
}
}
diff --git a/extras/Build/juce_build_tools/utils/juce_Entitlements.h b/extras/Build/juce_build_tools/utils/juce_Entitlements.h
index dd8f60e3e1..06e86fef65 100644
--- a/extras/Build/juce_build_tools/utils/juce_Entitlements.h
+++ b/extras/Build/juce_build_tools/utils/juce_Entitlements.h
@@ -56,6 +56,7 @@ namespace juce::build_tools
};
std::vector appSandboxTemporaryPaths;
+ StringArray appSandboxExceptionIOKit;
private:
StringPairArray getEntitlements() const;
diff --git a/extras/Projucer/Source/Application/jucer_MainWindow.cpp b/extras/Projucer/Source/Application/jucer_MainWindow.cpp
index 002bfa618a..d2884fafbd 100644
--- a/extras/Projucer/Source/Application/jucer_MainWindow.cpp
+++ b/extras/Projucer/Source/Application/jucer_MainWindow.cpp
@@ -385,8 +385,6 @@ void MainWindow::openFile (const File& file, std::function callback
parent->createProjectContentCompIfNeeded();
parent->getProjectContentComponent()->reloadLastOpenDocuments();
-
- parent->currentProject->updateDeprecatedProjectSettingsInteractively();
}
NullCheckedInvocation::invoke (callback, saveResult);
diff --git a/extras/Projucer/Source/Project/jucer_Project.cpp b/extras/Projucer/Source/Project/jucer_Project.cpp
index be7c103aad..26704bf630 100644
--- a/extras/Projucer/Source/Project/jucer_Project.cpp
+++ b/extras/Projucer/Source/Project/jucer_Project.cpp
@@ -261,14 +261,6 @@ void Project::updateDeprecatedProjectSettings()
exporter->updateDeprecatedSettings();
}
-void Project::updateDeprecatedProjectSettingsInteractively()
-{
- jassert (! ProjucerApplication::getApp().isRunningCommandLine);
-
- for (ExporterIterator exporter (*this); exporter.next();)
- exporter->updateDeprecatedSettingsInteractively();
-}
-
void Project::initialiseMainGroup()
{
// Create main file group if missing
@@ -462,8 +454,8 @@ void Project::removeDefunctExporters()
warningMessage << "\n"
<< TRANS ("These exporters have been removed from the project. If you save the project they will be also erased from the .jucer file.");
- auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, warningTitle, warningMessage);
- messageBox = AlertWindow::showScopedAsync (options, nullptr);
+ exporterRemovalMessageBoxOptions = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, warningTitle, warningMessage);
+ messageBoxQueueListenerScope = messageBoxQueue.addListener (*this);
}
}
}
@@ -1169,6 +1161,14 @@ void Project::valueTreeChildAddedOrRemoved (ValueTree& parent, ValueTree& child)
changed();
}
+void Project::canCreateMessageBox (CreatorFunction f)
+{
+ messageBox = f (*exporterRemovalMessageBoxOptions, [this] (auto)
+ {
+ messageBoxQueueListenerScope.reset();
+ });
+}
+
void Project::valueTreeChildAdded (ValueTree& parent, ValueTree& child)
{
valueTreeChildAddedOrRemoved (parent, child);
diff --git a/extras/Projucer/Source/Project/jucer_Project.h b/extras/Projucer/Source/Project/jucer_Project.h
index c9d91a4fa3..d258d34578 100644
--- a/extras/Projucer/Source/Project/jucer_Project.h
+++ b/extras/Projucer/Source/Project/jucer_Project.h
@@ -117,6 +117,64 @@ namespace ProjectMessages
using MessageAction = std::pair>;
}
+// Can be shared between multiple classes wanting to create a MessageBox. Ensures that there is one
+// MessageBox active at a given time.
+class MessageBoxQueue : private AsyncUpdater
+{
+public:
+ struct Listener
+ {
+ using CreatorFunction = std::function)>;
+
+ virtual ~Listener() = default;
+
+ virtual void canCreateMessageBox (CreatorFunction) = 0;
+ };
+
+ void handleAsyncUpdate()
+ {
+ schedule();
+ }
+
+ auto addListener (Listener& l)
+ {
+ triggerAsyncUpdate();
+ return listeners.addScoped (l);
+ }
+
+private:
+ ScopedMessageBox create (MessageBoxOptions options, std::function callback)
+ {
+ hasActiveMessageBox = true;
+
+ return AlertWindow::showScopedAsync (options, [this, cb = std::move (callback)] (int result)
+ {
+ cb (result);
+ hasActiveMessageBox = false;
+ triggerAsyncUpdate();
+ });
+ }
+
+ void schedule()
+ {
+ if (hasActiveMessageBox)
+ return;
+
+ auto& currentListeners = listeners.getListeners();
+
+ if (! currentListeners.isEmpty())
+ {
+ currentListeners[0]->canCreateMessageBox ([this] (auto o, auto c)
+ {
+ return create (o, c);
+ });
+ }
+ }
+
+ ListenerList listeners;
+ bool hasActiveMessageBox = false;
+};
+
enum class Async { no, yes };
//==============================================================================
@@ -124,7 +182,8 @@ class Project final : public FileBasedDocument,
private ValueTree::Listener,
private LicenseController::LicenseStateListener,
private ChangeListener,
- private AvailableModulesList::Listener
+ private AvailableModulesList::Listener,
+ private MessageBoxQueue::Listener
{
public:
//==============================================================================
@@ -355,8 +414,6 @@ public:
static build_tools::ProjectType::Target::Type getTargetTypeFromFilePath (const File& file, bool returnSharedTargetIfNoValidSuffix);
//==============================================================================
- void updateDeprecatedProjectSettingsInteractively();
-
StringPairArray getAppConfigDefs();
StringPairArray getAudioPluginFlags() const;
@@ -548,6 +605,8 @@ public:
bool isFileModificationCheckPending() const;
bool isSaveAndExportDisabled() const;
+ MessageBoxQueue messageBoxQueue;
+
private:
//==============================================================================
void valueTreePropertyChanged (ValueTree&, const Identifier&) override;
@@ -557,6 +616,9 @@ private:
void valueTreeChildAddedOrRemoved (ValueTree&, ValueTree&);
+ //==============================================================================
+ void canCreateMessageBox (CreatorFunction) override;
+
//==============================================================================
template
static auto& getEnabledModulesImpl (This&);
@@ -667,6 +729,9 @@ private:
std::unique_ptr chooser;
std::unique_ptr saver;
+
+ std::optional exporterRemovalMessageBoxOptions;
+ ErasedScopeGuard messageBoxQueueListenerScope;
ScopedMessageBox messageBox;
//==============================================================================
diff --git a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Xcode.h b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Xcode.h
index bd73fcc684..57d7d9400b 100644
--- a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Xcode.h
+++ b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Xcode.h
@@ -54,7 +54,8 @@ xcrun codesign --verify "$JUCE_FULL_PRODUCT_PATH" || xcrun codesign -f -s - "$JU
)";
//==============================================================================
-class XcodeProjectExporter final : public ProjectExporter
+class XcodeProjectExporter final : public ProjectExporter,
+ private MessageBoxQueue::Listener
{
public:
//==============================================================================
@@ -97,6 +98,7 @@ public:
appSandboxHomeDirRWValue (settings, Ids::appSandboxHomeDirRW, getUndoManager()),
appSandboxAbsDirROValue (settings, Ids::appSandboxAbsDirRO, getUndoManager()),
appSandboxAbsDirRWValue (settings, Ids::appSandboxAbsDirRW, getUndoManager()),
+ appSandboxExceptionIOKitValue (settings, Ids::appSandboxExceptionIOKit, getUndoManager()),
hardenedRuntimeValue (settings, Ids::hardenedRuntime, getUndoManager()),
hardenedRuntimeOptionsValue (settings, Ids::hardenedRuntimeOptions, getUndoManager(), Array(), ","),
microphonePermissionNeededValue (settings, Ids::microphonePermissionNeeded, getUndoManager()),
@@ -144,6 +146,11 @@ public:
name = getDisplayNameMac();
targetLocationValue.setDefault (getDefaultBuildsRootFolder() + getTargetFolderNameMac());
}
+
+ if (needsDisplayMessageBox())
+ {
+ messageBoxQueueListenerScope = project.messageBoxQueue.addListener (*this);
+ }
}
static XcodeProjectExporter* createForSettings (Project& projectToUse, const ValueTree& settingsToUse)
@@ -224,6 +231,11 @@ public:
return result;
}
+ StringArray getAppSandboxExceptionIOKitClasses() const
+ {
+ return getCommaOrWhitespaceSeparatedItems (appSandboxExceptionIOKitValue.get());
+ }
+
Array getValidArchs() const { return *validArchsValue.get().getArray(); }
bool isMicrophonePermissionEnabled() const { return microphonePermissionNeededValue.get(); }
@@ -510,7 +522,6 @@ public:
{ "Temporary Exception: Audio Unit Hosting", "temporary-exception.audio-unit-host" },
{ "Temporary Exception: Global Mach Service", "temporary-exception.mach-lookup.global-name" },
{ "Temporary Exception: Global Mach Service Dynamic Registration", "temporary-exception.mach-register.global-name" },
- { "Temporary Exception: IOKit User Client Class", "temporary-exception.iokit-user-client-class" },
{ "Temporary Exception: Shared Preference Domain (Read Only)", "temporary-exception.shared-preference.read-only" },
{ "Temporary Exception: Shared Preference Domain (Read/Write)", "temporary-exception.shared-preference.read-write" }
};
@@ -541,6 +552,14 @@ public:
"See Apple's File Access Temporary Exceptions documentation.");
}
+ props.add (new TextPropertyComponentWithEnablement (appSandboxExceptionIOKitValue,
+ appSandboxValue,
+ "App sandbox temporary exception: additional IOUserClient subclasses",
+ 8192,
+ true),
+ "A list of IOUserClient subclasses to open or to set properties on. "
+ "See Apple's IOKit User Client Class Temporary Exception documentation.");
+
props.add (new ChoicePropertyComponent (hardenedRuntimeValue, "Use Hardened Runtime"),
"Enable this to use the hardened runtime required for app notarization.");
@@ -781,32 +800,30 @@ public:
updateOldOrientationSettings();
}
- void updateDeprecatedSettingsInteractively() override
- {
- if (hasInvalidPostBuildScript())
- {
- String alertWindowText = iOS ? "Your Xcode (iOS) Exporter settings use an invalid post-build script. Click 'Update' to remove it."
- : "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"
- "Since JUCE 4.2, this is instead done using \"AU/VST/VST2/AAX Binary Location\" in the Xcode (OS X) configuration settings.\n\n"
- "Click 'Update' to remove the script (otherwise your plug-in may not compile correctly).";
-
- auto options = MessageBoxOptions::makeOptionsOkCancel (MessageBoxIconType::WarningIcon,
- "Project settings: " + project.getDocumentTitle(),
- alertWindowText,
- "Update",
- "Cancel");
- messageBox = AlertWindow::showScopedAsync (options, [this] (int result)
- {
- if (result != 0)
- postbuildCommandValue.resetToDefault();
- });
- }
- }
-
bool hasInvalidPostBuildScript() const
{
// check whether the script is identical to the old one that the Introjucer used to auto-generate
- return (MD5 (getPostBuildScript().toUTF8()).toHexString() == "265ac212a7e734c5bbd6150e1eae18a1");
+ return ! userAcknowledgedInvalidPostBuildScript
+ && (MD5 (getPostBuildScript().toUTF8()).toHexString() == "265ac212a7e734c5bbd6150e1eae18a1");
+ }
+
+ bool hasDefunctIOKitSetting() const
+ {
+ auto v = appSandboxOptionsValue.get();
+
+ if (! v.isArray())
+ {
+ jassertfalse;
+ return false;
+ }
+
+ return ! userAcknowledgedDefunctIOKitSetting
+ && v.getArray()->contains ("com.apple.security.temporary-exception.iokit-user-client-class");
+ }
+
+ bool needsDisplayMessageBox() const
+ {
+ return hasInvalidPostBuildScript() || hasDefunctIOKitSetting();
}
//==============================================================================
@@ -2082,6 +2099,62 @@ private:
File getProjectBundle() const { return getTargetFolder().getChildFile (project.getProjectFilenameRootString()).withFileExtension (".xcodeproj"); }
+ void canCreateMessageBox (CreatorFunction f) override
+ {
+ if (hasInvalidPostBuildScript())
+ {
+ String alertWindowText = iOS ? "Your Xcode (iOS) Exporter settings use an invalid post-build script. Click 'Update' to remove it."
+ : "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"
+ "Since JUCE 4.2, this is instead done using \"AU/VST/VST2/AAX Binary Location\" in the Xcode (OS X) configuration settings.\n\n"
+ "Click 'Update' to remove the script (otherwise your plug-in may not compile correctly).";
+
+ auto options = MessageBoxOptions::makeOptionsOkCancel (MessageBoxIconType::WarningIcon,
+ "Project settings: " + project.getDocumentTitle(),
+ alertWindowText,
+ "Update",
+ "Cancel");
+
+ messageBox = f (options, [this] (int result)
+ {
+ userAcknowledgedInvalidPostBuildScript = true;
+
+ if (result != 0)
+ postbuildCommandValue.resetToDefault();
+
+ if (! needsDisplayMessageBox())
+ messageBoxQueueListenerScope.reset();
+ });
+ }
+ else if (hasDefunctIOKitSetting())
+ {
+ String alertWindowText = "Your Xcode (macOS) Exporter settings use a defunct, boolean value for the iokit-user-client-class temporary exception entitlement.\n\n"
+ "If you need this entitlement, add the IOUserClient subclasses to the new IOKit exception related field.\n\n"
+ "For more information see Apple's IOKit User Client Class Temporary Exception documentation.\n\n"
+ "Clicking 'Update' will remove the defunct setting from your project.";
+
+ auto options = MessageBoxOptions::makeOptionsOkCancel (MessageBoxIconType::WarningIcon,
+ "Project settings: " + project.getDocumentTitle(),
+ alertWindowText,
+ "Update",
+ "Cancel");
+
+ messageBox = f (std::move (options), [this] (int result)
+ {
+ userAcknowledgedDefunctIOKitSetting = true;
+
+ if (result != 0)
+ {
+ auto v = appSandboxOptionsValue.get();
+ v.getArray()->removeAllInstancesOf ("com.apple.security.temporary-exception.iokit-user-client-class");
+ appSandboxOptionsValue.setValue (v, nullptr);
+ }
+
+ if (! needsDisplayMessageBox())
+ messageBoxQueueListenerScope.reset();
+ });
+ }
+ }
+
//==============================================================================
void createObjects() const
{
@@ -3267,6 +3340,7 @@ private:
options.hardenedRuntimeOptions = getHardenedRuntimeOptions();
options.appSandboxOptions = getAppSandboxOptions();
options.appSandboxTemporaryPaths = getAppSandboxTemporaryPaths();
+ options.appSandboxExceptionIOKit = getAppSandboxExceptionIOKitClasses();
const auto entitlementsFile = getTargetFolder().getChildFile (target.getEntitlementsFilename());
build_tools::overwriteFileIfDifferentOrThrow (entitlementsFile, options.getEntitlementsFileContent());
@@ -3748,6 +3822,7 @@ private:
iPadScreenOrientationValue, customXcodeResourceFoldersValue, customXcassetsFolderValue,
appSandboxValue, appSandboxInheritanceValue, appSandboxOptionsValue,
appSandboxHomeDirROValue, appSandboxHomeDirRWValue, appSandboxAbsDirROValue, appSandboxAbsDirRWValue,
+ appSandboxExceptionIOKitValue,
hardenedRuntimeValue, hardenedRuntimeOptionsValue,
microphonePermissionNeededValue, microphonePermissionsTextValue,
cameraPermissionNeededValue, cameraPermissionTextValue,
@@ -3757,7 +3832,6 @@ private:
iosContentSharingValue, iosBackgroundAudioValue, iosBackgroundBleValue, iosPushNotificationsValue, iosAppGroupsValue, iCloudPermissionsValue,
networkingMulticastValue, iosDevelopmentTeamIDValue, iosAppGroupsIDValue, keepCustomXcodeSchemesValue, useHeaderMapValue, customLaunchStoryboardValue,
exporterBundleIdentifierValue, suppressPlistResourceUsageValue, useLegacyBuildSystemValue, buildNumber;
- ScopedMessageBox messageBox;
struct SandboxFileAccessProperty
{
@@ -3773,5 +3847,11 @@ private:
{ appSandboxAbsDirRWValue, "App sandbox temporary exception: absolute path read/write file access", "absolute-path.read-write" }
};
+ bool userAcknowledgedInvalidPostBuildScript = false;
+ bool userAcknowledgedDefunctIOKitSetting = false;
+
+ ErasedScopeGuard messageBoxQueueListenerScope;
+ ScopedMessageBox messageBox;
+
JUCE_DECLARE_NON_COPYABLE (XcodeProjectExporter)
};
diff --git a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExporter.h b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExporter.h
index c216d4b240..5a5369df79 100644
--- a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExporter.h
+++ b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExporter.h
@@ -101,7 +101,6 @@ public:
virtual bool canCopeWithDuplicateFiles() = 0;
virtual bool supportsUserDefinedConfigurations() const = 0; // false if exporter only supports two configs Debug and Release
virtual void updateDeprecatedSettings() {}
- virtual void updateDeprecatedSettingsInteractively() {}
virtual void initialiseDependencyPathValues() {}
// IDE targeted by exporter
diff --git a/extras/Projucer/Source/Utility/Helpers/jucer_PresetIDs.h b/extras/Projucer/Source/Utility/Helpers/jucer_PresetIDs.h
index a95cf1c364..0214d983b9 100644
--- a/extras/Projucer/Source/Utility/Helpers/jucer_PresetIDs.h
+++ b/extras/Projucer/Source/Utility/Helpers/jucer_PresetIDs.h
@@ -202,6 +202,7 @@ namespace Ids
DECLARE_ID (appSandboxHomeDirRW);
DECLARE_ID (appSandboxAbsDirRO);
DECLARE_ID (appSandboxAbsDirRW);
+ DECLARE_ID (appSandboxExceptionIOKit);
DECLARE_ID (hardenedRuntime);
DECLARE_ID (hardenedRuntimeOptions);
DECLARE_ID (microphonePermissionNeeded);