Skip to content
Permalink
Browse files

Projucer: Added options to configure macOS Hardened Runtime

  • Loading branch information...
tpoole committed Feb 21, 2019
1 parent 74bcfa1 commit ba7e1f765876a9a9fdf7b46fe0981db8acbdfdd8
@@ -75,6 +75,8 @@ class XcodeProjectExporter : public ProjectExporter
iPadScreenOrientationValue (settings, Ids::iPadScreenOrientation, getUndoManager(), "portraitlandscape"),
customXcodeResourceFoldersValue (settings, Ids::customXcodeResourceFolders, getUndoManager()),
customXcassetsFolderValue (settings, Ids::customXcassetsFolder, getUndoManager()),
hardenedRuntimeValue (settings, Ids::hardenedRuntime, getUndoManager()),
hardenedRuntimeOptionsValue (settings, Ids::hardenedRuntimeOptions, getUndoManager(), Array<var>(), ","),
microphonePermissionNeededValue (settings, Ids::microphonePermissionNeeded, getUndoManager()),
microphonePermissionsTextValue (settings, Ids::microphonePermissionsText, getUndoManager(),
"This app requires audio input. If you do not have an audio interface connected it will use the built-in microphone."),
@@ -111,51 +113,54 @@ class XcodeProjectExporter : public ProjectExporter
}

//==============================================================================
String getPListToMergeString() const { return customPListValue.get(); }
String getPListPrefixHeaderString() const { return pListPrefixHeaderValue.get(); }
bool isPListPreprocessEnabled() const { return pListPreprocessValue.get(); }
String getPListToMergeString() const { return customPListValue.get(); }
String getPListPrefixHeaderString() const { return pListPrefixHeaderValue.get(); }
bool isPListPreprocessEnabled() const { return pListPreprocessValue.get(); }

String getSubprojectsString() const { return subprojectsValue.get(); }
String getSubprojectsString() const { return subprojectsValue.get(); }

String getExtraFrameworksString() const { return extraFrameworksValue.get(); }
String getFrameworkSearchPathsString() const { return frameworkSearchPathsValue.get(); }
String getExtraCustomFrameworksString() const { return extraCustomFrameworksValue.get(); }
String getEmbeddedFrameworksString() const { return embeddedFrameworksValue.get(); }
String getExtraFrameworksString() const { return extraFrameworksValue.get(); }
String getFrameworkSearchPathsString() const { return frameworkSearchPathsValue.get(); }
String getExtraCustomFrameworksString() const { return extraCustomFrameworksValue.get(); }
String getEmbeddedFrameworksString() const { return embeddedFrameworksValue.get(); }

String getPostBuildScript() const { return postbuildCommandValue.get(); }
String getPreBuildScript() const { return prebuildCommandValue.get(); }
String getPostBuildScript() const { return postbuildCommandValue.get(); }
String getPreBuildScript() const { return prebuildCommandValue.get(); }

bool shouldDuplicateAppExResourcesFolder() const { return duplicateAppExResourcesFolderValue.get(); }
bool shouldDuplicateAppExResourcesFolder() const { return duplicateAppExResourcesFolderValue.get(); }

String getDeviceFamilyString() const { return iosDeviceFamilyValue.get(); }
String getDeviceFamilyString() const { return iosDeviceFamilyValue.get(); }

String getiPhoneScreenOrientationString() const { return iPhoneScreenOrientationValue.get(); }
String getiPadScreenOrientationString() const { return iPadScreenOrientationValue.get(); }
String getiPhoneScreenOrientationString() const { return iPhoneScreenOrientationValue.get(); }
String getiPadScreenOrientationString() const { return iPadScreenOrientationValue.get(); }

String getCustomResourceFoldersString() const { return customXcodeResourceFoldersValue.get().toString().replaceCharacters ("\r\n", "::"); }
String getCustomXcassetsFolderString() const { return customXcassetsFolderValue.get(); }
String getCustomLaunchStoryboardString() const { return customLaunchStoryboardValue.get(); }
String getCustomResourceFoldersString() const { return customXcodeResourceFoldersValue.get().toString().replaceCharacters ("\r\n", "::"); }
String getCustomXcassetsFolderString() const { return customXcassetsFolderValue.get(); }
String getCustomLaunchStoryboardString() const { return customLaunchStoryboardValue.get(); }

