|
|
|
@ -51,11 +51,51 @@ void EditorExportPlatformMacOS::get_preset_features(const Ref<EditorExportPreset
|
|
|
|
|
r_features->push_back(p_preset->get("binary_format/architecture"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool EditorExportPlatformMacOS::get_export_option_visibility(const String &p_option, const HashMap<StringName, Variant> &p_options) const {
|
|
|
|
|
// These options are not supported by built-in codesign, used on non macOS host.
|
|
|
|
|
if (!OS::get_singleton()->has_feature("macos")) {
|
|
|
|
|
if (p_option == "codesign/identity" || p_option == "codesign/timestamp" || p_option == "codesign/hardened_runtime" || p_option == "codesign/custom_options" || p_option.begins_with("notarization/")) {
|
|
|
|
|
return false;
|
|
|
|
|
bool EditorExportPlatformMacOS::get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
|
|
|
|
|
// Hide irrelevant code signing options.
|
|
|
|
|
if (p_preset) {
|
|
|
|
|
int codesign_tool = p_preset->get("codesign/codesign");
|
|
|
|
|
switch (codesign_tool) {
|
|
|
|
|
case 1: { // built-in ad-hoc
|
|
|
|
|
if (p_option == "codesign/identity" || p_option == "codesign/certificate_file" || p_option == "codesign/certificate_password" || p_option == "codesign/custom_options") {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
} break;
|
|
|
|
|
case 2: { // "rcodesign"
|
|
|
|
|
if (p_option == "codesign/identity") {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
} break;
|
|
|
|
|
#ifdef MACOS_ENABLED
|
|
|
|
|
case 3: { // "codesign"
|
|
|
|
|
if (p_option == "codesign/certificate_file" || p_option == "codesign/certificate_password") {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
} break;
|
|
|
|
|
#endif
|
|
|
|
|
default: { // disabled
|
|
|
|
|
if (p_option == "codesign/identity" || p_option == "codesign/certificate_file" || p_option == "codesign/certificate_password" || p_option == "codesign/custom_options" || p_option.begins_with("codesign/entitlements")) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
} break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Hide irrelevant notarization options.
|
|
|
|
|
int notary_tool = p_preset->get("notarization/notarization");
|
|
|
|
|
switch (notary_tool) {
|
|
|
|
|
case 1: { // "rcodesign"
|
|
|
|
|
if (p_option == "notarization/apple_id_name" || p_option == "notarization/apple_id_password" || p_option == "notarization/apple_team_id") {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
} break;
|
|
|
|
|
case 2: { // "altool"
|
|
|
|
|
// All options are visible.
|
|
|
|
|
} break;
|
|
|
|
|
default: { // disabled
|
|
|
|
|
if (p_option == "notarization/apple_id_name" || p_option == "notarization/apple_id_password" || p_option == "notarization/apple_team_id" || p_option == "notarization/api_uuid" || p_option == "notarization/api_key") {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
} break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -83,6 +123,56 @@ void EditorExportPlatformMacOS::get_export_options(List<ExportOption> *r_options
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), ""));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::DICTIONARY, "application/copyright_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary()));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "display/high_res"), false));
|
|
|
|
|
|
|
|
|
|
#ifdef MACOS_ENABLED
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/codesign", PROPERTY_HINT_ENUM, "Disabled,Built-in (ad-hoc only),PyOxidizer rcodesign,Xcode codesign"), 3, true));
|
|
|
|
|
#else
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/codesign", PROPERTY_HINT_ENUM, "Disabled,Built-in (ad-hoc only),PyOxidizer rcodesign"), 1, true));
|
|
|
|
|
#endif
|
|
|
|
|
// "codesign" only options:
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity", PROPERTY_HINT_PLACEHOLDER_TEXT, "Type: Name (ID)"), ""));
|
|
|
|
|
// "rcodesign" only options:
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/certificate_file", PROPERTY_HINT_GLOBAL_FILE, "*.pfx,*.p12"), ""));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/certificate_password", PROPERTY_HINT_PASSWORD), ""));
|
|
|
|
|
// "codesign" and "rcodesign" only options:
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/entitlements/custom_file", PROPERTY_HINT_GLOBAL_FILE, "*.plist"), ""));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_jit_code_execution"), false));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_unsigned_executable_memory"), false));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_dyld_environment_variables"), false));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/disable_library_validation"), false));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/audio_input"), false));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/camera"), false));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/location"), false));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/address_book"), false));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/calendars"), false));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/photos_library"), false));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/apple_events"), false));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/debugging"), false));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/enabled"), false));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/network_server"), false));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/network_client"), false));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/device_usb"), false));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/device_bluetooth"), false));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_downloads", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_pictures", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_music", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_movies", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::ARRAY, "codesign/entitlements/app_sandbox/helper_executables", PROPERTY_HINT_ARRAY_TYPE, itos(Variant::STRING) + "/" + itos(PROPERTY_HINT_GLOBAL_FILE) + ":"), Array()));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "codesign/custom_options"), PackedStringArray()));
|
|
|
|
|
|
|
|
|
|
#ifdef MACOS_ENABLED
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "notarization/notarization", PROPERTY_HINT_ENUM, "Disabled,PyOxidizer rcodesign,Xcode altool"), 0, true));
|
|
|
|
|
#else
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "notarization/notarization", PROPERTY_HINT_ENUM, "Disabled,PyOxidizer rcodesign"), 0, true));
|
|
|
|
|
#endif
|
|
|
|
|
// "altool" only options:
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Apple ID email"), ""));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_password", PROPERTY_HINT_PASSWORD, "Enable two-factor authentication and provide app-specific password"), ""));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_team_id", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide team ID if your Apple ID belongs to multiple teams"), ""));
|
|
|
|
|
// "altool" and "rcodesign" only options:
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/api_uuid", PROPERTY_HINT_PLACEHOLDER_TEXT, "App Store Connect issuer ID"), ""));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/api_key", PROPERTY_HINT_PLACEHOLDER_TEXT, "App Store Connect API key ID"), ""));
|
|
|
|
|
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/microphone_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the microphone"), ""));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::DICTIONARY, "privacy/microphone_usage_description_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary()));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/camera_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the camera"), ""));
|
|
|
|
@ -106,45 +196,6 @@ void EditorExportPlatformMacOS::get_export_options(List<ExportOption> *r_options
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/removable_volumes_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use removable volumes"), ""));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::DICTIONARY, "privacy/removable_volumes_usage_description_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary()));
|
|
|
|
|
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/enable"), true));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity", PROPERTY_HINT_PLACEHOLDER_TEXT, "Type: Name (ID)"), ""));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/timestamp"), true));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/replace_existing_signature"), true));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/hardened_runtime"), true));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/entitlements/custom_file", PROPERTY_HINT_GLOBAL_FILE, "*.plist"), ""));
|
|
|
|
|
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_jit_code_execution"), false));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_unsigned_executable_memory"), false));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_dyld_environment_variables"), false));
|
|
|
|
|
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/disable_library_validation"), false));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/audio_input"), false));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/camera"), false));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/location"), false));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/address_book"), false));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/calendars"), false));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/photos_library"), false));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/apple_events"), false));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/debugging"), false));
|
|
|
|
|
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/enabled"), false));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/network_server"), false));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/network_client"), false));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/device_usb"), false));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/device_bluetooth"), false));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_downloads", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_pictures", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_music", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_movies", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::ARRAY, "codesign/entitlements/app_sandbox/helper_executables", PROPERTY_HINT_ARRAY_TYPE, itos(Variant::STRING) + "/" + itos(PROPERTY_HINT_GLOBAL_FILE) + ":"), Array()));
|
|
|
|
|
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "codesign/custom_options"), PackedStringArray()));
|
|
|
|
|
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "notarization/enable"), false));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Apple ID email"), ""));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_password", PROPERTY_HINT_PLACEHOLDER_TEXT, "Enable two-factor authentication and provide app-specific password"), ""));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_team_id", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide team ID if your Apple ID belongs to multiple teams"), ""));
|
|
|
|
|
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc"), true));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc"), false));
|
|
|
|
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc2"), false));
|
|
|
|
@ -415,156 +466,284 @@ void EditorExportPlatformMacOS::_fix_plist(const Ref<EditorExportPreset> &p_pres
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
Error EditorExportPlatformMacOS::_notarize(const Ref<EditorExportPreset> &p_preset, const String &p_path) {
|
|
|
|
|
int notary_tool = p_preset->get("notarization/notarization");
|
|
|
|
|
switch (notary_tool) {
|
|
|
|
|
case 1: { // "rcodesign"
|
|
|
|
|
print_verbose("using rcodesign notarization...");
|
|
|
|
|
|
|
|
|
|
String rcodesign = EditorSettings::get_singleton()->get("export/macos/rcodesign").operator String();
|
|
|
|
|
if (rcodesign.is_empty()) {
|
|
|
|
|
add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("rcodesign path is not set. Configure rcodesign path in the Editor Settings (Export > macOS > rcodesign)."));
|
|
|
|
|
return Error::FAILED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
List<String> args;
|
|
|
|
|
|
|
|
|
|
args.push_back("notary-submit");
|
|
|
|
|
|
|
|
|
|
if (p_preset->get("notarization/api_uuid") == "") {
|
|
|
|
|
add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("App Store Connect issuer ID name not specified."));
|
|
|
|
|
return Error::FAILED;
|
|
|
|
|
}
|
|
|
|
|
if (p_preset->get("notarization/api_key") == "") {
|
|
|
|
|
add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("App Store Connect API key ID not specified."));
|
|
|
|
|
return Error::FAILED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
args.push_back("--api-issuer");
|
|
|
|
|
args.push_back(p_preset->get("notarization/api_uuid"));
|
|
|
|
|
|
|
|
|
|
args.push_back("--api-key");
|
|
|
|
|
args.push_back(p_preset->get("notarization/api_key"));
|
|
|
|
|
|
|
|
|
|
args.push_back(p_path);
|
|
|
|
|
|
|
|
|
|
String str;
|
|
|
|
|
int exitcode = 0;
|
|
|
|
|
|
|
|
|
|
Error err = OS::get_singleton()->execute(rcodesign, args, &str, &exitcode, true);
|
|
|
|
|
if (err != OK) {
|
|
|
|
|
add_message(EXPORT_MESSAGE_WARNING, TTR("Notarization"), TTR("Could not start rcodesign executable."));
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int rq_offset = str.find("created submission ID:");
|
|
|
|
|
if (exitcode != 0 || rq_offset == -1) {
|
|
|
|
|
print_line("rcodesign (" + p_path + "):\n" + str);
|
|
|
|
|
add_message(EXPORT_MESSAGE_WARNING, TTR("Notarization"), TTR("Notarization failed, see editor log for details."));
|
|
|
|
|
return Error::FAILED;
|
|
|
|
|
} else {
|
|
|
|
|
print_verbose("rcodesign (" + p_path + "):\n" + str);
|
|
|
|
|
int next_nl = str.find("\n", rq_offset);
|
|
|
|
|
String request_uuid = (next_nl == -1) ? str.substr(rq_offset + 14, -1) : str.substr(rq_offset + 14, next_nl - rq_offset - 14);
|
|
|
|
|
add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), vformat(TTR("Notarization request UUID: \"%s\""), request_uuid));
|
|
|
|
|
add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), TTR("The notarization process generally takes less than an hour."));
|
|
|
|
|
add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), "\t" + TTR("You can check progress manually by opening a Terminal and running the following command:"));
|
|
|
|
|
add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), "\t\t\"rcodesign notary-log --api-issuer <api uuid> --api-key <api key> <request uuid>\"");
|
|
|
|
|
add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), "\t" + TTR("Run the following command to staple the notarization ticket to the exported application (optional):"));
|
|
|
|
|
add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), "\t\t\"rcodesign staple <app path>\"");
|
|
|
|
|
}
|
|
|
|
|
} break;
|
|
|
|
|
#ifdef MACOS_ENABLED
|
|
|
|
|
List<String> args;
|
|
|
|
|
case 2: { // "altool"
|
|
|
|
|
print_verbose("using altool notarization...");
|
|
|
|
|
|
|
|
|
|
args.push_back("altool");
|
|
|
|
|
args.push_back("--notarize-app");
|
|
|
|
|
if (!FileAccess::exists("/usr/bin/xcrun") && !FileAccess::exists("/bin/xcrun")) {
|
|
|
|
|
add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("Xcode command line tools are not installed."));
|
|
|
|
|
return Error::FAILED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
args.push_back("--primary-bundle-id");
|
|
|
|
|
args.push_back(p_preset->get("application/bundle_identifier"));
|
|
|
|
|
List<String> args;
|
|
|
|
|
|
|
|
|
|
args.push_back("--username");
|
|
|
|
|
args.push_back(p_preset->get("notarization/apple_id_name"));
|
|
|
|
|
args.push_back("altool");
|
|
|
|
|
args.push_back("--notarize-app");
|
|
|
|
|
|
|
|
|
|
args.push_back("--password");
|
|
|
|
|
args.push_back(p_preset->get("notarization/apple_id_password"));
|
|
|
|
|
args.push_back("--primary-bundle-id");
|
|
|
|
|
args.push_back(p_preset->get("application/bundle_identifier"));
|
|
|
|
|
|
|
|
|
|
args.push_back("--type");
|
|
|
|
|
args.push_back("osx");
|
|
|
|
|
if (p_preset->get("notarization/apple_id_name") == "" && p_preset->get("notarization/api_uuid") == "") {
|
|
|
|
|
add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("Neither Apple ID name nor App Store Connect issuer ID name not specified."));
|
|
|
|
|
return Error::FAILED;
|
|
|
|
|
}
|
|
|
|
|
if (p_preset->get("notarization/apple_id_name") != "" && p_preset->get("notarization/api_uuid") != "") {
|
|
|
|
|
add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("Both Apple ID name and App Store Connect issuer ID name are specified, only one should be set at the same time."));
|
|
|
|
|
return Error::FAILED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (p_preset->get("notarization/apple_team_id")) {
|
|
|
|
|
args.push_back("--asc-provider");
|
|
|
|
|
args.push_back(p_preset->get("notarization/apple_team_id"));
|
|
|
|
|
}
|
|
|
|
|
if (p_preset->get("notarization/apple_id_name") != "") {
|
|
|
|
|
if (p_preset->get("notarization/apple_id_password") == "") {
|
|
|
|
|
add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("Apple ID password not specified."));
|
|
|
|
|
return Error::FAILED;
|
|
|
|
|
}
|
|
|
|
|
args.push_back("--username");
|
|
|
|
|
args.push_back(p_preset->get("notarization/apple_id_name"));
|
|
|
|
|
|
|
|
|
|
args.push_back("--file");
|
|
|
|
|
args.push_back(p_path);
|
|
|
|
|
args.push_back("--password");
|
|
|
|
|
args.push_back(p_preset->get("notarization/apple_id_password"));
|
|
|
|
|
} else {
|
|
|
|
|
if (p_preset->get("notarization/api_key") == "") {
|
|
|
|
|
add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("App Store Connect API key ID not specified."));
|
|
|
|
|
return Error::FAILED;
|
|
|
|
|
}
|
|
|
|
|
args.push_back("--apiIssuer");
|
|
|
|
|
args.push_back(p_preset->get("notarization/api_uuid"));
|
|
|
|
|
|
|
|
|
|
String str;
|
|
|
|
|
Error err = OS::get_singleton()->execute("xcrun", args, &str, nullptr, true);
|
|
|
|
|
if (err != OK || (str.find("not found") != -1) || (str.find("not recognized") != -1)) {
|
|
|
|
|
add_message(EXPORT_MESSAGE_WARNING, TTR("Notarization"), TTR("Could not start xcrun executable."));
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
args.push_back("--apiKey");
|
|
|
|
|
args.push_back(p_preset->get("notarization/api_key"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
print_verbose("altool (" + p_path + "):\n" + str);
|
|
|
|
|
int rq_offset = str.find("RequestUUID");
|
|
|
|
|
if (rq_offset == -1) {
|
|
|
|
|
add_message(EXPORT_MESSAGE_WARNING, TTR("Notarization"), TTR("Notarization failed."));
|
|
|
|
|
return FAILED;
|
|
|
|
|
} else {
|
|
|
|
|
int next_nl = str.find("\n", rq_offset);
|
|
|
|
|
String request_uuid = (next_nl == -1) ? str.substr(rq_offset + 14, -1) : str.substr(rq_offset + 14, next_nl - rq_offset - 14);
|
|
|
|
|
add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), vformat(TTR("Notarization request UUID: \"%s\""), request_uuid));
|
|
|
|
|
add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), TTR("The notarization process generally takes less than an hour. When the process is completed, you'll receive an email."));
|
|
|
|
|
add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), "\t" + TTR("You can check progress manually by opening a Terminal and running the following command:"));
|
|
|
|
|
add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), "\t\t\"xcrun altool --notarization-history 0 -u <your email> -p <app-specific pwd>\"");
|
|
|
|
|
add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), "\t" + TTR("Run the following command to staple the notarization ticket to the exported application (optional):"));
|
|
|
|
|
add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), "\t\t\"xcrun stapler staple <app path>\"");
|
|
|
|
|
}
|
|
|
|
|
args.push_back("--type");
|
|
|
|
|
args.push_back("osx");
|
|
|
|
|
|
|
|
|
|
if (p_preset->get("notarization/apple_team_id")) {
|
|
|
|
|
args.push_back("--asc-provider");
|
|
|
|
|
args.push_back(p_preset->get("notarization/apple_team_id"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
args.push_back("--file");
|
|
|
|
|
args.push_back(p_path);
|
|
|
|
|
|
|
|
|
|
String str;
|
|
|
|
|
int exitcode = 0;
|
|
|
|
|
Error err = OS::get_singleton()->execute("xcrun", args, &str, &exitcode, true);
|
|
|
|
|
if (err != OK) {
|
|
|
|
|
add_message(EXPORT_MESSAGE_WARNING, TTR("Notarization"), TTR("Could not start xcrun executable."));
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int rq_offset = str.find("RequestUUID");
|
|
|
|
|
if (exitcode != 0 || rq_offset == -1) {
|
|
|
|
|
print_line("xcrun altool (" + p_path + "):\n" + str);
|
|
|
|
|
add_message(EXPORT_MESSAGE_WARNING, TTR("Notarization"), TTR("Notarization failed, see editor log for details."));
|
|
|
|
|
return Error::FAILED;
|
|
|
|
|
} else {
|
|
|
|
|
print_verbose("xcrun altool (" + p_path + "):\n" + str);
|
|
|
|
|
int next_nl = str.find("\n", rq_offset);
|
|
|
|
|
String request_uuid = (next_nl == -1) ? str.substr(rq_offset + 14, -1) : str.substr(rq_offset + 14, next_nl - rq_offset - 14);
|
|
|
|
|
add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), vformat(TTR("Notarization request UUID: \"%s\""), request_uuid));
|
|
|
|
|
add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), TTR("The notarization process generally takes less than an hour. When the process is completed, you'll receive an email."));
|
|
|
|
|
add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), "\t" + TTR("You can check progress manually by opening a Terminal and running the following command:"));
|
|
|
|
|
add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), "\t\t\"xcrun altool --notarization-history 0 -u <your email> -p <app-specific pwd>\"");
|
|
|
|
|
add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), "\t" + TTR("Run the following command to staple the notarization ticket to the exported application (optional):"));
|
|
|
|
|
add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), "\t\t\"xcrun stapler staple <app path>\"");
|
|
|
|
|
}
|
|
|
|
|
} break;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
default: {
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
return OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Error EditorExportPlatformMacOS::_code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path, bool p_warn) {
|
|
|
|
|
bool force_builtin_codesign = EditorSettings::get_singleton()->get("export/macos/force_builtin_codesign");
|
|
|
|
|
bool ad_hoc = (p_preset->get("codesign/identity") == "" || p_preset->get("codesign/identity") == "-");
|
|
|
|
|
|
|
|
|
|
if ((!FileAccess::exists("/usr/bin/codesign") && !FileAccess::exists("/bin/codesign")) || force_builtin_codesign) {
|
|
|
|
|
print_verbose("using built-in codesign...");
|
|
|
|
|
int codesign_tool = p_preset->get("codesign/codesign");
|
|
|
|
|
switch (codesign_tool) {
|
|
|
|
|
case 1: { // built-in ad-hoc
|
|
|
|
|
print_verbose("using built-in codesign...");
|
|
|
|
|
#ifdef MODULE_REGEX_ENABLED
|
|
|
|
|
|
|
|
|
|
#ifdef MACOS_ENABLED
|
|
|
|
|
if (p_preset->get("codesign/timestamp") && p_warn) {
|
|
|
|
|
add_message(EXPORT_MESSAGE_INFO, TTR("Code Signing"), TTR("Timestamping is not compatible with ad-hoc signature, and was disabled!"));
|
|
|
|
|
}
|
|
|
|
|
if (p_preset->get("codesign/hardened_runtime") && p_warn) {
|
|
|
|
|
add_message(EXPORT_MESSAGE_INFO, TTR("Code Signing"), TTR("Hardened Runtime is not compatible with ad-hoc signature, and was disabled!"));
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
String error_msg;
|
|
|
|
|
Error err = CodeSign::codesign(false, p_preset->get("codesign/replace_existing_signature"), p_path, p_ent_path, error_msg);
|
|
|
|
|
if (err != OK) {
|
|
|
|
|
add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), vformat(TTR("Built-in CodeSign failed with error \"%s\"."), error_msg));
|
|
|
|
|
return FAILED;
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("Built-in CodeSign require regex module."));
|
|
|
|
|
#endif
|
|
|
|
|
return OK;
|
|
|
|
|
} else {
|
|
|
|
|
print_verbose("using external codesign...");
|
|
|
|
|
List<String> args;
|
|
|
|
|
if (p_preset->get("codesign/timestamp")) {
|
|
|
|
|
if (ad_hoc) {
|
|
|
|
|
if (p_warn) {
|
|
|
|
|
add_message(EXPORT_MESSAGE_INFO, TTR("Code Signing"), TTR("Timestamping is not compatible with ad-hoc signature, and was disabled!"));
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
args.push_back("--timestamp");
|
|
|
|
|
String error_msg;
|
|
|
|
|
Error err = CodeSign::codesign(false, true, p_path, p_ent_path, error_msg);
|
|
|
|
|
if (err != OK) {
|
|
|
|
|
add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), vformat(TTR("Built-in CodeSign failed with error \"%s\"."), error_msg));
|
|
|
|
|
return Error::FAILED;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (p_preset->get("codesign/hardened_runtime")) {
|
|
|
|
|
if (ad_hoc) {
|
|
|
|
|
if (p_warn) {
|
|
|
|
|
add_message(EXPORT_MESSAGE_INFO, TTR("Code Signing"), TTR("Hardened Runtime is not compatible with ad-hoc signature, and was disabled!"));
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("Built-in CodeSign require regex module."));
|
|
|
|
|
#endif
|
|
|
|
|
} break;
|
|
|
|
|
case 2: { // "rcodesign"
|
|
|
|
|
print_verbose("using rcodesign codesign...");
|
|
|
|
|
|
|
|
|
|
String rcodesign = EditorSettings::get_singleton()->get("export/macos/rcodesign").operator String();
|
|
|
|
|
if (rcodesign.is_empty()) {
|
|
|
|
|
add_message(EXPORT_MESSAGE_ERROR, TTR("Code Signing"), TTR("Xrcodesign path is not set. Configure rcodesign path in the Editor Settings (Export > macOS > rcodesign)."));
|
|
|
|
|
return Error::FAILED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
List<String> args;
|
|
|
|
|
args.push_back("sign");
|
|
|
|
|
|
|
|
|
|
if (p_path.get_extension() != "dmg") {
|
|
|
|
|
args.push_back("--entitlements-xml-path");
|
|
|
|
|
args.push_back(p_ent_path);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String certificate_file = p_preset->get("codesign/certificate_file");
|
|
|
|
|
String certificate_pass = p_preset->get("codesign/certificate_password");
|
|
|
|
|
if (!certificate_file.is_empty() && !certificate_file.is_empty()) {
|
|
|
|
|
args.push_back("--p12-file");
|
|
|
|
|
args.push_back(certificate_file);
|
|
|
|
|
args.push_back("--p12-password");
|
|
|
|
|
args.push_back(certificate_pass);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
args.push_back("-v"); /* provide some more feedback */
|
|
|
|
|
|
|
|
|
|
args.push_back(p_path);
|
|
|
|
|
|
|
|
|
|
String str;
|
|
|
|
|
int exitcode = 0;
|
|
|
|
|
|
|
|
|
|
Error err = OS::get_singleton()->execute(rcodesign, args, &str, &exitcode, true);
|
|
|
|
|
if (err != OK) {
|
|
|
|
|
add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("Could not start rcodesign executable."));
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (exitcode != 0) {
|
|
|
|
|
print_line("rcodesign (" + p_path + "):\n" + str);
|
|
|
|
|
add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("Code signing failed, see editor log for details."));
|
|
|
|
|
return Error::FAILED;
|
|
|
|
|
} else {
|
|
|
|
|
print_verbose("rcodesign (" + p_path + "):\n" + str);
|
|
|
|
|
}
|
|
|
|
|
} break;
|
|
|
|
|
#ifdef MACOS_ENABLED
|
|
|
|
|
case 3: { // "codesign"
|
|
|
|
|
print_verbose("using xcode codesign...");
|
|
|
|
|
|
|
|
|
|
if (!FileAccess::exists("/usr/bin/codesign") && !FileAccess::exists("/bin/codesign")) {
|
|
|
|
|
add_message(EXPORT_MESSAGE_ERROR, TTR("Code Signing"), TTR("Xcode command line tools are not installed."));
|
|
|
|
|
return Error::FAILED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ad_hoc = (p_preset->get("codesign/identity") == "" || p_preset->get("codesign/identity") == "-");
|
|
|
|
|
|
|
|
|
|
List<String> args;
|
|
|
|
|
if (!ad_hoc) {
|
|
|
|
|
args.push_back("--timestamp");
|
|
|
|
|
args.push_back("--options");
|
|
|
|
|
args.push_back("runtime");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (p_path.get_extension() != "dmg") {
|
|
|
|
|
args.push_back("--entitlements");
|
|
|
|
|
args.push_back(p_ent_path);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PackedStringArray user_args = p_preset->get("codesign/custom_options");
|
|
|
|
|
for (int i = 0; i < user_args.size(); i++) {
|
|
|
|
|
String user_arg = user_args[i].strip_edges();
|
|
|
|
|
if (!user_arg.is_empty()) {
|
|
|
|
|
args.push_back(user_arg);
|
|
|
|
|
if (p_path.get_extension() != "dmg") {
|
|
|
|
|
args.push_back("--entitlements");
|
|
|
|
|
args.push_back(p_ent_path);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
args.push_back("-s");
|
|
|
|
|
if (ad_hoc) {
|
|
|
|
|
args.push_back("-");
|
|
|
|
|
} else {
|
|
|
|
|
args.push_back(p_preset->get("codesign/identity"));
|
|
|
|
|
}
|
|
|
|
|
PackedStringArray user_args = p_preset->get("codesign/custom_options");
|
|
|
|
|
for (int i = 0; i < user_args.size(); i++) {
|
|
|
|
|
String user_arg = user_args[i].strip_edges();
|
|
|
|
|
if (!user_arg.is_empty()) {
|
|
|
|
|
args.push_back(user_arg);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
args.push_back("-v"); /* provide some more feedback */
|
|
|
|
|
args.push_back("-s");
|
|
|
|
|
if (ad_hoc) {
|
|
|
|
|
args.push_back("-");
|
|
|
|
|
} else {
|
|
|
|
|
args.push_back(p_preset->get("codesign/identity"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (p_preset->get("codesign/replace_existing_signature")) {
|
|
|
|
|
args.push_back("-v"); /* provide some more feedback */
|
|
|
|
|
args.push_back("-f");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
args.push_back(p_path);
|
|
|
|
|
args.push_back(p_path);
|
|
|
|
|
|
|
|
|
|
String str;
|
|
|
|
|
Error err = OS::get_singleton()->execute("codesign", args, &str, nullptr, true);
|
|
|
|
|
if (err != OK || (str.find("not found") != -1) || (str.find("not recognized") != -1)) {
|
|
|
|
|
add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("Could not start codesign executable, make sure Xcode command line tools are installed."));
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
String str;
|
|
|
|
|
int exitcode = 0;
|
|
|
|
|
|
|
|
|
|
print_verbose("codesign (" + p_path + "):\n" + str);
|
|
|
|
|
if (str.find("no identity found") != -1) {
|
|
|
|
|
add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("No identity found."));
|
|
|
|
|
return FAILED;
|
|
|
|
|
}
|
|
|
|
|
if ((str.find("unrecognized blob type") != -1) || (str.find("cannot read entitlement data") != -1)) {
|
|
|
|
|
add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("Invalid entitlements file."));
|
|
|
|
|
return FAILED;
|
|
|
|
|
}
|
|
|
|
|
return OK;
|
|
|
|
|
Error err = OS::get_singleton()->execute("codesign", args, &str, nullptr, true);
|
|
|
|
|
if (err != OK) {
|
|
|
|
|
add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("Could not start codesign executable, make sure Xcode command line tools are installed."));
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (exitcode != 0) {
|
|
|
|
|
print_line("codesign (" + p_path + "):\n" + str);
|
|
|
|
|
add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("Code signing failed, see editor log for details."));
|
|
|
|
|
return Error::FAILED;
|
|
|
|
|
} else {
|
|
|
|
|
print_verbose("codesign (" + p_path + "):\n" + str);
|
|
|
|
|
}
|
|
|
|
|
} break;
|
|
|
|
|
#endif
|
|
|
|
|
default: {
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Error EditorExportPlatformMacOS::_code_sign_directory(const Ref<EditorExportPreset> &p_preset, const String &p_path,
|
|
|
|
@ -816,7 +995,9 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
|
|
|
|
|
err = ERR_CANT_CREATE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DirAccess::remove_file_or_error(scr_path);
|
|
|
|
|
if (FileAccess::exists(scr_path)) {
|
|
|
|
|
DirAccess::remove_file_or_error(scr_path);
|
|
|
|
|
}
|
|
|
|
|
if (DirAccess::exists(tmp_app_path_name)) {
|
|
|
|
|
String old_dir = tmp_app_dir->get_current_dir();
|
|
|
|
|
if (tmp_app_dir->change_dir(tmp_app_path_name) == OK) {
|
|
|
|
@ -1148,7 +1329,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
|
|
|
|
|
err = save_pack(p_preset, p_debug, pack_path, &shared_objects);
|
|
|
|
|
|
|
|
|
|
// See if we can code sign our new package.
|
|
|
|
|
bool sign_enabled = p_preset->get("codesign/enable");
|
|
|
|
|
bool sign_enabled = (p_preset->get("codesign/codesign").operator int() > 0);
|
|
|
|
|
|
|
|
|
|
String ent_path = p_preset->get("codesign/entitlements/custom_file");
|
|
|
|
|
String hlp_ent_path = EditorPaths::get_singleton()->get_cache_dir().plus_file(pkg_name + "_helper.entitlements");
|
|
|
|
@ -1314,14 +1495,25 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ad_hoc = true;
|
|
|
|
|
if (err == OK) {
|
|
|
|
|
bool ad_hoc = false;
|
|
|
|
|
int codesign_tool = p_preset->get("codesign/codesign");
|
|
|
|
|
switch (codesign_tool) {
|
|
|
|
|
case 1: { // built-in ad-hoc
|
|
|
|
|
ad_hoc = true;
|
|
|
|
|
} break;
|
|
|
|
|
case 2: { // "rcodesign"
|
|
|
|
|
ad_hoc = p_preset->get("codesign/certificate_file").operator String().is_empty() || p_preset->get("codesign/certificate_password").operator String().is_empty();
|
|
|
|
|
} break;
|
|
|
|
|
#ifdef MACOS_ENABLED
|
|
|
|
|
String sign_identity = p_preset->get("codesign/identity");
|
|
|
|
|
#else
|
|
|
|
|
String sign_identity = "-";
|
|
|
|
|
case 3: { // "codesign"
|
|
|
|
|
ad_hoc = (p_preset->get("codesign/identity") == "" || p_preset->get("codesign/identity") == "-");
|
|
|
|
|
} break;
|
|
|
|
|
#endif
|
|
|
|
|
ad_hoc = (sign_identity == "" || sign_identity == "-");
|
|
|
|
|
default: {
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (err == OK) {
|
|
|
|
|
bool lib_validation = p_preset->get("codesign/entitlements/disable_library_validation");
|
|
|
|
|
if ((!dylibs_found.is_empty() || !shared_objects.is_empty()) && sign_enabled && ad_hoc && !lib_validation) {
|
|
|
|
|
add_message(EXPORT_MESSAGE_ERROR, TTR("Code Signing"), TTR("Ad-hoc signed applications require the 'Disable Library Validation' entitlement to load dynamic libraries."));
|
|
|
|
@ -1404,8 +1596,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef MACOS_ENABLED
|
|
|
|
|
bool noto_enabled = p_preset->get("notarization/enable");
|
|
|
|
|
bool noto_enabled = (p_preset->get("notarization/notarization").operator int() > 0);
|
|
|
|
|
if (err == OK && noto_enabled) {
|
|
|
|
|
if (export_format == "app") {
|
|
|
|
|
add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), TTR("Notarization requires the app to be archived first, select the DMG or ZIP export format instead."));
|
|
|
|
@ -1416,10 +1607,11 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
|
|
|
|
|
err = _notarize(p_preset, p_path);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// Clean up temporary entitlements files.
|
|
|
|
|
DirAccess::remove_file_or_error(hlp_ent_path);
|
|
|
|
|
if (FileAccess::exists(hlp_ent_path)) {
|
|
|
|
|
DirAccess::remove_file_or_error(hlp_ent_path);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Clean up temporary .app dir and generated entitlements.
|
|
|
|
|
if ((String)(p_preset->get("codesign/entitlements/custom_file")) == "") {
|
|
|
|
@ -1602,65 +1794,98 @@ bool EditorExportPlatformMacOS::has_valid_project_configuration(const Ref<Editor
|
|
|
|
|
valid = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool sign_enabled = p_preset->get("codesign/enable");
|
|
|
|
|
|
|
|
|
|
bool ad_hoc = false;
|
|
|
|
|
int codesign_tool = p_preset->get("codesign/codesign");
|
|
|
|
|
switch (codesign_tool) {
|
|
|
|
|
case 1: { // built-in ad-hoc
|
|
|
|
|
ad_hoc = true;
|
|
|
|
|
} break;
|
|
|
|
|
case 2: { // "rcodesign"
|
|
|
|
|
ad_hoc = p_preset->get("codesign/certificate_file").operator String().is_empty() || p_preset->get("codesign/certificate_password").operator String().is_empty();
|
|
|
|
|
} break;
|
|
|
|
|
#ifdef MACOS_ENABLED
|
|
|
|
|
bool noto_enabled = p_preset->get("notarization/enable");
|
|
|
|
|
bool ad_hoc = ((p_preset->get("codesign/identity") == "") || (p_preset->get("codesign/identity") == "-"));
|
|
|
|
|
|
|
|
|
|
if (!ad_hoc && (bool)EditorSettings::get_singleton()->get("export/macos/force_builtin_codesign")) {
|
|
|
|
|
err += TTR("Warning: Built-in \"codesign\" is selected in the Editor Settings. Code signing is limited to ad-hoc signature only.") + "\n";
|
|
|
|
|
}
|
|
|
|
|
if (!ad_hoc && !FileAccess::exists("/usr/bin/codesign") && !FileAccess::exists("/bin/codesign")) {
|
|
|
|
|
err += TTR("Warning: Xcode command line tools are not installed, using built-in \"codesign\". Code signing is limited to ad-hoc signature only.") + "\n";
|
|
|
|
|
case 3: { // "codesign"
|
|
|
|
|
ad_hoc = (p_preset->get("codesign/identity") == "" || p_preset->get("codesign/identity") == "-");
|
|
|
|
|
} break;
|
|
|
|
|
#endif
|
|
|
|
|
default: {
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
int notary_tool = p_preset->get("notarization/notarization");
|
|
|
|
|
|
|
|
|
|
if (noto_enabled) {
|
|
|
|
|
if (notary_tool > 0) {
|
|
|
|
|
if (ad_hoc) {
|
|
|
|
|
err += TTR("Notarization: Notarization with an ad-hoc signature is not supported.") + "\n";
|
|
|
|
|
valid = false;
|
|
|
|
|
}
|
|
|
|
|
if (!sign_enabled) {
|
|
|
|
|
if (codesign_tool == 0) {
|
|
|
|
|
err += TTR("Notarization: Code signing is required for notarization.") + "\n";
|
|
|
|
|
valid = false;
|
|
|
|
|
}
|
|
|
|
|
if (!(bool)p_preset->get("codesign/hardened_runtime")) {
|
|
|
|
|
err += TTR("Notarization: Hardened runtime is required for notarization.") + "\n";
|
|
|
|
|
valid = false;
|
|
|
|
|
}
|
|
|
|
|
if (!(bool)p_preset->get("codesign/timestamp")) {
|
|
|
|
|
err += TTR("Notarization: Timestamping is required for notarization.") + "\n";
|
|
|
|
|
valid = false;
|
|
|
|
|
}
|
|
|
|
|
if (p_preset->get("notarization/apple_id_name") == "") {
|
|
|
|
|
err += TTR("Notarization: Apple ID name not specified.") + "\n";
|
|
|
|
|
valid = false;
|
|
|
|
|
}
|
|
|
|
|
if (p_preset->get("notarization/apple_id_password") == "") {
|
|
|
|
|
err += TTR("Notarization: Apple ID password not specified.") + "\n";
|
|
|
|
|
valid = false;
|
|
|
|
|
if (notary_tool == 2) {
|
|
|
|
|
if (!FileAccess::exists("/usr/bin/xcrun") && !FileAccess::exists("/bin/xcrun")) {
|
|
|
|
|
err += TTR("Notarization: Xcode command line tools are not installed.") + "\n";
|
|
|
|
|
valid = false;
|
|
|
|
|
}
|
|
|
|
|
if (p_preset->get("notarization/apple_id_name") == "" && p_preset->get("notarization/api_uuid") == "") {
|
|
|
|
|
err += TTR("Notarization: Neither Apple ID name nor App Store Connect issuer ID name not specified.") + "\n";
|
|
|
|
|
valid = false;
|
|
|
|
|
} else if (p_preset->get("notarization/apple_id_name") != "" && p_preset->get("notarization/api_uuid") != "") {
|
|
|
|
|
err += TTR("Notarization: Both Apple ID name and App Store Connect issuer ID name are specified, only one should be set at the same time.") + "\n";
|
|
|
|
|
valid = false;
|
|
|
|
|
} else {
|
|
|
|
|
if (p_preset->get("notarization/apple_id_name") != "") {
|
|
|
|
|
if (p_preset->get("notarization/apple_id_password") == "") {
|
|
|
|
|
err += TTR("Notarization: Apple ID password not specified.") + "\n";
|
|
|
|
|
}
|
|
|
|
|
valid = false;
|
|
|
|
|
}
|
|
|
|
|
if (p_preset->get("notarization/api_uuid") != "") {
|
|
|
|
|
if (p_preset->get("notarization/api_key") == "") {
|
|
|
|
|
err += TTR("Notarization: App Store Connect API key ID not specified.") + "\n";
|
|
|
|
|
valid = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (notary_tool == 1) {
|
|
|
|
|
if (p_preset->get("notarization/api_uuid") == "") {
|
|
|
|
|
err += TTR("Notarization: App Store Connect issuer ID name not specified.") + "\n";
|
|
|
|
|
valid = false;
|
|
|
|
|
}
|
|
|
|
|
if (p_preset->get("notarization/api_key") == "") {
|
|
|
|
|
err += TTR("Notarization: App Store Connect API key ID not specified.") + "\n";
|
|
|
|
|
valid = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String rcodesign = EditorSettings::get_singleton()->get("export/macos/rcodesign").operator String();
|
|
|
|
|
if (rcodesign.is_empty()) {
|
|
|
|
|
err += TTR("Notarization: rcodesign path is not set. Configure rcodesign path in the Editor Settings (Export > macOS > rcodesign).") + "\n";
|
|
|
|
|
valid = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
err += TTR("Warning: Notarization is disabled. The exported project will be blocked by Gatekeeper if it's downloaded from an unknown source.") + "\n";
|
|
|
|
|
if (!sign_enabled) {
|
|
|
|
|
if (codesign_tool == 0) {
|
|
|
|
|
err += TTR("Code signing is disabled. The exported project will not run on Macs with enabled Gatekeeper and Apple Silicon powered Macs.") + "\n";
|
|
|
|
|
} else {
|
|
|
|
|
if ((bool)p_preset->get("codesign/hardened_runtime") && ad_hoc) {
|
|
|
|
|
err += TTR("Hardened Runtime is not compatible with ad-hoc signature, and will be disabled!") + "\n";
|
|
|
|
|
}
|
|
|
|
|
if ((bool)p_preset->get("codesign/timestamp") && ad_hoc) {
|
|
|
|
|
err += TTR("Timestamping is not compatible with ad-hoc signature, and will be disabled!") + "\n";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
err += TTR("Warning: Notarization is not supported from this OS. The exported project will be blocked by Gatekeeper if it's downloaded from an unknown source.") + "\n";
|
|
|
|
|
if (!sign_enabled) {
|
|
|
|
|
err += TTR("Code signing is disabled. The exported project will not run on Macs with enabled Gatekeeper and Apple Silicon powered Macs.") + "\n";
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (sign_enabled) {
|
|
|
|
|
if (codesign_tool > 0) {
|
|
|
|
|
if (ad_hoc) {
|
|
|
|
|
err += TTR("Code signing: Using ad-hoc signature. The exported project will be blocked by Gatekeeper") + "\n";
|
|
|
|
|
}
|
|
|
|
|
if (codesign_tool == 3) {
|
|
|
|
|
if (!FileAccess::exists("/usr/bin/codesign") && !FileAccess::exists("/bin/codesign")) {
|
|
|
|
|
err += TTR("Code signing: Xcode command line tools are not installed.") + "\n";
|
|
|
|
|
valid = false;
|
|
|
|
|
}
|
|
|
|
|
} else if (codesign_tool == 2) {
|
|
|
|
|
String rcodesign = EditorSettings::get_singleton()->get("export/macos/rcodesign").operator String();
|
|
|
|
|
if (rcodesign.is_empty()) {
|
|
|
|
|
err += TTR("Code signing: rcodesign path is not set. Configure rcodesign path in the Editor Settings (Export > macOS > rcodesign).") + "\n";
|
|
|
|
|
valid = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ((bool)p_preset->get("codesign/entitlements/audio_input") && ((String)p_preset->get("privacy/microphone_usage_description")).is_empty()) {
|
|
|
|
|
err += TTR("Privacy: Microphone access is enabled, but usage description is not specified.") + "\n";
|
|
|
|
|
valid = false;
|
|
|
|
|