From e5c4351bc7a304848374ecbfe4a169badcd71578 Mon Sep 17 00:00:00 2001 From: Aaron Franke Date: Sun, 29 May 2022 03:51:33 -0500 Subject: [PATCH] Unify bits, android_arch, macos_arch ios_arch into arch, support non-x86 Unify arguments and add support for ARM64 and RV64 Linux --- .github/workflows/ci.yml | 15 ++-- README.md | 23 ++++-- SConstruct | 151 +++++++++++++++++++++++----------- binding_generator.py | 2 +- test/demo/example.gdextension | 12 ++- test/demo/icon.png.import | 11 ++- test/demo/main.gd | 4 +- test/demo/main.tscn | 6 -- test/demo/project.godot | 3 +- 9 files changed, 142 insertions(+), 85 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ffbd9680..39934cb1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,26 +13,26 @@ jobs: os: ubuntu-18.04 platform: linux artifact-name: godot-cpp-linux-glibc2.27-x86_64-release - artifact-path: bin/libgodot-cpp.linux.release.64.a + artifact-path: bin/libgodot-cpp.linux.release.x86_64.a - name: 🐧 Linux (GCC, Double Precision) os: ubuntu-18.04 platform: linux artifact-name: godot-cpp-linux-glibc2.27-x86_64-double-release - artifact-path: bin/libgodot-cpp.linux.release.64.a + artifact-path: bin/libgodot-cpp.linux.release.x86_64.a flags: float=64 - name: 🏁 Windows (x86_64, MSVC) os: windows-2019 platform: windows artifact-name: godot-cpp-windows-msvc2019-x86_64-release - artifact-path: bin/libgodot-cpp.windows.release.64.lib + artifact-path: bin/libgodot-cpp.windows.release.x86_64.lib - name: 🏁 Windows (x86_64, MinGW) os: windows-2019 platform: windows artifact-name: godot-cpp-linux-mingw-x86_64-release - artifact-path: bin/libgodot-cpp.windows.release.64.a + artifact-path: bin/libgodot-cpp.windows.release.x86_64.a flags: use_mingw=yes - name: 🍎 macOS (universal) @@ -40,20 +40,21 @@ jobs: platform: osx artifact-name: godot-cpp-macos-universal-release artifact-path: bin/libgodot-cpp.osx.release.universal.a - flags: macos_arch=universal + flags: arch=universal - name: 🤖 Android (arm64) os: ubuntu-18.04 platform: android artifact-name: godot-cpp-android-arm64-release - artifact-path: bin/libgodot-cpp.android.release.arm64v8.a - flags: android_arch=arm64v8 + artifact-path: bin/libgodot-cpp.android.release.arm64.a + flags: arch=arm64 - name: 🍏 iOS (arm64) os: macos-11 platform: ios artifact-name: godot-cpp-ios-arm64-release artifact-path: bin/libgodot-cpp.ios.release.arm64.a + flags: arch=arm64 steps: - name: Checkout diff --git a/README.md b/README.md index 59a80795..8c10c3b9 100644 --- a/README.md +++ b/README.md @@ -42,12 +42,15 @@ so formatting is done before your changes are submitted. ## Getting Started -It's a bit similar to what it was for 3.x but also a bit different. This new approach is much more akin to how core Godot modules are structured. +It's a bit similar to what it was for 3.x but also a bit different. +This new approach is much more akin to how core Godot modules are structured. Compiling this repository generates a static library to be linked with your shared lib, just like before. -To use the shared lib in your Godot project you'll need a `.gdextension` file, which replaces what was the `.gdnlib` before. Follow the example: +To use the shared lib in your Godot project you'll need a `.gdextension` +file, which replaces what was the `.gdnlib` before. +Follow [the example](test/demo/example.gdextension): ```ini [configuration] @@ -56,15 +59,17 @@ entry_symbol = "example_library_init" [libraries] -linux.64.debug = "bin/libgdexample.linux.debug.64.so" -linux.64.release = "bin/libgdexample.linux.release.64.so" -windows.64.debug = "bin/libgdexample.windows.debug.64.dll" -windows.64.release = "bin/libgdexample.windows.release.64.dll" -macos.debug = "bin/libgdexample.debug.framework" -macos.release = "bin/libgdexample.release.framework" +macos.debug = "bin/libgdexample.osx.debug.framework" +macos.release = "bin/libgdexample.osx.release.framework" +windows.debug.x86_64 = "bin/libgdexample.windows.debug.x86_64.dll" +windows.release.x86_64 = "bin/libgdexample.windows.release.x86_64.dll" +linux.debug.x86_64 = "bin/libgdexample.linux.debug.x86_64.so" +linux.release.x86_64 = "bin/libgdexample.linux.release.x86_64.so" +# Repeat for other architectures to support arm64, rv64, etc. ``` -The `entry_symbol` is the name of the function that initializes your library. It should be similar to following layout: +The `entry_symbol` is the name of the function that initializes +your library. It should be similar to following layout: ```cpp extern "C" { diff --git a/SConstruct b/SConstruct index c8a9508f..46462739 100644 --- a/SConstruct +++ b/SConstruct @@ -1,6 +1,7 @@ #!/usr/bin/env python import os +import platform import sys import subprocess from binding_generator import scons_generate_bindings, scons_emit_files @@ -82,15 +83,6 @@ else: env = Environment(ENV=os.environ) -is64 = sys.maxsize > 2**32 -if ( - env["TARGET_ARCH"] == "amd64" - or env["TARGET_ARCH"] == "emt64" - or env["TARGET_ARCH"] == "x86_64" - or env["TARGET_ARCH"] == "arm64-v8a" -): - is64 = True - opts = Variables([], ARGUMENTS) opts.Add( EnumVariable( @@ -101,7 +93,7 @@ opts.Add( ignorecase=2, ) ) -opts.Add(EnumVariable("bits", "Target platform bits", "64" if is64 else "32", ("32", "64"))) + opts.Add(BoolVariable("use_llvm", "Use the LLVM compiler - only effective when targeting Linux or FreeBSD", False)) opts.Add(BoolVariable("use_mingw", "Use the MinGW compiler instead of MSVC - only effective on Windows", False)) # Must be the same setting as used for cpp_bindings @@ -115,11 +107,8 @@ opts.Add(PathVariable("custom_api_file", "Path to a custom JSON API file", None, opts.Add( BoolVariable("generate_bindings", "Force GDExtension API bindings generation. Auto-detected by default.", False) ) -opts.Add(EnumVariable("android_arch", "Target Android architecture", "armv7", ["armv7", "arm64v8", "x86", "x86_64"])) opts.Add("macos_deployment_target", "macOS deployment target", "default") opts.Add("macos_sdk_path", "macOS SDK path", "") -opts.Add(EnumVariable("macos_arch", "Target macOS architecture", "universal", ["universal", "x86_64", "arm64"])) -opts.Add(EnumVariable("ios_arch", "Target iOS architecture", "arm64", ["universal", "arm64", "x86_64"])) opts.Add(BoolVariable("ios_simulator", "Target iOS Simulator", False)) opts.Add( "IPHONEPATH", @@ -129,7 +118,7 @@ opts.Add( opts.Add( "android_api_level", "Target Android API level", - "18" if ARGUMENTS.get("android_arch", "armv7") in ["armv7", "x86"] else "21", + "18" if "32" in ARGUMENTS.get("arch", "arm64") else "21", ) opts.Add( "ANDROID_NDK_ROOT", @@ -141,9 +130,54 @@ opts.Add(BoolVariable("generate_template_get_node", "Generate a template version opts.Add(BoolVariable("build_library", "Build the godot-cpp library.", True)) opts.Add(EnumVariable("float", "Floating-point precision", "32", ("32", "64"))) +# CPU architecture options. +architecture_array = ["", "universal", "x86_32", "x86_64", "arm32", "arm64", "rv64", "ppc32", "ppc64", "wasm32"] +architecture_aliases = { + "x64": "x86_64", + "amd64": "x86_64", + "armv7": "arm32", + "armv8": "arm64", + "arm64v8": "arm64", + "aarch64": "arm64", + "rv": "rv64", + "riscv": "rv64", + "riscv64": "rv64", + "ppcle": "ppc32", + "ppc": "ppc32", + "ppc64le": "ppc64", +} +opts.Add(EnumVariable("arch", "CPU architecture", "", architecture_array, architecture_aliases)) + opts.Update(env) Help(opts.GenerateHelpText(env)) +# Process CPU architecture argument. +if env["arch"] == "": + # No architecture specified. Default to arm64 if building for Android, + # universal if building for macOS or iOS, wasm32 if building for web, + # otherwise default to the host architecture. + if env["platform"] in ["osx", "ios"]: + env["arch"] = "universal" + elif env["platform"] == "android": + env["arch"] = "arm64" + elif env["platform"] == "javascript": + env["arch"] = "wasm32" + else: + host_machine = platform.machine().lower() + if host_machine in architecture_array: + env["arch"] = host_machine + elif host_machine in architecture_aliases.keys(): + env["arch"] = architecture_aliases[host_machine] + elif "86" in host_machine: + # Catches x86, i386, i486, i586, i686, etc. + env["arch"] = "x86_32" + else: + print("Unsupported CPU architecture: " + host_machine) + Exit() + +# We use this to re-set env["arch"] anytime we call opts.Update(env). +env_arch = env["arch"] + # Detect and print a warning listing unknown SCons variables to ease troubleshooting. unknown = opts.UnknownVariables() if unknown: @@ -151,16 +185,19 @@ if unknown: for item in unknown.items(): print(" " + item[0] + "=" + item[1]) +print("Building for architecture " + env["arch"] + " on platform " + env["platform"]) + # This makes sure to keep the session environment variables on Windows. # This way, you can run SCons in a Visual Studio 2017 prompt and it will find # all the required tools if host_platform == "windows" and env["platform"] != "android": - if env["bits"] == "64": + if env["arch"] == "x86_64": env = Environment(TARGET_ARCH="amd64") - elif env["bits"] == "32": + elif env["arch"] == "x86_32": env = Environment(TARGET_ARCH="x86") opts.Update(env) + env["arch"] = env_arch # Require C++17 if host_platform == "windows" and env["platform"] == "windows" and not env["use_mingw"]: @@ -187,26 +224,35 @@ if env["platform"] == "linux" or env["platform"] == "freebsd": elif env["target"] == "release": env.Append(CCFLAGS=["-O3"]) - if env["bits"] == "64": - env.Append(CCFLAGS=["-m64"]) - env.Append(LINKFLAGS=["-m64"]) - elif env["bits"] == "32": - env.Append(CCFLAGS=["-m32"]) - env.Append(LINKFLAGS=["-m32"]) + if env["arch"] == "x86_64": + # -m64 and -m32 are x86-specific already, but it doesn't hurt to + # be clear and also specify -march=x86-64. Similar with 32-bit. + env.Append(CCFLAGS=["-m64", "-march=x86-64"]) + env.Append(LINKFLAGS=["-m64", "-march=x86-64"]) + elif env["arch"] == "x86_32": + env.Append(CCFLAGS=["-m32", "-march=i686"]) + env.Append(LINKFLAGS=["-m32", "-march=i686"]) + elif env_arch == "arm64": + env.Append(CCFLAGS=["-march=armv8-a"]) + env.Append(LINKFLAGS=["-march=armv8-a"]) + elif env_arch == "rv64": + env.Append(CCFLAGS=["-march=rv64gc"]) + env.Append(LINKFLAGS=["-march=rv64gc"]) elif env["platform"] == "osx": + if env["arch"] not in ("universal", "arm64", "x86_64"): + print("Only universal, arm64, and x86_64 are supported on macOS. Exiting.") + Exit() + # Use Clang on macOS by default env["CXX"] = "clang++" - if env["bits"] == "32": - raise ValueError("Only 64-bit builds are supported for the macOS target.") - - if env["macos_arch"] == "universal": + if env["arch"] == "universal": env.Append(LINKFLAGS=["-arch", "x86_64", "-arch", "arm64"]) env.Append(CCFLAGS=["-arch", "x86_64", "-arch", "arm64"]) else: - env.Append(LINKFLAGS=["-arch", env["macos_arch"]]) - env.Append(CCFLAGS=["-arch", env["macos_arch"]]) + env.Append(LINKFLAGS=["-arch", env["arch"]]) + env.Append(CCFLAGS=["-arch", env["arch"]]) if env["macos_deployment_target"] != "default": env.Append(CCFLAGS=["-mmacosx-version-min=" + env["macos_deployment_target"]]) @@ -230,6 +276,10 @@ elif env["platform"] == "osx": env.Append(CCFLAGS=["-O3"]) elif env["platform"] == "ios": + if env["arch"] not in ("universal", "arm64", "x86_64"): + print("Only universal, arm64, and x86_64 are supported on iOS. Exiting.") + Exit() + if env["ios_simulator"]: sdk_name = "iphonesimulator" env.Append(CCFLAGS=["-mios-simulator-version-min=10.0"]) @@ -251,7 +301,7 @@ elif env["platform"] == "ios": env["RANLIB"] = compiler_path + "ranlib" env["SHLIBSUFFIX"] = ".dylib" - if env["ios_arch"] == "universal": + if env["arch"] == "universal": if env["ios_simulator"]: env.Append(LINKFLAGS=["-arch", "x86_64", "-arch", "arm64"]) env.Append(CCFLAGS=["-arch", "x86_64", "-arch", "arm64"]) @@ -259,8 +309,8 @@ elif env["platform"] == "ios": env.Append(LINKFLAGS=["-arch", "arm64"]) env.Append(CCFLAGS=["-arch", "arm64"]) else: - env.Append(LINKFLAGS=["-arch", env["ios_arch"]]) - env.Append(CCFLAGS=["-arch", env["ios_arch"]]) + env.Append(LINKFLAGS=["-arch", env["arch"]]) + env.Append(CCFLAGS=["-arch", env["arch"]]) env.Append(CCFLAGS=["-isysroot", sdk_path]) env.Append(LINKFLAGS=["-isysroot", sdk_path, "-F" + sdk_path]) @@ -282,12 +332,12 @@ elif env["platform"] == "windows": elif host_platform == "linux" or host_platform == "freebsd" or host_platform == "osx": # Cross-compilation using MinGW - if env["bits"] == "64": + if env["arch"] == "x86_64": env["CXX"] = "x86_64-w64-mingw32-g++" env["AR"] = "x86_64-w64-mingw32-ar" env["RANLIB"] = "x86_64-w64-mingw32-ranlib" env["LINK"] = "x86_64-w64-mingw32-g++" - elif env["bits"] == "32": + elif env["arch"] == "x86_32": env["CXX"] = "i686-w64-mingw32-g++" env["AR"] = "i686-w64-mingw32-ar" env["RANLIB"] = "i686-w64-mingw32-ranlib" @@ -297,6 +347,7 @@ elif env["platform"] == "windows": # Don't Clone the environment. Because otherwise, SCons will pick up msvc stuff. env = Environment(ENV=os.environ, tools=["mingw"]) opts.Update(env) + env["arch"] = env_arch # Still need to use C++17. env.Append(CCFLAGS=["-std=c++17"]) @@ -322,10 +373,15 @@ elif env["platform"] == "windows": ) elif env["platform"] == "android": + if env["arch"] not in ("arm64", "x86_64", "arm32", "x86_32"): + print("Only arm64, x86_64, arm32, and x86_32 are supported on Android. Exiting.") + Exit() + if host_platform == "windows": # Don't Clone the environment. Because otherwise, SCons will pick up msvc stuff. env = Environment(ENV=os.environ, tools=["mingw"]) opts.Update(env) + env["arch"] = env_arch # Long line hack. Use custom spawn, quick AR append (to avoid files with the same names to override each other). env["SPAWN"] = mySpawn @@ -339,7 +395,7 @@ elif env["platform"] == "android": # Validate API level api_level = int(env["android_api_level"]) - if env["android_arch"] in ["x86_64", "arm64v8"] and api_level < 21: + if "64" in env["arch"] and api_level < 21: print("WARN: 64-bit Android architectures require an API level of at least 21; setting android_api_level=21") env["android_api_level"] = "21" api_level = 21 @@ -360,21 +416,21 @@ elif env["platform"] == "android": # Get architecture info arch_info_table = { - "armv7": { + "arm32": { "march": "armv7-a", "target": "armv7a-linux-androideabi", "tool_path": "arm-linux-androideabi", "compiler_path": "armv7a-linux-androideabi", "ccflags": ["-mfpu=neon"], }, - "arm64v8": { + "arm64": { "march": "armv8-a", "target": "aarch64-linux-android", "tool_path": "aarch64-linux-android", "compiler_path": "aarch64-linux-android", "ccflags": [], }, - "x86": { + "x86_32": { "march": "i686", "target": "i686-linux-android", "tool_path": "i686-linux-android", @@ -389,7 +445,7 @@ elif env["platform"] == "android": "ccflags": [], }, } - arch_info = arch_info_table[env["android_arch"]] + arch_info = arch_info_table[env["arch"]] # Setup tools env["CC"] = toolchain + "/bin/clang" @@ -409,9 +465,14 @@ elif env["platform"] == "android": env.Append(CCFLAGS=["-O3"]) elif env["platform"] == "javascript": + if env["arch"] not in ("wasm32"): + print("Only wasm32 supported on web. Exiting.") + Exit() + if host_platform == "windows": env = Environment(ENV=os.environ, tools=["cc", "c++", "ar", "link", "textfile", "zip"]) opts.Update(env) + env["arch"] = env_arch else: env["ENV"] = os.environ @@ -474,17 +535,9 @@ add_sources(sources, "src/core", "cpp") add_sources(sources, "src/variant", "cpp") sources.extend([f for f in bindings if str(f).endswith(".cpp")]) -env["arch_suffix"] = env["bits"] -if env["platform"] == "android": - env["arch_suffix"] = env["android_arch"] -elif env["platform"] == "ios": - env["arch_suffix"] = env["ios_arch"] - if env["ios_simulator"]: - env["arch_suffix"] += ".simulator" -elif env["platform"] == "javascript": - env["arch_suffix"] = "wasm" -elif env["platform"] == "osx": - env["arch_suffix"] = env["macos_arch"] +env["arch_suffix"] = env["arch"] +if env["ios_simulator"]: + env["arch_suffix"] += ".simulator" library = None env["OBJSUFFIX"] = ".{}.{}.{}{}".format(env["platform"], env["target"], env["arch_suffix"], env["OBJSUFFIX"]) diff --git a/binding_generator.py b/binding_generator.py index 3e0bcd68..450624b3 100644 --- a/binding_generator.py +++ b/binding_generator.py @@ -67,7 +67,7 @@ def scons_generate_bindings(target, source, env): generate_bindings( str(source[0]), env["generate_template_get_node"], - env["bits"], + "32" if "32" in env["arch"] else "64", "double" if (env["float"] == "64") else "float", target[0].abspath, ) diff --git a/test/demo/example.gdextension b/test/demo/example.gdextension index a963bdc7..d7c834a3 100644 --- a/test/demo/example.gdextension +++ b/test/demo/example.gdextension @@ -4,9 +4,13 @@ entry_symbol = "example_library_init" [libraries] -linux.64.debug = "bin/libgdexample.linux.debug.64.so" -linux.64.release = "bin/libgdexample.linux.release.64.so" -windows.64.debug = "bin/libgdexample.windows.debug.64.dll" -windows.64.release = "bin/libgdexample.windows.release.64.dll" macos.debug = "bin/libgdexample.osx.debug.framework" macos.release = "bin/libgdexample.osx.release.framework" +windows.debug.x86_64 = "bin/libgdexample.windows.debug.x86_64.dll" +windows.release.x86_64 = "bin/libgdexample.windows.release.x86_64.dll" +linux.debug.x86_64 = "bin/libgdexample.linux.debug.x86_64.so" +linux.release.x86_64 = "bin/libgdexample.linux.release.x86_64.so" +linux.debug.arm64 = "bin/libgdexample.linux.debug.arm64.so" +linux.release.arm64 = "bin/libgdexample.linux.release.arm64.so" +linux.debug.rv64 = "bin/libgdexample.linux.debug.rv64.so" +linux.release.rv64 = "bin/libgdexample.linux.release.rv64.so" diff --git a/test/demo/icon.png.import b/test/demo/icon.png.import index fc6df247..36d7be27 100644 --- a/test/demo/icon.png.import +++ b/test/demo/icon.png.import @@ -1,9 +1,9 @@ [remap] importer="texture" -type="StreamTexture2D" +type="CompressedTexture2D" uid="uid://cswr8vy4lt7dt" -path="res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" +path="res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex" metadata={ "vram_texture": false } @@ -11,7 +11,7 @@ metadata={ [deps] source_file="res://icon.png" -dest_files=["res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.stex"] +dest_files=["res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"] [params] @@ -21,7 +21,6 @@ compress/hdr_compression=1 compress/bptc_ldr=0 compress/normal_map=0 compress/channel_pack=0 -compress/streamed=false mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 @@ -29,7 +28,7 @@ roughness/src_normal="" process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false -process/HDR_as_SRGB=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 -svg/scale=1.0 diff --git a/test/demo/main.gd b/test/demo/main.gd index 8ff16ac0..a0c8bff4 100644 --- a/test/demo/main.gd +++ b/test/demo/main.gd @@ -46,5 +46,5 @@ func _ready(): prints(" ANSWER_TO_EVERYTHING", $Example.ANSWER_TO_EVERYTHING) prints(" CONSTANT_WITHOUT_ENUM", $Example.CONSTANT_WITHOUT_ENUM) -func _on_Example_custom_signal(name, value): - prints("Example emitted:", name, value) +func _on_Example_custom_signal(signal_name, value): + prints("Example emitted:", signal_name, value) diff --git a/test/demo/main.tscn b/test/demo/main.tscn index 4b6a1a4c..2d101f8a 100644 --- a/test/demo/main.tscn +++ b/test/demo/main.tscn @@ -12,16 +12,10 @@ offset_left = 194.0 offset_top = -2.0 offset_right = 234.0 offset_bottom = 21.0 -__meta__ = { -"_edit_use_anchors_": false -} [node name="Button" type="Button" parent="."] offset_right = 79.0 offset_bottom = 29.0 text = "Click me!" -__meta__ = { -"_edit_use_anchors_": false -} [connection signal="custom_signal" from="Example" to="." method="_on_Example_custom_signal"] diff --git a/test/demo/project.godot b/test/demo/project.godot index d1c7331f..904beb0d 100644 --- a/test/demo/project.godot +++ b/test/demo/project.godot @@ -6,13 +6,14 @@ ; [section] ; section goes between [] ; param=value ; assign values to parameters -config_version=4 +config_version=5 [application] config/name="GDExtension Test Project" run/main_scene="res://main.tscn" config/icon="res://icon.png" +config/features=PackedStringArray("4.0") [native_extensions]