bool isHardenedRuntimeEnabled() const { return hardenedRuntimeValue.get(); }
Array<var> getHardenedRuntimeOptions() const { return *hardenedRuntimeOptionsValue.get().getArray(); }

bool isMicrophonePermissionEnabled() const { return microphonePermissionNeededValue.get(); }
String getMicrophonePermissionsTextString() const { return microphonePermissionsTextValue.get(); }

bool isCameraPermissionEnabled() const { return cameraPermissionNeededValue.get(); }
String getCameraPermissionTextString() const { return cameraPermissionTextValue.get(); }
bool isCameraPermissionEnabled() const { return cameraPermissionNeededValue.get(); }
String getCameraPermissionTextString() const { return cameraPermissionTextValue.get(); }

bool isInAppPurchasesEnabled() const { return iosInAppPurchasesValue.get(); }
bool isBackgroundAudioEnabled() const { return iosBackgroundAudioValue.get(); }
bool isBackgroundBleEnabled() const { return iosBackgroundBleValue.get(); }
bool isPushNotificationsEnabled() const { return iosPushNotificationsValue.get(); }
bool isAppGroupsEnabled() const { return iosAppGroupsValue.get(); }
bool isiCloudPermissionsEnabled() const { return iCloudPermissionsValue.get(); }
bool isFileSharingEnabled() const { return uiFileSharingEnabledValue.get(); }
bool isDocumentBrowserEnabled() const { return uiSupportsDocumentBrowserValue.get(); }
bool isStatusBarHidden() const { return uiStatusBarHiddenValue.get(); }
bool isInAppPurchasesEnabled() const { return iosInAppPurchasesValue.get(); }
bool isBackgroundAudioEnabled() const { return iosBackgroundAudioValue.get(); }
bool isBackgroundBleEnabled() const { return iosBackgroundBleValue.get(); }
bool isPushNotificationsEnabled() const { return iosPushNotificationsValue.get(); }
bool isAppGroupsEnabled() const { return iosAppGroupsValue.get(); }
bool isiCloudPermissionsEnabled() const { return iCloudPermissionsValue.get(); }
bool isFileSharingEnabled() const { return uiFileSharingEnabledValue.get(); }
bool isDocumentBrowserEnabled() const { return uiSupportsDocumentBrowserValue.get(); }
bool isStatusBarHidden() const { return uiStatusBarHiddenValue.get(); }

String getIosDevelopmentTeamIDString() const { return iosDevelopmentTeamIDValue.get(); }
String getAppGroupIdString() const { return iosAppGroupsIDValue.get(); }
String getIosDevelopmentTeamIDString() const { return iosDevelopmentTeamIDValue.get(); }
String getAppGroupIdString() const { return iosAppGroupsIDValue.get(); }

String getDefaultLaunchStoryboardName() const { jassert (iOS); return "LaunchScreen"; }
String getDefaultLaunchStoryboardName() const { jassert (iOS); return "LaunchScreen"; }

//==============================================================================
bool usesMMFiles() const override { return true; }
@@ -263,6 +268,44 @@ class XcodeProjectExporter : public ProjectExporter
"Using a leading '.' is optional, and the extensions are not case-sensitive.");
}

if (isOSX())
{
props.add (new ChoicePropertyComponent (hardenedRuntimeValue, "Use Hardened Runtime"),
"Enable this to use the hardened runtime required for app notarization.");

std::vector<std::pair<String, String>> options
{
{ "Allow Execution of JIT-compiled Code", "cs.allow-jit" },
{ "Allow Unsigned Executable Memory", "cs.allow-unsigned-executable-memory" },
{ "Allow DYLD Environment Variables", "cs.allow-dyld-environment-variables" },
{ "Disable Library Validation", "cs.disable-library-validation" },
{ "Disable Executable Memory Protection", "cs.disable-executable-page-protection" },
{ "Debugging Tool", "cs.debugger" },
{ "Audio Input", "device.audio-input" },
{ "Camera", "device.camera" },
{ "Location", "personal-information.location" },
{ "Address Book", "personal-information.addressbook" },
{ "Calendar", "personal-information.calendars" },
{ "Photos Library", "personal-information.photos-library" },
{ "Apple Events", "automation.apple-events" },
};

StringArray keys;
Array<var> values;

for (auto& opt : options)
{
keys.add (opt.first);
values.add ("com.apple.security." + opt.second);
}

props.add (new MultiChoicePropertyComponentWithEnablement (hardenedRuntimeOptionsValue,
hardenedRuntimeValue,
"Hardened Runtime Options",
keys,
values));
}

