-
Notifications
You must be signed in to change notification settings - Fork 7
/
godot_plugin.py
397 lines (307 loc) · 13.7 KB
/
godot_plugin.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
#!/usr/bin/python
#
# Python script used for developing, building, and managing the Godot plugin and environment
#
# Created by Christopher Cong on 12/02/24.
#
# Usage: python godot_plugin.py <command> [arguments]
#
import glob
import os
import sys
import tarfile
import shutil
import subprocess
try:
# Python 3
from urllib.request import urlretrieve
except ImportError:
# Python 2
from urllib import urlretrieve
def show_help():
help_text = """
Godot Build Script
-------------------
Usage: python godot_plugin.py <command> [arguments]
Commands:
Main Commands:
prepare_plugin_environment <VERSION> Set up the environment for plugin development (downloads Godot and Android libraries, generates iOS headers).
build_ios --version=<VERSION> [--debug] Build the iOS plugin.
build_android --version=<VERSION> [--debug] Build the Android plugin.
clean Remove artifacts generated by previous builds or downloads.
Plugin Development Commands:
download_godot <VERSION> Download and extract the specified Godot version.
download_android_lib <VERSION> Download the Godot Android library.
generate_ios_headers Generate iOS headers using SCons.
Options:
-h, --help Show this help message and exit.
Examples:
python godot_plugin.py clean
python godot_plugin.py prepare_ios_plugin 4.3
python godot_plugin.py build_ios --version=1.1.0
Description:
This script automates the build process for Godot projects, including downloading specific versions,
generating iOS headers, and performing clean and build. It combines common tasks into a single utility.
Report issues or contribute to the script on this repository.
"""
print(help_text)
### Main Commands ###
# Prepare the environment for plugin development and building. Downloads the Godot engine, Godot Android library, and generates the iOS headers.
def prepare_plugin_environment(version):
if not version:
print("Error: Please provide the Godot version as an argument.")
sys.exit(1)
try:
print("Preparing plugin environment...")
clean()
download_godot(version)
download_godot_android_lib(version)
generate_ios_headers()
generate_pods()
print("Plugin environment prepared successfully.")
except subprocess.CalledProcessError as e:
print("Prepare iOS plugin failed: {}".format(e))
# Build the iOS plugin in either debug or release target.
def build_ios(version=None, debug=False):
try:
target = "debug" if debug else "release"
print("Building iOS plugin in {} target...".format(target))
build_dir = "./Source/iOS/build"
if not os.path.exists(build_dir):
print("Creating directory: {}".format(build_dir))
os.makedirs(build_dir)
generate_static_library(target, build_dir)
# Move the xcframework to the plugin directory
plugin_name = "libAppLovinMAXGodotPlugin.a"
plugin_lib = "libAppLovinMAXGodotPlugin.{}.a".format(target)
plugin_path = "{}/{}".format(build_dir, plugin_lib)
rename(plugin_path, plugin_name)
move(build_dir + "/" + plugin_name, "ios/plugins/AppLovin-MAX-Godot-Plugin/")
print("Cleaning up intermediate files...")
for pattern in ["*.o", "*.d"]:
files = glob.glob(os.path.join("./Source/iOS/AppLovin-MAX-Godot-Plugin", "**", pattern), recursive=True)
for file in files:
print("Removing: {}".format(file))
os.remove(file)
print("iOS plugin build in {} target completed successfully.".format(target))
except Exception as e:
print("Error during iOS plugin {} build: {}".format(target), e)
# Build the Android plugin in either debug or release target.
def build_android(version=None, debug=False):
try:
target = "Debug" if debug else "Release"
gradle_task = "assemble{}".format(target)
# Build the Android plugin
print("Building Android plugin in {} target...".format(target))
subprocess.check_call(["./gradlew", gradle_task], cwd="Source/Android")
# Move the AAR to the plugin directory
aar_dir = "Source/Android/build/outputs/aar/"
plugin_name = "AppLovin-MAX-Godot-Plugin.aar"
rename("{}AppLovin-MAX-Godot-standalone-{}.aar".format(aar_dir, target.lower()), plugin_name)
move(aar_dir + plugin_name, "android/plugins/AppLovin-MAX-Godot-Plugin/")
print("Android plugin build in {} target completed successfully.".format(target))
except Exception as e:
print("Error during Android plugin {} build: {}".format(target), e)
# Clean up artifacts generated by previous builds or downloads.
def clean():
print("Cleaning up old artifacts and builds...")
# Remove the 'godot' folder if it exists
if os.path.exists("godot"):
print("Removing 'godot' folder...")
shutil.rmtree("godot")
# Remove any .tar.xz files
for file in os.listdir("."):
if file.endswith(".tar.xz"):
print("Removing archive: {}...".format(file))
os.remove(file)
# Remove godot-lib artifacts
godot_lib_files = glob.glob("godot-lib.*.aar")
for godot_lib_file in godot_lib_files:
print("Removing Godot Android library: {}...".format(godot_lib_file))
os.remove(godot_lib_file)
# Remove godot-lib from Source/Android/libs/
android_libs_dir = "Source/Android/libs/"
if os.path.exists(android_libs_dir):
print("Cleaning up Godot Android libraries in {}...".format(android_libs_dir))
for file in glob.glob(os.path.join(android_libs_dir, "godot-lib*.aar")):
print("Removing {}".format(file))
os.remove(file)
# Remove artifacts in iOS build directory
ios_build_dir = "Source/iOS/build"
if os.path.exists(ios_build_dir):
print("Cleaning up iOS build directory: {}...".format(ios_build_dir))
shutil.rmtree(ios_build_dir)
# Remove Pods
subprocess.call(["pod", "deintegrate"], cwd="Source/iOS")
print("Clean complete.")
### Plugin Development ###
def generate_ios_headers(timeout=15):
"""
Generate Godot iOS headers for the plugin using SCons, with a timeout.
Args:
timeout (int): Maximum time (in seconds) to allow the command to run.
"""
try:
os.chdir("./godot")
print("Running SCons to generate iOS headers...")
subprocess.check_call(
["scons", "platform=ios", "target=template_release"],
timeout=timeout
)
except subprocess.TimeoutExpired:
print("Error: The iOS header generation command timed out after {} seconds.".format(timeout))
except Exception as e:
print("Error while generating iOS headers: {}".format(e))
finally:
os.chdir("..")
# Generate the AppLovinSDK dependency
def generate_pods():
try:
print("Generating AppLovinSDK dependency...")
subprocess.call(["pod", "install"])
subprocess.check_call(["touch", ".gdignore"], cwd="Pods/")
except Exception as e:
print("Error while generating AppLovinSDK dependency: {}".format(e))
# Downloads the official Godot engine
def download_godot(version, stable=True):
if not version:
print("Error: Please provide the Godot version as an argument.")
sys.exit(1)
version_suffix = "-stable" if stable else ""
godot_folder = "godot-{}{}".format(version, version_suffix)
download_file = "{}.tar.xz".format(godot_folder)
url = "https://github.com/godotengine/godot/releases/download/{}{}/{}".format(version, version_suffix, download_file)
print("Downloading Godot version {} from {}...".format(version, url))
urlretrieve(url, download_file)
print("Extracting {}...".format(download_file))
with tarfile.open(download_file, "r:xz") as tar:
tar.extractall()
if os.path.exists("godot"):
print("Deleting existing 'godot' folder...")
shutil.rmtree("godot")
os.rename(godot_folder, "godot")
os.remove(download_file)
# Ignore the Godot project files for the Example Godot project
subprocess.check_call(["touch", ".gdignore"], cwd="godot/")
print("Download and extraction complete.")
# Download the Godot Android lib
def download_godot_android_lib(version, stable=True):
if not version:
print("Error: Please provide the Godot version as an argument.")
sys.exit(1)
try:
version_suffix = "-stable" if stable else ""
# Format the URL and output file name
download_file = "godot-lib.{}{}.template_release.aar".format(version.replace("-", "."), version_suffix.replace("-", "."))
url = "https://github.com/godotengine/godot/releases/download/{}{}/{}".format(
version, version_suffix, download_file
)
# Download the file
print("Downloading Godot Android library version {} from {}...".format(version, url))
urlretrieve(url, download_file)
print("Godot Android library downloaded successfully as {}".format(download_file))
godot_android_lib_name = "godot-lib-{}{}.aar".format(version, version_suffix)
rename(download_file, godot_android_lib_name)
move(godot_android_lib_name, "Source/Android/libs")
except Exception as e:
raise Exception("Error downloading Godot Android library: {}".format(e))
### Utils ###
def generate_static_library(target, build_dir):
try:
subprocess.check_call(["scons", "plugin={}".format("AppLovinMAXGodotPlugin"), "target={}".format(target), "arch=arm64"])
subprocess.check_call(["scons", "plugin={}".format("AppLovinMAXGodotPlugin"), "target={}".format(target), "arch=x86_64", "simulator=yes"])
subprocess.check_call([
"lipo", "-create",
"{}/lib{}.arm64-ios.{}.a".format(build_dir, "AppLovinMAXGodotPlugin", target),
"{}/lib{}.x86_64-simulator.{}.a".format(build_dir, "AppLovinMAXGodotPlugin", target),
"-output", "{}/lib{}.{}.a".format(build_dir, "AppLovinMAXGodotPlugin", target)
])
except subprocess.CalledProcessError as e:
raise Exception("An error occurred while generating the static library: {}".format(e))
# Moves a file from the source path to the destination directory.
def move(source_path, destination_dir):
try:
# Check if the source file exists
if not os.path.exists(source_path):
raise Exception("Source file {} does not exist.".format(source_path))
# Ensure the destination directory exists
if not os.path.exists(destination_dir):
print("Creating directory: {}".format(destination_dir))
os.makedirs(destination_dir)
# Extract the file name from the source path
file_name = os.path.basename(source_path)
# Construct the destination path
destination_path = os.path.join(destination_dir, file_name)
# Move the file
print("Moving {} to {}...".format(file_name, destination_dir))
shutil.move(source_path, destination_path)
print("File successfully moved to {}.".format(destination_path))
except Exception as e:
raise Exception("Error moving file: {}".format(e))
# Rename file at path
def rename(source_path, new_name):
try:
# Get the directory of the source file
directory = os.path.dirname(source_path)
# Construct the new path
new_path = os.path.join(directory, new_name)
# Rename the file
print("Renaming {} to {}...".format(source_path, new_path))
os.rename(source_path, new_path)
print("File successfully renamed to {}.".format(new_path))
return new_path
except Exception as e:
raise Exception("Error renaming file: {}".format(e))
### Main Script Execution ###
def main():
if len(sys.argv) < 2:
show_help()
sys.exit(1)
command = sys.argv[1]
try:
if command in ("-h", "--help"):
show_help()
elif command == "clean":
clean()
elif command == "prepare_plugin_environment":
if len(sys.argv) < 3:
print("Error: 'prepare_plugin_environment' requires a Godot version as an argument.")
sys.exit(1)
prepare_plugin_environment(sys.argv[2])
elif command == "build_ios":
version = None
debug = "--debug" in sys.argv
# Parse optional version argument
for arg in sys.argv[2:]:
if arg.startswith("--version="):
version = arg.split("=")[1]
build_ios(version=version, debug=debug)
elif command == "build_android":
version = None
debug = "--debug" in sys.argv
# Parse optional version argument
for arg in sys.argv[2:]:
if arg.startswith("--version="):
version = arg.split("=")[1]
build_android(version=version, debug=debug)
elif command == "download_godot":
if len(sys.argv) < 3:
print("Error: 'download_godot' requires a Godot version as an argument.")
sys.exit(1)
download_godot(sys.argv[2])
elif command == "download_android_lib":
if len(sys.argv) < 3:
print("Error: 'download_android_lib' requires a Godot version as an argument.")
sys.exit(1)
download_godot_android_lib(sys.argv[2])
elif command == "generate_ios_headers":
generate_ios_headers()
else:
print("Unknown command: {}".format(command))
show_help()
sys.exit(1)
except Exception as e:
print("Error: {}".format(e))
sys.exit(1)
if __name__ == "__main__":
main()