Merge pull request #87029 from bruvzg/info_plist_keys

[macOS/iOS export] Add option to set custom Info.plist data.
This commit is contained in:
Rémi Verschelde 2024-01-11 20:46:02 +01:00
commit 26b1fd0d84
No known key found for this signature in database
GPG Key ID: C3336907360768E1
9 changed files with 108 additions and 24 deletions

View File

@ -448,7 +448,9 @@ PList::PList() {
} }
PList::PList(const String &p_string) { PList::PList(const String &p_string) {
load_string(p_string); String err_str;
bool ok = load_string(p_string, err_str);
ERR_FAIL_COND_MSG(!ok, "PList: " + err_str);
} }
uint64_t PList::read_bplist_var_size_int(Ref<FileAccess> p_file, uint8_t p_size) { uint64_t PList::read_bplist_var_size_int(Ref<FileAccess> p_file, uint8_t p_size) {
@ -642,11 +644,15 @@ bool PList::load_file(const String &p_filename) {
String ret; String ret;
ret.parse_utf8((const char *)array.ptr(), array.size()); ret.parse_utf8((const char *)array.ptr(), array.size());
return load_string(ret); String err_str;
bool ok = load_string(ret, err_str);
ERR_FAIL_COND_V_MSG(!ok, false, "PList: " + err_str);
return true;
} }
} }
bool PList::load_string(const String &p_string) { bool PList::load_string(const String &p_string, String &r_err_out) {
root = Ref<PListNode>(); root = Ref<PListNode>();
int pos = 0; int pos = 0;
@ -657,14 +663,16 @@ bool PList::load_string(const String &p_string) {
while (pos >= 0) { while (pos >= 0) {
int open_token_s = p_string.find("<", pos); int open_token_s = p_string.find("<", pos);
if (open_token_s == -1) { if (open_token_s == -1) {
ERR_FAIL_V_MSG(false, "PList: Unexpected end of data. No tags found."); r_err_out = "Unexpected end of data. No tags found.";
return false;
} }
int open_token_e = p_string.find(">", open_token_s); int open_token_e = p_string.find(">", open_token_s);
pos = open_token_e; pos = open_token_e;
String token = p_string.substr(open_token_s + 1, open_token_e - open_token_s - 1); String token = p_string.substr(open_token_s + 1, open_token_e - open_token_s - 1);
if (token.is_empty()) { if (token.is_empty()) {
ERR_FAIL_V_MSG(false, "PList: Invalid token name."); r_err_out = "Invalid token name.";
return false;
} }
String value; String value;
if (token[0] == '?' || token[0] == '!') { // Skip <?xml ... ?> and <!DOCTYPE ... > if (token[0] == '?' || token[0] == '!') { // Skip <?xml ... ?> and <!DOCTYPE ... >
@ -684,7 +692,8 @@ bool PList::load_string(const String &p_string) {
} }
if (!in_plist) { if (!in_plist) {
ERR_FAIL_V_MSG(false, "PList: Node outside of <plist> tag."); r_err_out = "Node outside of <plist> tag.";
return false;
} }
if (token == "dict") { if (token == "dict") {
@ -693,13 +702,15 @@ bool PList::load_string(const String &p_string) {
Ref<PListNode> dict = PListNode::new_dict(); Ref<PListNode> dict = PListNode::new_dict();
dict->data_type = PList::PLNodeType::PL_NODE_TYPE_DICT; dict->data_type = PList::PLNodeType::PL_NODE_TYPE_DICT;
if (!stack.back()->get()->push_subnode(dict, key)) { if (!stack.back()->get()->push_subnode(dict, key)) {
ERR_FAIL_V_MSG(false, "PList: Can't push subnode, invalid parent type."); r_err_out = "Can't push subnode, invalid parent type.";
return false;
} }
stack.push_back(dict); stack.push_back(dict);
} else { } else {
// Add root node. // Add root node.
if (!root.is_null()) { if (!root.is_null()) {
ERR_FAIL_V_MSG(false, "PList: Root node already set."); r_err_out = "Root node already set.";
return false;
} }
Ref<PListNode> dict = PListNode::new_dict(); Ref<PListNode> dict = PListNode::new_dict();
stack.push_back(dict); stack.push_back(dict);
@ -711,7 +722,8 @@ bool PList::load_string(const String &p_string) {
if (token == "/dict") { if (token == "/dict") {
// Exit current dict. // Exit current dict.
if (stack.is_empty() || stack.back()->get()->data_type != PList::PLNodeType::PL_NODE_TYPE_DICT) { if (stack.is_empty() || stack.back()->get()->data_type != PList::PLNodeType::PL_NODE_TYPE_DICT) {
ERR_FAIL_V_MSG(false, "PList: Mismatched </dict> tag."); r_err_out = "Mismatched </dict> tag.";
return false;
} }
stack.pop_back(); stack.pop_back();
continue; continue;
@ -722,13 +734,15 @@ bool PList::load_string(const String &p_string) {
// Add subnode end enter it. // Add subnode end enter it.
Ref<PListNode> arr = PListNode::new_array(); Ref<PListNode> arr = PListNode::new_array();
if (!stack.back()->get()->push_subnode(arr, key)) { if (!stack.back()->get()->push_subnode(arr, key)) {
ERR_FAIL_V_MSG(false, "PList: Can't push subnode, invalid parent type."); r_err_out = "Can't push subnode, invalid parent type.";
return false;
} }
stack.push_back(arr); stack.push_back(arr);
} else { } else {
// Add root node. // Add root node.
if (!root.is_null()) { if (!root.is_null()) {
ERR_FAIL_V_MSG(false, "PList: Root node already set."); r_err_out = "Root node already set.";
return false;
} }
Ref<PListNode> arr = PListNode::new_array(); Ref<PListNode> arr = PListNode::new_array();
stack.push_back(arr); stack.push_back(arr);
@ -740,7 +754,8 @@ bool PList::load_string(const String &p_string) {
if (token == "/array") { if (token == "/array") {
// Exit current array. // Exit current array.
if (stack.is_empty() || stack.back()->get()->data_type != PList::PLNodeType::PL_NODE_TYPE_ARRAY) { if (stack.is_empty() || stack.back()->get()->data_type != PList::PLNodeType::PL_NODE_TYPE_ARRAY) {
ERR_FAIL_V_MSG(false, "PList: Mismatched </array> tag."); r_err_out = "Mismatched </array> tag.";
return false;
} }
stack.pop_back(); stack.pop_back();
continue; continue;
@ -751,13 +766,15 @@ bool PList::load_string(const String &p_string) {
} else { } else {
int end_token_s = p_string.find("</", pos); int end_token_s = p_string.find("</", pos);
if (end_token_s == -1) { if (end_token_s == -1) {
ERR_FAIL_V_MSG(false, vformat("PList: Mismatched <%s> tag.", token)); r_err_out = vformat("Mismatched <%s> tag.", token);
return false;
} }
int end_token_e = p_string.find(">", end_token_s); int end_token_e = p_string.find(">", end_token_s);
pos = end_token_e; pos = end_token_e;
String end_token = p_string.substr(end_token_s + 2, end_token_e - end_token_s - 2); String end_token = p_string.substr(end_token_s + 2, end_token_e - end_token_s - 2);
if (end_token != token) { if (end_token != token) {
ERR_FAIL_V_MSG(false, vformat("PList: Mismatched <%s> and <%s> token pair.", token, end_token)); r_err_out = vformat("Mismatched <%s> and <%s> token pair.", token, end_token);
return false;
} }
value = p_string.substr(open_token_e + 1, end_token_s - open_token_e - 1); value = p_string.substr(open_token_e + 1, end_token_s - open_token_e - 1);
} }
@ -780,15 +797,18 @@ bool PList::load_string(const String &p_string) {
} else if (token == "date") { } else if (token == "date") {
var = PListNode::new_date(value); var = PListNode::new_date(value);
} else { } else {
ERR_FAIL_V_MSG(false, "PList: Invalid value type."); r_err_out = vformat("Invalid value type: %s.", token);
return false;
} }
if (stack.is_empty() || !stack.back()->get()->push_subnode(var, key)) { if (stack.is_empty() || !stack.back()->get()->push_subnode(var, key)) {
ERR_FAIL_V_MSG(false, "PList: Can't push subnode, invalid parent type."); r_err_out = "Can't push subnode, invalid parent type.";
return false;
} }
} }
} }
if (!stack.is_empty() || !done_plist) { if (!stack.is_empty() || !done_plist) {
ERR_FAIL_V_MSG(false, "PList: Unexpected end of data. Root node is not closed."); r_err_out = "Unexpected end of data. Root node is not closed.";
return false;
} }
return true; return true;
} }

View File

@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/ /**************************************************************************/
#ifndef MACOS_PLIST_H #ifndef PLIST_H
#define MACOS_PLIST_H #define PLIST_H
// Property list file format (application/x-plist) parser, property list ASN-1 serialization. // Property list file format (application/x-plist) parser, property list ASN-1 serialization.
@ -75,7 +75,7 @@ public:
PList(const String &p_string); PList(const String &p_string);
bool load_file(const String &p_filename); bool load_file(const String &p_filename);
bool load_string(const String &p_string); bool load_string(const String &p_string, String &r_err_out);
PackedByteArray save_asn1() const; PackedByteArray save_asn1() const;
String save_text() const; String save_text() const;
@ -125,4 +125,4 @@ public:
~PListNode() {} ~PListNode() {}
}; };
#endif // MACOS_PLIST_H #endif // PLIST_H

View File

@ -58,5 +58,6 @@ $usage_descriptions
</dict> </dict>
<key>NSHighResolutionCapable</key> <key>NSHighResolutionCapable</key>
$highres $highres
$additional_plist_content
</dict> </dict>
</plist> </plist>

View File

@ -10,6 +10,13 @@
<link title="iOS plugins documentation index">$DOCS_URL/tutorials/platform/ios/index.html</link> <link title="iOS plugins documentation index">$DOCS_URL/tutorials/platform/ios/index.html</link>
</tutorials> </tutorials>
<members> <members>
<member name="application/additional_plist_content" type="String" setter="" getter="">
Additional data added to the root [code]&lt;dict&gt;[/code] section of the [url=https://developer.apple.com/documentation/bundleresources/information_property_list]Info.plist[/url] file. The value should be an XML section with pairs of key-value elements, e.g.:
[codeblock]
&lt;key&gt;key_name&lt;/key&gt;
&lt;string&gt;value&lt;/string&gt;
[/codeblock]
</member>
<member name="application/app_store_team_id" type="String" setter="" getter=""> <member name="application/app_store_team_id" type="String" setter="" getter="">
Apple Team ID, unique 10-character string. To locate your Team ID check "Membership details" section in your Apple developer account dashboard, or "Organizational Unit" of your code signing certificate. See [url=https://developer.apple.com/help/account/manage-your-team/locate-your-team-id]Locate your Team ID[/url]. Apple Team ID, unique 10-character string. To locate your Team ID check "Membership details" section in your Apple developer account dashboard, or "Organizational Unit" of your code signing certificate. See [url=https://developer.apple.com/help/account/manage-your-team/locate-your-team-id]Locate your Team ID[/url].
</member> </member>

View File

@ -34,6 +34,7 @@
#include "run_icon_svg.gen.h" #include "run_icon_svg.gen.h"
#include "core/io/json.h" #include "core/io/json.h"
#include "core/io/plist.h"
#include "core/string/translation.h" #include "core/string/translation.h"
#include "editor/editor_node.h" #include "editor/editor_node.h"
#include "editor/editor_paths.h" #include "editor/editor_paths.h"
@ -154,6 +155,8 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/min_ios_version"), "12.0")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/min_ios_version"), "12.0"));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/additional_plist_content", PROPERTY_HINT_MULTILINE_TEXT), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/icon_interpolation", PROPERTY_HINT_ENUM, "Nearest neighbor,Bilinear,Cubic,Trilinear,Lanczos"), 4)); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/icon_interpolation", PROPERTY_HINT_ENUM, "Nearest neighbor,Bilinear,Cubic,Trilinear,Lanczos"), 4));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "application/export_project_only"), false)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "application/export_project_only"), false));
@ -1498,6 +1501,8 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
false false
}; };
config_data.plist_content += p_preset->get("application/additional_plist_content").operator String() + "\n";
Vector<IOSExportAsset> assets; Vector<IOSExportAsset> assets;
Ref<DirAccess> tmp_app_path = DirAccess::create_for_path(dest_dir); Ref<DirAccess> tmp_app_path = DirAccess::create_for_path(dest_dir);
@ -1867,6 +1872,26 @@ bool EditorExportPlatformIOS::has_valid_export_configuration(const Ref<EditorExp
valid = dvalid || rvalid; valid = dvalid || rvalid;
r_missing_templates = !valid; r_missing_templates = !valid;
const String &additional_plist_content = p_preset->get("application/additional_plist_content");
if (!additional_plist_content.is_empty()) {
const String &plist = vformat("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">"
"<plist version=\"1.0\">"
"<dict>\n"
"%s\n"
"</dict>\n"
"</plist>\n",
additional_plist_content);
String plist_err;
Ref<PList> plist_parser;
plist_parser.instantiate();
if (!plist_parser->load_string(plist, plist_err)) {
err += TTR("Invalid additional PList content: ") + plist_err + "\n";
valid = false;
}
}
if (!err.is_empty()) { if (!err.is_empty()) {
r_error = err; r_error = err;
} }

View File

@ -10,6 +10,13 @@
<link title="Running Godot apps on macOS">$DOCS_URL/tutorials//export/running_on_macos.html</link> <link title="Running Godot apps on macOS">$DOCS_URL/tutorials//export/running_on_macos.html</link>
</tutorials> </tutorials>
<members> <members>
<member name="application/additional_plist_content" type="String" setter="" getter="">
Additional data added to the root [code]&lt;dict&gt;[/code] section of the [url=https://developer.apple.com/documentation/bundleresources/information_property_list]Info.plist[/url] file. The value should be an XML section with pairs of key-value elements, e.g.:
[codeblock]
&lt;key&gt;key_name&lt;/key&gt;
&lt;string&gt;value&lt;/string&gt;
[/codeblock]
</member>
<member name="application/app_category" type="String" setter="" getter=""> <member name="application/app_category" type="String" setter="" getter="">
Application category for the App Store. Application category for the App Store.
</member> </member>

View File

@ -32,8 +32,8 @@
#include "lipo.h" #include "lipo.h"
#include "macho.h" #include "macho.h"
#include "plist.h"
#include "core/io/plist.h"
#include "core/os/os.h" #include "core/os/os.h"
#include "editor/editor_paths.h" #include "editor/editor_paths.h"
#include "editor/editor_settings.h" #include "editor/editor_settings.h"

View File

@ -41,11 +41,10 @@
// - Requirements code generator is not implemented (only hard-coded requirements for the ad-hoc signing is supported). // - Requirements code generator is not implemented (only hard-coded requirements for the ad-hoc signing is supported).
// - RFC5652/CMS blob generation is not implemented, supports ad-hoc signing only. // - RFC5652/CMS blob generation is not implemented, supports ad-hoc signing only.
#include "plist.h"
#include "core/crypto/crypto_core.h" #include "core/crypto/crypto_core.h"
#include "core/io/dir_access.h" #include "core/io/dir_access.h"
#include "core/io/file_access.h" #include "core/io/file_access.h"
#include "core/io/plist.h"
#include "core/object/ref_counted.h" #include "core/object/ref_counted.h"
#include "modules/modules_enabled.gen.h" // For regex. #include "modules/modules_enabled.gen.h" // For regex.

View File

@ -37,6 +37,7 @@
#include "run_icon_svg.gen.h" #include "run_icon_svg.gen.h"
#include "core/io/image_loader.h" #include "core/io/image_loader.h"
#include "core/io/plist.h"
#include "core/string/translation.h" #include "core/string/translation.h"
#include "drivers/png/png_driver_common.h" #include "drivers/png/png_driver_common.h"
#include "editor/editor_node.h" #include "editor/editor_node.h"
@ -388,6 +389,8 @@ void EditorExportPlatformMacOS::get_export_options(List<ExportOption> *r_options
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/export_angle", PROPERTY_HINT_ENUM, "Auto,Yes,No"), 0, true)); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/export_angle", PROPERTY_HINT_ENUM, "Auto,Yes,No"), 0, true));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "display/high_res"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "display/high_res"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/additional_plist_content", PROPERTY_HINT_MULTILINE_TEXT), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "xcode/platform_build"), "14C18")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "xcode/platform_build"), "14C18"));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "xcode/sdk_version"), "13.1")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "xcode/sdk_version"), "13.1"));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "xcode/sdk_build"), "22C55")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "xcode/sdk_build"), "22C55"));
@ -672,6 +675,8 @@ void EditorExportPlatformMacOS::_fix_plist(const Ref<EditorExportPreset> &p_pres
strnew += lines[i].replace("$min_version", p_preset->get("application/min_macos_version")) + "\n"; strnew += lines[i].replace("$min_version", p_preset->get("application/min_macos_version")) + "\n";
} else if (lines[i].find("$highres") != -1) { } else if (lines[i].find("$highres") != -1) {
strnew += lines[i].replace("$highres", p_preset->get("display/high_res") ? "\t<true/>" : "\t<false/>") + "\n"; strnew += lines[i].replace("$highres", p_preset->get("display/high_res") ? "\t<true/>" : "\t<false/>") + "\n";
} else if (lines[i].find("$additional_plist_content") != -1) {
strnew += lines[i].replace("$additional_plist_content", p_preset->get("application/additional_plist_content")) + "\n";
} else if (lines[i].find("$platfbuild") != -1) { } else if (lines[i].find("$platfbuild") != -1) {
strnew += lines[i].replace("$platfbuild", p_preset->get("xcode/platform_build")) + "\n"; strnew += lines[i].replace("$platfbuild", p_preset->get("xcode/platform_build")) + "\n";
} else if (lines[i].find("$sdkver") != -1) { } else if (lines[i].find("$sdkver") != -1) {
@ -2095,6 +2100,26 @@ bool EditorExportPlatformMacOS::has_valid_project_configuration(const Ref<Editor
}; };
} }
const String &additional_plist_content = p_preset->get("application/additional_plist_content");
if (!additional_plist_content.is_empty()) {
const String &plist = vformat("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">"
"<plist version=\"1.0\">"
"<dict>\n"
"%s\n"
"</dict>\n"
"</plist>\n",
additional_plist_content);
String plist_err;
Ref<PList> plist_parser;
plist_parser.instantiate();
if (!plist_parser->load_string(plist, plist_err)) {
err += TTR("Invalid additional PList content: ") + plist_err + "\n";
valid = false;
}
}
List<ExportOption> options; List<ExportOption> options;
get_export_options(&options); get_export_options(&options);
for (const EditorExportPlatform::ExportOption &E : options) { for (const EditorExportPlatform::ExportOption &E : options) {