props.add (new ChoicePropertyComponent (microphonePermissionNeededValue, "Microphone Access"),
"Enable this to allow your app to use the microphone. "
"The user of your app will be prompted to grant microphone access permissions.");
@@ -969,13 +1012,15 @@ class XcodeProjectExporter : public ProjectExporter

auto pushNotificationsEnabled = owner.isPushNotificationsEnabled() ? 1 : 0;
auto sandboxEnabled = (type == Target::AudioUnitv3PlugIn ? 1 : 0);
auto hardendedRuntimeEnabled = owner.isHardenedRuntimeEnabled() ? 1 : 0;

attributes << "SystemCapabilities = {";
attributes << "com.apple.ApplicationGroups.iOS = { enabled = " << appGroupsEnabled << "; }; ";
attributes << "com.apple.InAppPurchase = { enabled = " << inAppPurchasesEnabled << "; }; ";
attributes << "com.apple.InterAppAudio = { enabled = " << interAppAudioEnabled << "; }; ";
attributes << "com.apple.Push = { enabled = " << pushNotificationsEnabled << "; }; ";
attributes << "com.apple.Sandbox = { enabled = " << sandboxEnabled << "; }; ";
attributes << "com.apple.HardenedRuntime = { enabled = " << hardendedRuntimeEnabled << "; }; ";

