1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

Projucer: Fix the type of the iokit sandbox exception in the Xcode exporter

This commit is contained in:
attila 2023-10-03 20:33:14 +02:00
parent f0d147a470
commit ef61128127
8 changed files with 197 additions and 42 deletions

View file

@ -123,6 +123,17 @@ namespace juce:: build_tools
paths += "\n\t</array>";
entitlements.set (option.key, paths);
}
if (! appSandboxExceptionIOKit.isEmpty())
{
String ioKitClasses = "<array>";
for (const auto& c : appSandboxExceptionIOKit)
ioKitClasses += "\n\t\t<string>" + c + "</string>";
ioKitClasses += "\n\t</array>";
entitlements.set ("com.apple.security.temporary-exception.iokit-user-client-class", ioKitClasses);
}
}
}

View file

@ -56,6 +56,7 @@ namespace juce::build_tools
};
std::vector<KeyAndStringArray> appSandboxTemporaryPaths;
StringArray appSandboxExceptionIOKit;
private:
StringPairArray getEntitlements() const;

View file

@ -385,8 +385,6 @@ void MainWindow::openFile (const File& file, std::function<void (bool)> callback
parent->createProjectContentCompIfNeeded();
parent->getProjectContentComponent()->reloadLastOpenDocuments();
parent->currentProject->updateDeprecatedProjectSettingsInteractively();
}
NullCheckedInvocation::invoke (callback, saveResult);

View file

@ -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);

View file

@ -117,6 +117,64 @@ namespace ProjectMessages
using MessageAction = std::pair<String, std::function<void()>>;
}
// 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<ScopedMessageBox (MessageBoxOptions, std::function<void (int)>)>;
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<void (int)> 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<Listener> 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 <typename This>
static auto& getEnabledModulesImpl (This&);
@ -667,6 +729,9 @@ private:
std::unique_ptr<FileChooser> chooser;
std::unique_ptr<ProjectSaver> saver;
std::optional<MessageBoxOptions> exporterRemovalMessageBoxOptions;
ErasedScopeGuard messageBoxQueueListenerScope;
ScopedMessageBox messageBox;
//==============================================================================

View file

@ -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<var>(), ","),
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<var> 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)
};

View file

@ -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

View file

@ -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);