1 | /**************************************************************************/ |
2 | /* gdextension_export_plugin.h */ |
3 | /**************************************************************************/ |
4 | /* This file is part of: */ |
5 | /* GODOT ENGINE */ |
6 | /* https://godotengine.org */ |
7 | /**************************************************************************/ |
8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ |
9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ |
10 | /* */ |
11 | /* Permission is hereby granted, free of charge, to any person obtaining */ |
12 | /* a copy of this software and associated documentation files (the */ |
13 | /* "Software"), to deal in the Software without restriction, including */ |
14 | /* without limitation the rights to use, copy, modify, merge, publish, */ |
15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ |
16 | /* permit persons to whom the Software is furnished to do so, subject to */ |
17 | /* the following conditions: */ |
18 | /* */ |
19 | /* The above copyright notice and this permission notice shall be */ |
20 | /* included in all copies or substantial portions of the Software. */ |
21 | /* */ |
22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ |
23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ |
24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ |
25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ |
26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ |
27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ |
28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
29 | /**************************************************************************/ |
30 | |
31 | #ifndef GDEXTENSION_EXPORT_PLUGIN_H |
32 | #define GDEXTENSION_EXPORT_PLUGIN_H |
33 | |
34 | #include "editor/export/editor_export.h" |
35 | |
36 | class GDExtensionExportPlugin : public EditorExportPlugin { |
37 | protected: |
38 | virtual void _export_file(const String &p_path, const String &p_type, const HashSet<String> &p_features); |
39 | virtual String get_name() const { return "GDExtension" ; } |
40 | }; |
41 | |
42 | void GDExtensionExportPlugin::_export_file(const String &p_path, const String &p_type, const HashSet<String> &p_features) { |
43 | if (p_type != "GDExtension" ) { |
44 | return; |
45 | } |
46 | |
47 | Ref<ConfigFile> config; |
48 | config.instantiate(); |
49 | |
50 | Error err = config->load(p_path); |
51 | ERR_FAIL_COND_MSG(err, "Failed to load GDExtension file: " + p_path); |
52 | |
53 | // Check whether this GDExtension should be exported. |
54 | bool android_aar_plugin = config->get_value("configuration" , "android_aar_plugin" , false); |
55 | if (android_aar_plugin && p_features.has("android" )) { |
56 | // The gdextension configuration and Android .so files will be provided by the Android aar |
57 | // plugin it's part of, so we abort here. |
58 | skip(); |
59 | return; |
60 | } |
61 | |
62 | ERR_FAIL_COND_MSG(!config->has_section_key("configuration" , "entry_symbol" ), "Failed to export GDExtension file, missing entry symbol: " + p_path); |
63 | |
64 | String entry_symbol = config->get_value("configuration" , "entry_symbol" ); |
65 | |
66 | HashSet<String> all_archs; |
67 | all_archs.insert("x86_32" ); |
68 | all_archs.insert("x86_64" ); |
69 | all_archs.insert("arm32" ); |
70 | all_archs.insert("arm64" ); |
71 | all_archs.insert("rv64" ); |
72 | all_archs.insert("ppc32" ); |
73 | all_archs.insert("ppc64" ); |
74 | all_archs.insert("wasm32" ); |
75 | all_archs.insert("universal" ); |
76 | |
77 | HashSet<String> archs; |
78 | HashSet<String> features_wo_arch; |
79 | for (const String &tag : p_features) { |
80 | if (all_archs.has(tag)) { |
81 | archs.insert(tag); |
82 | } else { |
83 | features_wo_arch.insert(tag); |
84 | } |
85 | } |
86 | |
87 | if (archs.is_empty()) { |
88 | archs.insert("unknown_arch" ); // Not archs specified, still try to match. |
89 | } |
90 | |
91 | for (const String &arch_tag : archs) { |
92 | PackedStringArray tags; |
93 | String library_path = GDExtension::find_extension_library( |
94 | p_path, config, [features_wo_arch, arch_tag](String p_feature) { return features_wo_arch.has(p_feature) || (p_feature == arch_tag); }, &tags); |
95 | if (!library_path.is_empty()) { |
96 | add_shared_object(library_path, tags); |
97 | |
98 | if (p_features.has("iOS" ) && (library_path.ends_with(".a" ) || library_path.ends_with(".xcframework" ))) { |
99 | String additional_code = "extern void register_dynamic_symbol(char *name, void *address);\n" |
100 | "extern void add_ios_init_callback(void (*cb)());\n" |
101 | "\n" |
102 | "extern \"C\" void $ENTRY();\n" |
103 | "void $ENTRY_init() {\n" |
104 | " if (&$ENTRY) register_dynamic_symbol((char *)\"$ENTRY\", (void *)$ENTRY);\n" |
105 | "}\n" |
106 | "struct $ENTRY_struct {\n" |
107 | " $ENTRY_struct() {\n" |
108 | " add_ios_init_callback($ENTRY_init);\n" |
109 | " }\n" |
110 | "};\n" |
111 | "$ENTRY_struct $ENTRY_struct_instance;\n\n" ; |
112 | additional_code = additional_code.replace("$ENTRY" , entry_symbol); |
113 | add_ios_cpp_code(additional_code); |
114 | |
115 | String linker_flags = "-Wl,-U,_" + entry_symbol; |
116 | add_ios_linker_flags(linker_flags); |
117 | } |
118 | } else { |
119 | Vector<String> features_vector; |
120 | for (const String &E : p_features) { |
121 | features_vector.append(E); |
122 | } |
123 | ERR_FAIL_MSG(vformat("No suitable library found for GDExtension: %s. Possible feature flags for your platform: %s" , p_path, String(", " ).join(features_vector))); |
124 | } |
125 | |
126 | List<String> dependencies; |
127 | if (config->has_section("dependencies" )) { |
128 | config->get_section_keys("dependencies" , &dependencies); |
129 | } |
130 | |
131 | for (const String &E : dependencies) { |
132 | Vector<String> dependency_tags = E.split("." ); |
133 | bool all_tags_met = true; |
134 | for (int i = 0; i < dependency_tags.size(); i++) { |
135 | String tag = dependency_tags[i].strip_edges(); |
136 | if (!p_features.has(tag)) { |
137 | all_tags_met = false; |
138 | break; |
139 | } |
140 | } |
141 | |
142 | if (all_tags_met) { |
143 | Dictionary dependency = config->get_value("dependencies" , E); |
144 | for (const Variant *key = dependency.next(nullptr); key; key = dependency.next(key)) { |
145 | String dependency_path = *key; |
146 | String target_path = dependency[*key]; |
147 | if (dependency_path.is_relative_path()) { |
148 | dependency_path = p_path.get_base_dir().path_join(dependency_path); |
149 | } |
150 | add_shared_object(dependency_path, dependency_tags, target_path); |
151 | } |
152 | break; |
153 | } |
154 | } |
155 | } |
156 | } |
157 | |
158 | #endif // GDEXTENSION_EXPORT_PLUGIN_H |
159 | |