if (owner.iOS && owner.isiCloudPermissionsEnabled())
attributes << "com.apple.iCloud = { enabled = 1; }; ";
@@ -1019,7 +1064,10 @@ class XcodeProjectExporter : public ProjectExporter
//==============================================================================
bool shouldAddEntitlements() const
{
if (owner.isPushNotificationsEnabled() || owner.isAppGroupsEnabled() || (owner.isiOS() && owner.isiCloudPermissionsEnabled()))
if (owner.isPushNotificationsEnabled()
|| owner.isAppGroupsEnabled()
|| owner.isHardenedRuntimeEnabled()
|| (owner.isiOS() && owner.isiCloudPermissionsEnabled()))
return true;

if (owner.project.getProjectType().isAudioPlugin()
@@ -1172,6 +1220,9 @@ class XcodeProjectExporter : public ProjectExporter

s.set ("CONFIGURATION_BUILD_DIR", addQuotesIfRequired (configurationBuildDir));

if (owner.isHardenedRuntimeEnabled())
s.set ("ENABLE_HARDENED_RUNTIME", "YES");

String gccVersion ("com.apple.compilers.llvm.clang.1_0");

if (owner.iOS)
@@ -1819,6 +1870,7 @@ class XcodeProjectExporter : public ProjectExporter
postbuildCommandValue, prebuildCommandValue,
duplicateAppExResourcesFolderValue, iosDeviceFamilyValue, iPhoneScreenOrientationValue,
iPadScreenOrientationValue, customXcodeResourceFoldersValue, customXcassetsFolderValue,
hardenedRuntimeValue, hardenedRuntimeOptionsValue,
microphonePermissionNeededValue, microphonePermissionsTextValue, cameraPermissionNeededValue, cameraPermissionTextValue,
uiFileSharingEnabledValue, uiSupportsDocumentBrowserValue, uiStatusBarHiddenValue, documentExtensionsValue, iosInAppPurchasesValue,
iosBackgroundAudioValue, iosBackgroundBleValue, iosPushNotificationsValue, iosAppGroupsValue, iCloudPermissionsValue,
@@ -1967,6 +2019,7 @@ class XcodeProjectExporter : public ProjectExporter
void addFilesAndGroupsToProject (StringArray& topLevelGroupIDs) const
{
auto entitlements = getEntitlements();

if (entitlements.size() > 0)
topLevelGroupIDs.add (addEntitlementsFile (entitlements));

@@ -2417,7 +2470,7 @@ class XcodeProjectExporter : public ProjectExporter
s.set ("CLANG_WARN_SUSPICIOUS_MOVE", "YES");
s.set ("CLANG_WARN_UNREACHABLE_CODE", "YES");
s.set ("CLANG_WARN__DUPLICATE_METHOD_MATCH", "YES");
s.set ("WARNING_CFLAGS", "-Wreorder");
s.set ("WARNING_CFLAGS", "\"-Wreorder\"");

if (projectType.isStaticLibrary())
{
@@ -2453,7 +2506,7 @@ class XcodeProjectExporter : public ProjectExporter
s.set ("ZERO_LINK", "NO");

if (xcodeCanUseDwarf)
s.set ("DEBUG_INFORMATION_FORMAT", "\"dwarf\"");
s.set ("DEBUG_INFORMATION_FORMAT", "dwarf");

s.set ("PRODUCT_NAME", replacePreprocessorTokens (config, config.getTargetBinaryNameString()).quoted());

@@ -2919,6 +2972,7 @@ class XcodeProjectExporter : public ProjectExporter
StringPairArray getEntitlements() const
{
StringPairArray entitlements;

if (project.getProjectType().isAudioPlugin())
{
if (isiOS())
@@ -2936,12 +2990,12 @@ class XcodeProjectExporter : public ProjectExporter
if (isPushNotificationsEnabled())
entitlements.set (isiOS() ? "aps-environment"
: "com.apple.developer.aps-environment",
"<string>development</string>");
"<string>development</string>");
}

if (isAppGroupsEnabled())
{
auto appGroups = StringArray::fromTokens (getAppGroupIdString(), ";", { });
auto appGroups = StringArray::fromTokens (getAppGroupIdString(), ";", {});
auto groups = String ("<array>");

for (auto group : appGroups)
@@ -2952,6 +3006,10 @@ class XcodeProjectExporter : public ProjectExporter
entitlements.set ("com.apple.security.application-groups", groups);
}

if (isHardenedRuntimeEnabled())
for (auto& option : getHardenedRuntimeOptions())
entitlements.set (option, "<true/>");

if (isiOS() && isiCloudPermissionsEnabled())
{
entitlements.set ("com.apple.developer.icloud-container-identifiers",
@@ -185,6 +185,8 @@ namespace Ids
DECLARE_ID (showAllCode);
DECLARE_ID (useLocalCopy);
DECLARE_ID (overwriteOnSave);
DECLARE_ID (hardenedRuntime);
DECLARE_ID (hardenedRuntimeOptions);
DECLARE_ID (microphonePermissionNeeded);
DECLARE_ID (microphonePermissionsText);
DECLARE_ID (cameraPermissionNeeded);
@@ -118,3 +118,33 @@ class ChoicePropertyComponentWithEnablement : public ChoicePropertyComponent,
setEnabled (valueWithDefault.get());
}
};

//==============================================================================
class MultiChoicePropertyComponentWithEnablement : public MultiChoicePropertyComponent,
private Value::Listener
{
public:
MultiChoicePropertyComponentWithEnablement (ValueWithDefault& valueToControl,
ValueWithDefault valueToListenTo,
const String& propertyName,
const StringArray& choices,
const Array<var>& correspondingValues)
: MultiChoicePropertyComponent (valueToControl,
propertyName,
choices,
correspondingValues),
valueWithDefault (valueToListenTo),
value (valueToListenTo.getPropertyAsValue())
{
value.addListener (this);
valueChanged (value);
}

~MultiChoicePropertyComponentWithEnablement() override { value.removeListener (this); }

private:
void valueChanged (Value&) override { setEnabled (valueWithDefault.get()); }

ValueWithDefault valueWithDefault;
Value value;
};

0 comments on commit ba7e1f7

Please sign in to comment.
You can’t perform that action at this time.