From da656d6aa5d69cbf210fcc5c9a42e31952f3a9b9 Mon Sep 17 00:00:00 2001 From: Fabio Alessandrelli Date: Mon, 26 May 2025 17:56:56 +0200 Subject: [PATCH 1/3] Use mbedTLS instead of OpenSSL Build dependencies using cmake --- .gitmodules | 7 +- SConstruct | 15 +-- godot-git-plugin/SCsub | 4 +- thirdparty/git2/libgit2 | 2 +- thirdparty/mbedtls | 1 + thirdparty/openssl | 1 - thirdparty/ssh2/libssh2 | 2 +- tools/git2.py | 22 ++- tools/mbedtls.py | 69 ++++++++++ tools/openssl.py | 288 ---------------------------------------- tools/ssh2.py | 13 +- 11 files changed, 98 insertions(+), 326 deletions(-) create mode 160000 thirdparty/mbedtls delete mode 160000 thirdparty/openssl create mode 100644 tools/mbedtls.py delete mode 100644 tools/openssl.py diff --git a/.gitmodules b/.gitmodules index 4d5dd20c..e231b39f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,7 +1,6 @@ [submodule "godot-cpp"] path = godot-cpp url = https://github.com/godotengine/godot-cpp - branch = 3.x [submodule "thirdparty/git2/libgit2"] path = thirdparty/git2/libgit2 url = https://github.com/libgit2/libgit2 @@ -9,6 +8,6 @@ [submodule "thirdparty/ssh2/libssh2"] path = thirdparty/ssh2/libssh2 url = https://github.com/libssh2/libssh2 -[submodule "thirdparty/openssl"] - path = thirdparty/openssl - url = https://github.com/openssl/openssl +[submodule "thirdparty/mbedtls"] + path = thirdparty/mbedtls + url = https://github.com/Mbed-TLS/mbedtls.git diff --git a/SConstruct b/SConstruct index ad759fe0..4e4a6b5e 100644 --- a/SConstruct +++ b/SConstruct @@ -29,22 +29,19 @@ env.PrependENVPath("PATH", os.getenv("PATH")) # Prepend PATH, done upstream in if env["platform"] == "windows" and env.get("is_msvc", False): env.AppendUnique(LINKFLAGS=["/LTCG"]) -# OpenSSL Builder -env.Tool("openssl", toolpath=["tools"]) - -# SSH2 Builder env.Tool("cmake", toolpath=["tools"]) +env.Tool("mbedtls", toolpath=["tools"]) env.Tool("ssh2", toolpath=["tools"]) env.Tool("git2", toolpath=["tools"]) opts.Update(env) -ssl = env.OpenSSL() -ssh2 = env.BuildSSH2(ssl) -ssl += ssh2 -git2 = env.BuildGIT2(ssl) +mbedtls = env.BuildMbedTLS() +ssh2 = env.BuildSSH2(mbedtls) +mbedtls += ssh2 +git2 = env.BuildGIT2(mbedtls) -Export("ssl") +Export("mbedtls") Export("env") SConscript("godot-git-plugin/SCsub") diff --git a/godot-git-plugin/SCsub b/godot-git-plugin/SCsub index 05ec9944..f924ca66 100644 --- a/godot-git-plugin/SCsub +++ b/godot-git-plugin/SCsub @@ -4,7 +4,7 @@ import os env = {} Import("env") -Import("ssl") +Import("mbedtls") env["target_path"] = "../" + env["target_path"] @@ -29,7 +29,7 @@ env.Append(CPPPATH=[".", "src/"]) env.Append(CPPPATH=["#thirdparty/git2/libgit2/include/"]) lib_sources = Glob("src/*.cpp") -env.Depends(lib_sources, ssl) +env.Depends(lib_sources, mbedtls) library = env.SharedLibrary( target=env["target_path"] + "{}{}{}".format(env["target_name"], env["suffix"], env["SHLIBSUFFIX"]), diff --git a/thirdparty/git2/libgit2 b/thirdparty/git2/libgit2 index 3e2baa6d..338e6fb6 160000 --- a/thirdparty/git2/libgit2 +++ b/thirdparty/git2/libgit2 @@ -1 +1 @@ -Subproject commit 3e2baa6d0bfb42f9016e24cba1733a6ae26a8ae6 +Subproject commit 338e6fb681369ff0537719095e22ce9dc602dbf0 diff --git a/thirdparty/mbedtls b/thirdparty/mbedtls new file mode 160000 index 00000000..22098d41 --- /dev/null +++ b/thirdparty/mbedtls @@ -0,0 +1 @@ +Subproject commit 22098d41c6620ce07cf8a0134d37302355e1e5ef diff --git a/thirdparty/openssl b/thirdparty/openssl deleted file mode 160000 index 2cf4e90e..00000000 --- a/thirdparty/openssl +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2cf4e90eaaf7402bf038b158dbdacd0a15561fb7 diff --git a/thirdparty/ssh2/libssh2 b/thirdparty/ssh2/libssh2 index 1c3f1b7d..a312b433 160000 --- a/thirdparty/ssh2/libssh2 +++ b/thirdparty/ssh2/libssh2 @@ -1 +1 @@ -Subproject commit 1c3f1b7da588f2652260285529ec3c1f1125eb4e +Subproject commit a312b43325e3383c865a87bb1d26cb52e3292641 diff --git a/tools/git2.py b/tools/git2.py index 80d80b9a..52de8f40 100644 --- a/tools/git2.py +++ b/tools/git2.py @@ -4,23 +4,23 @@ def build_library(env, deps): config = { "CMAKE_BUILD_TYPE": "RelWithDebInfo" if env["debug_symbols"] else "Release", - "OPENSSL_USE_STATIC_LIBS": 1, - "OPENSSL_INCLUDE_DIR": env["SSL_INCLUDE"], - "OPENSSL_SSL_LIBRARY": env["SSL_LIBRARY"], - "OPENSSL_CRYPTO_LIBRARY": env["SSL_CRYPTO_LIBRARY"], - "OPENSSL_ROOT_DIR": env["SSL_INSTALL"], + "CMAKE_C_STANDARD": "99", + "USE_SHARED_MBEDTLS_LIBRARY": 0, + "USE_STATIC_MBEDTLS_LIBRARY": 1, + "MBEDTLS_LIBRARY": env["MBEDTLS_LIBRARY"], + "MBEDCRYPTO_LIBRARY": env["MBEDTLS_CRYPTO_LIBRARY"], + "MBEDX509_LIBRARY": env["MBEDTLS_X509_LIBRARY"], + "MBEDTLS_INCLUDE_DIR": env["MBEDTLS_INCLUDE"], "BUILD_TESTS": 0, "BUILD_CLI": 0, "BUILD_EXAMPLES": 0, "BUILD_FUZZERS": 0, - "USE_SSH": 1, - "USE_HTTPS": 1, - "USE_SHA1": 1, + "USE_SSH": "libssh2", "USE_BUNDLED_ZLIB": 1, "USE_HTTP_PARSER": "builtin", "REGEX_BACKEND": "builtin", - "USE_HTTPS": "OpenSSL", - "USE_SHA1": "OpenSSL", + "USE_HTTPS": "mbedTLS", + "USE_SHA1": "mbedTLS", "BUILD_SHARED_LIBS": 0, "LINK_WITH_STATIC_LIBRARIES": 1, "LIBSSH2_INCLUDE_DIR": env.Dir("#thirdparty/ssh2/libssh2/include").abspath, @@ -31,8 +31,6 @@ def build_library(env, deps): if env["platform"] != "windows": config["CMAKE_C_FLAGS"] = "-fPIC" - else: - config["OPENSSL_ROOT_DIR"] = env["SSL_BUILD"] is_msvc = env.get("is_msvc", False) lib_ext = ".lib" if is_msvc else ".a" diff --git a/tools/mbedtls.py b/tools/mbedtls.py new file mode 100644 index 00000000..1891f0f0 --- /dev/null +++ b/tools/mbedtls.py @@ -0,0 +1,69 @@ +def build_library(env): + mbedtls_bin = env.Dir("bin/thirdparty/mbedtls/{}/{}/install".format(env["platform"], env["arch"])) + is_msvc = env.get("is_msvc", False) + c_flags = "-DMBEDTLS_SSL_DTLS_SRTP" + if env["platform"] == "linux": + # This is needed on some arch when building with the godot buildroot toolchain + c_flags += " -fPIC" + elif env["platform"] == "windows" and not is_msvc: + c_flags += " -D__USE_MINGW_ANSI_STDIO=0" # See https://github.com/Mbed-TLS/mbedtls/issues/10161 + + mbedtls_config = { + "CMAKE_BUILD_TYPE": "RelWithDebInfo" if env["debug_symbols"] else "Release", + "ENABLE_TESTING": 0, + "ENABLE_PROGRAMS": 0, + "CMAKE_INSTALL_PREFIX": env.Dir(mbedtls_bin).abspath, + "CMAKE_INSTALL_LIBDIR": "lib", + "CMAKE_C_FLAGS": c_flags, + "MBEDTLS_FATAL_WARNINGS": 0, + } + lib_ext = ".lib" if is_msvc else ".a" + lib_prefix = "" if is_msvc else "lib" + mbedtls_libs = [ + "/install/lib/{}mbedtls{}".format(lib_prefix, lib_ext), + "/install/lib/{}mbedx509{}".format(lib_prefix, lib_ext), + "/install/lib/{}mbedcrypto{}".format(lib_prefix, lib_ext), + ] + + mbedtls_cmake_config = [ + "/install/lib/cmake/MbedTLS/MbedTLSConfig.cmake", + "/install/lib/cmake/MbedTLS/MbedTLSConfigVersion.cmake", + "/install/lib/cmake/MbedTLS/MbedTLSTargets.cmake", + ] + + # Build libdatachannel + mbedtls = env.CMakeBuild( + env.Dir("bin/thirdparty/mbedtls/"), + env.Dir("thirdparty/mbedtls"), + cmake_options=mbedtls_config, + cmake_outputs=mbedtls_libs + mbedtls_cmake_config, + install=True, + ) + + # Configure env. + if env["platform"] == "windows": + env.PrependUnique(LIBS=["bcrypt", "ws2_32", "iphlpapi"]) + if env["platform"] == "linux": + env.PrependUnique(LIBS=["pthread"]) + env.Prepend(LIBS=list(filter(lambda f: str(f).endswith(lib_ext), mbedtls))) + env.Append(CPPPATH=[env.Dir("thirdparty/mbedtls/include")]) + + return mbedtls + + +def exists(env): + return "CMake" in env + + +def generate(env): + mbedtls_install_dir = "bin/thirdparty/mbedtls/{}/{}/install".format(env["platform"], env["arch"]) + lib_ext = ".lib" if env.get("is_msvc", False) else ".a" + mbedtls = env.File(mbedtls_install_dir + "/lib/libmbedtls" + lib_ext) + crypto = env.File(mbedtls_install_dir + "/lib/libmbedcrypto" + lib_ext) + x509 = env.File(mbedtls_install_dir + "/lib/libmbedx509" + lib_ext) + includes = env.Dir("thirdparty/mbedtls/include") + env.AddMethod(build_library, "BuildMbedTLS") + env["MBEDTLS_LIBRARY"] = mbedtls.abspath + env["MBEDTLS_CRYPTO_LIBRARY"] = crypto.abspath + env["MBEDTLS_X509_LIBRARY"] = x509.abspath + env["MBEDTLS_INCLUDE"] = includes.abspath diff --git a/tools/openssl.py b/tools/openssl.py deleted file mode 100644 index 99a2426e..00000000 --- a/tools/openssl.py +++ /dev/null @@ -1,288 +0,0 @@ -import os, sys -import SCons.Util -import SCons.Builder -import SCons.Action -from SCons.Defaults import Mkdir -from SCons.Variables import PathVariable, BoolVariable - - -def ssl_platform_target(env): - targets = {} - platform = env["platform"] - if platform == "linux": - targets = { - "x86_32": "linux-x86", - "x86_64": "linux-x86_64", - "arm64": "linux-aarch64", - "arm32": "linux-armv4", - "rv64": "linux64-riscv64", - } - elif platform == "android": - targets = { - "arm64": "android-arm64", - "arm32": "android-arm", - "x86_32": "android-x86", - "x86_64": "android-x86_64", - } - elif platform == "macos": - targets = { - "x86_64": "darwin64-x86_64", - "arm64": "darwin64-arm64", - } - elif platform == "ios": - if env["ios_simulator"]: - targets = { - "x86_64": "iossimulator-xcrun", - "arm64": "iossimulator-xcrun", - } - else: - targets = { - "arm64": "ios64-xcrun", - "arm32": "ios-xcrun", - } - elif platform == "windows": - if env.get("is_msvc", False): - targets = { - "x86_32": "VC-WIN32", - "x86_64": "VC-WIN64A", - } - else: - targets = { - "x86_32": "mingw", - "x86_64": "mingw64", - } - - arch = env["arch"] - target = targets.get(arch, "") - if target == "": - raise ValueError("Architecture '%s' not supported for platform: '%s'" % (arch, platform)) - return target - - -def ssl_platform_options(env): - ssl_config_options = [ - "no-ssl2", - "no-ssl3", - "no-weak-ssl-ciphers", - "no-legacy", - "no-shared", - "no-tests", - ] - if env["platform"] == "windows": - ssl_config_options.append("enable-capieng") - return ssl_config_options - - -def ssl_platform_flags(env): - args = [] - if env["platform"] == "android": - if env.get("android_api_level", ""): - api = int(env["android_api_level"]) - args.append("-D__ANDROID_API__=%s" % api) - elif env["platform"] == "macos": - if env["macos_deployment_target"] != "default": - args.append("-mmacosx-version-min=%s" % env["macos_deployment_target"]) - # OSXCross toolchain setup. - if sys.platform != "darwin" and "OSXCROSS_ROOT" in os.environ: - for k in ["CC", "CXX", "AR", "AS", "RANLIB"]: - args.append("%s=%s" % (k, env[k])) - elif env["platform"] == "windows": - is_win_host = sys.platform in ["win32", "msys", "cygwin"] - if not (is_win_host or env.get("is_msvc", False)): - mingw_prefixes = { - "x86_32": "--cross-compile-prefix=i686-w64-mingw32-", - "x86_64": "--cross-compile-prefix=x86_64-w64-mingw32-", - } - args.append(mingw_prefixes[env["arch"]]) - return args - - -def ssl_configure_args(env): - if env.get("openssl_configure_options", ""): - opts = SCons.Util.CLVar(env["openssl_configure_options"]) - else: - opts = ssl_platform_options(env) - - if env.get("openssl_configure_target", ""): - target = [env["openssl_configure_target"]] - else: - target = [ssl_platform_target(env)] - - if env.get("openssl_configure_flags", ""): - flags = SCons.Util.CLVar(env["openssl_configure_flags"]) - else: - flags = ssl_platform_flags(env) - - return opts + target + flags - - -def ssl_emitter(target, source, env): - return env["SSL_LIBS"], [env.File(env["SSL_SOURCE"] + "/Configure"), env.File(env["SSL_SOURCE"] + "/VERSION.dat")] - - -def build_openssl(env, jobs=None): - if env["SSL_EXTERNAL"]: - # Setup the env to use the provided libraries, and return them without building. - env.Prepend(CPPPATH=[env["SSL_INCLUDE"]]) - env.Prepend(LIBPATH=[env["SSL_BUILD"]]) - if env["platform"] == "windows": - env.PrependUnique(LIBS=["crypt32", "ws2_32", "advapi32", "user32"]) - env.Prepend(LIBS=env["SSL_LIBS"]) - return [env["SSL_CRYPTO_LIBRARY"], env["SSL_LIBRARY"]] - - if jobs is None: - jobs = int(env.GetOption("num_jobs")) - - # Since the OpenSSL build system does not support macOS universal binaries, we first need to build the two libraries - # separately, then we join them together using lipo. - if env["platform"] == "macos" and env["arch"] == "universal": - build_envs = { - "x86_64": env.Clone(), - "arm64": env.Clone(), - } - arch_ssl = [] - for arch in build_envs: - benv = build_envs[arch] - benv["arch"] = arch - generate(benv) - benv["SSLBUILDJOBS"] = max([1, int(jobs / len(build_envs))]) - ssl = benv.OpenSSLBuilder() - arch_ssl.extend(ssl) - benv.NoCache(ssl) # Needs refactoring to properly cache generated headers. - - # x86_64 and arm64 includes are equivalent. - env["SSL_INCLUDE"] = build_envs["arm64"]["SSL_INCLUDE"] - - # Join libraries using lipo. - lipo_action = "lipo $SOURCES -create -output $TARGET" - ssl_libs = list(map(lambda arch: build_envs[arch]["SSL_LIBRARY"], build_envs)) - ssl_crypto_libs = list(map(lambda arch: build_envs[arch]["SSL_CRYPTO_LIBRARY"], build_envs)) - ssl = env.Command(env["SSL_LIBRARY"], ssl_libs, lipo_action) - ssl += env.Command(env["SSL_CRYPTO_LIBRARY"], ssl_crypto_libs, lipo_action) - env.Depends(ssl, arch_ssl) - else: - benv = env.Clone() - benv["SSLBUILDJOBS"] = jobs - ssl = benv.OpenSSLBuilder() - benv.NoCache(ssl) # Needs refactoring to properly cache generated headers. - - # Setup the environment to use the freshly built openssl. - env.Prepend(CPPPATH=[env["SSL_INCLUDE"]]) - env.Prepend(LIBPATH=[env["SSL_BUILD"]]) - if env["platform"] == "windows": - env.PrependUnique(LIBS=["crypt32", "ws2_32", "advapi32", "user32"]) - env.Prepend(LIBS=env["SSL_LIBS"]) - - return ssl - - -def ssl_generator(target, source, env, for_signature): - # Strip the -j option for signature to avoid rebuilding when num_jobs changes. - build = env["SSLBUILDCOM"].replace("-j$SSLBUILDJOBS", "") if for_signature else env["SSLBUILDCOM"] - return [ - Mkdir("$SSL_BUILD"), - Mkdir("$SSL_INSTALL"), - SCons.Action.Action("$SSLCONFIGCOM", "$SSLCONFIGCOMSTR"), - SCons.Action.Action(build, "$SSLBUILDCOMSTR"), - ] - - -def options(opts): - opts.Add(PathVariable("openssl_source", "Path to the openssl sources.", "thirdparty/openssl")) - opts.Add("openssl_build", "Destination path of the openssl build.", "bin/thirdparty/openssl") - opts.Add( - "openssl_configure_options", - "OpenSSL configure options override. Will use a reasonable default if not specified.", - "", - ) - opts.Add( - "openssl_configure_target", "OpenSSL configure target override, will be autodetected if not specified.", "" - ) - opts.Add( - "openssl_configure_flags", - "OpenSSL configure compiler flags override. Will be autodetected if not specified.", - "", - ) - opts.Add( - "openssl_external_crypto", - 'An external libcrypto static library (e.g. "/usr/lib/x86_64-linux-gnu/libcrypto.a"). If not provided, OpenSSL will be built from source.', - "", - ) - opts.Add( - "openssl_external_ssl", - 'An external libssl static library (e.g. "/usr/lib/x86_64-linux-gnu/libssl.a"). If not provided, OpenSSL will be built from source.', - "", - ) - opts.Add( - "openssl_external_include", - 'An external OpenSSL "include" folder (e.g. "/usr/include/openssl").', - "", - ) - - -def exists(env): - return True - - -def generate(env): - env.AddMethod(build_openssl, "OpenSSL") - - # Check if the user specified infos about external OpenSSL files. - external_opts = ["openssl_external_crypto", "openssl_external_ssl", "openssl_external_include"] - is_set = lambda k: env.get(k, "") != "" - if any(map(is_set, external_opts)): - # Need provide the whole (crypto, ssl, include) triple to proceed. - if not all(map(is_set, external_opts)): - print('Error: The options "%s" must all be set to use a external library.' % '", "'.join(external_opts)) - sys.exit(255) - - env["SSL_CRYPTO_LIBRARY"] = env.File("${openssl_external_crypto}") - env["SSL_LIBRARY"] = env.File("${openssl_external_ssl}") - env["SSL_BUILD"] = env.Dir("${SSL_LIBRARY.dir}").abspath - env["SSL_INSTALL"] = env.Dir("${SSL_LIBRARY.dir}").abspath - env["SSL_INCLUDE"] = env.Dir("${openssl_external_include}").abspath - env["SSL_LIBS"] = [env["SSL_LIBRARY"], env["SSL_CRYPTO_LIBRARY"]] - env["SSL_EXTERNAL"] = True - return - - # We will need to build our own OpenSSL library. - env["SSL_EXTERNAL"] = False - - # Android needs the NDK in ENV, and proper PATH setup. - if env["platform"] == "android" and env["ENV"].get("ANDROID_NDK_ROOT", "") == "": - cc_path = os.path.dirname(env["CC"]) - if cc_path and cc_path not in env["ENV"]: - env.PrependENVPath("PATH", cc_path) - if "ANDROID_NDK_ROOT" not in env["ENV"]: - env["ENV"]["ANDROID_NDK_ROOT"] = env.get("ANDROID_NDK_ROOT", os.environ.get("ANDROID_NDK_ROOT", "")) - - env["SSL_SOURCE"] = env.Dir(env["openssl_source"]).abspath - env["SSL_BUILD"] = env.Dir(env["openssl_build"] + "/{}/{}".format(env["platform"], env["arch"])).abspath - env["SSL_INSTALL"] = env.Dir(env["SSL_BUILD"] + "/dest").abspath - env["SSL_INCLUDE"] = env.Dir(env["SSL_INSTALL"] + "/include").abspath - lib_ext = ".lib" if env.get("is_msvc", False) else ".a" - env["SSL_LIBRARY"] = env.File(env["SSL_BUILD"] + "/libssl" + lib_ext) - env["SSL_CRYPTO_LIBRARY"] = env.File(env["SSL_BUILD"] + "/libcrypto" + lib_ext) - env["SSL_LIBS"] = [env["SSL_LIBRARY"], env["SSL_CRYPTO_LIBRARY"]] - - # Configure action - env["PERL"] = env.get("PERL", "perl") - env["_ssl_configure_args"] = ssl_configure_args - env["SSLPLATFORMCONFIG"] = "${_ssl_configure_args(__env__)}" - env["SSLCONFFLAGS"] = SCons.Util.CLVar("") - # fmt: off - env["SSLCONFIGCOM"] = 'cd ${TARGET.dir} && $PERL -- ${SOURCE.abspath} --prefix="${SSL_INSTALL}" --openssldir="${SSL_INSTALL}" $SSLPLATFORMCONFIG $SSLCONFFLAGS' - # fmt: on - - # Build action - env["SSLBUILDJOBS"] = "${__env__.GetOption('num_jobs')}" - # fmt: off - env["SSLBUILDCOM"] = "make -j$SSLBUILDJOBS -C ${TARGET.dir} && make -j$SSLBUILDJOBS -C ${TARGET.dir} install_sw install_ssldirs" - # fmt: on - - # Windows MSVC needs to build using NMake - if env["platform"] == "windows" and env.get("is_msvc", False): - env["SSLBUILDCOM"] = "cd ${TARGET.dir} && nmake install_sw install_ssldirs" - - env["BUILDERS"]["OpenSSLBuilder"] = SCons.Builder.Builder(generator=ssl_generator, emitter=ssl_emitter) - env.AddMethod(build_openssl, "OpenSSL") diff --git a/tools/ssh2.py b/tools/ssh2.py index a5cbe60f..a67a219f 100644 --- a/tools/ssh2.py +++ b/tools/ssh2.py @@ -4,21 +4,18 @@ def build_library(env, deps): config = { "CMAKE_BUILD_TYPE": "RelWithDebInfo" if env["debug_symbols"] else "Release", - "OPENSSL_USE_STATIC_LIBS": 1, - "OPENSSL_INCLUDE_DIR": env["SSL_INCLUDE"], - "OPENSSL_SSL_LIBRARY": env["SSL_LIBRARY"].abspath, - "OPENSSL_CRYPTO_LIBRARY": env["SSL_CRYPTO_LIBRARY"].abspath, - "OPENSSL_ROOT_DIR": env["SSL_INSTALL"], + "MbedTLS_LIBRARY": env["MBEDTLS_LIBRARY"], + "MBEDCRYPTO_LIBRARY": env["MBEDTLS_CRYPTO_LIBRARY"], + "MBEDX509_LIBRARY": env["MBEDTLS_X509_LIBRARY"], + "MBEDTLS_INCLUDE_DIR": env["MBEDTLS_INCLUDE"], "BUILD_EXAMPLES": 0, "BUILD_TESTING": 0, "BUILD_SHARED_LIBS": 0, - "CRYPTO_BACKEND": "OpenSSL", + "CRYPTO_BACKEND": "mbedTLS", } if env["platform"] != "windows": config["CMAKE_C_FLAGS"] = "-fPIC" - else: - config["OPENSSL_ROOT_DIR"] = env["SSL_BUILD"] is_msvc = env.get("is_msvc", False) lib_ext = ".lib" if is_msvc else ".a" From 296b298c4e0641bb9753306c603f4fb90de3a2d5 Mon Sep 17 00:00:00 2001 From: Fabio Alessandrelli Date: Tue, 27 May 2025 19:26:43 +0200 Subject: [PATCH 2/3] Refactor build system, move src folder to top level --- .../.editorconfig => .editorconfig | 0 .github/workflows/clang-format.yml | 2 +- .gitignore | 6 +-- .gitmodules | 8 ++-- SConstruct | 38 ++++++++++--------- .../godot-git-plugin/git_plugin.gdextension | 10 ----- godot-git-plugin/cpp.hint => cpp.hint | 0 godot-git-plugin/SCsub | 38 ------------------- misc/git_plugin.gdextension | 16 ++++++++ {godot-git-plugin/src => src}/.clang-format | 0 {godot-git-plugin/src => src}/gdlibrary.cpp | 0 .../src => src}/git_callbacks.cpp | 0 {godot-git-plugin/src => src}/git_callbacks.h | 0 {godot-git-plugin/src => src}/git_plugin.cpp | 0 {godot-git-plugin/src => src}/git_plugin.h | 0 .../src => src}/git_wrappers.cpp | 0 {godot-git-plugin/src => src}/git_wrappers.h | 0 thirdparty/{git2 => }/libgit2 | 0 thirdparty/{ssh2 => }/libssh2 | 0 thirdparty/ssh2/libssh2_config.h | 0 tools/git2.py | 26 +++++++++---- tools/ssh2.py | 11 +++--- 22 files changed, 65 insertions(+), 90 deletions(-) rename godot-git-plugin/.editorconfig => .editorconfig (100%) delete mode 100644 addons/godot-git-plugin/git_plugin.gdextension rename godot-git-plugin/cpp.hint => cpp.hint (100%) delete mode 100644 godot-git-plugin/SCsub create mode 100644 misc/git_plugin.gdextension rename {godot-git-plugin/src => src}/.clang-format (100%) rename {godot-git-plugin/src => src}/gdlibrary.cpp (100%) rename {godot-git-plugin/src => src}/git_callbacks.cpp (100%) rename {godot-git-plugin/src => src}/git_callbacks.h (100%) rename {godot-git-plugin/src => src}/git_plugin.cpp (100%) rename {godot-git-plugin/src => src}/git_plugin.h (100%) rename {godot-git-plugin/src => src}/git_wrappers.cpp (100%) rename {godot-git-plugin/src => src}/git_wrappers.h (100%) rename thirdparty/{git2 => }/libgit2 (100%) rename thirdparty/{ssh2 => }/libssh2 (100%) delete mode 100644 thirdparty/ssh2/libssh2_config.h diff --git a/godot-git-plugin/.editorconfig b/.editorconfig similarity index 100% rename from godot-git-plugin/.editorconfig rename to .editorconfig diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index cd728c67..2ab44f75 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -10,6 +10,6 @@ jobs: - uses: actions/checkout@v4 - uses: DoozyX/clang-format-lint-action@v0.18.2 with: - source: "godot-git-plugin/src" + source: "src" extensions: "h,cpp" clangFormatVersion: 18 diff --git a/.gitignore b/.gitignore index f46add85..523a2409 100644 --- a/.gitignore +++ b/.gitignore @@ -19,11 +19,7 @@ extension_api.json # Binaries __pycache__/ -build/ -bin/ -macos/ -linux/ -win64/ +/bin/ *.lib *.a *.obj diff --git a/.gitmodules b/.gitmodules index e231b39f..726ab576 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,12 +2,12 @@ path = godot-cpp url = https://github.com/godotengine/godot-cpp [submodule "thirdparty/git2/libgit2"] - path = thirdparty/git2/libgit2 - url = https://github.com/libgit2/libgit2 + path = thirdparty/libgit2 + url = https://github.com/libgit2/libgit2.git ignore = untracked [submodule "thirdparty/ssh2/libssh2"] - path = thirdparty/ssh2/libssh2 - url = https://github.com/libssh2/libssh2 + path = thirdparty/libssh2 + url = https://github.com/libssh2/libssh2.git [submodule "thirdparty/mbedtls"] path = thirdparty/mbedtls url = https://github.com/Mbed-TLS/mbedtls.git diff --git a/SConstruct b/SConstruct index 4e4a6b5e..2592612f 100644 --- a/SConstruct +++ b/SConstruct @@ -2,18 +2,12 @@ import os -EnsureSConsVersion(3, 1, 2) -EnsurePythonVersion(3, 6) +EnsureSConsVersion(3, 0, 0) +EnsurePythonVersion(3, 5) opts = Variables([], ARGUMENTS) -env = Environment(ENV=os.environ) - -# Define our options -opts.Add(PathVariable("target_path", - "The path where the lib is installed.", "addons/godot-git-plugin/")) -opts.Add(PathVariable("target_name", "The library name.", - "libgit_plugin", PathVariable.PathAccept)) +env = Environment() # Updates the environment with the option variables. opts.Update(env) @@ -23,28 +17,36 @@ if ARGUMENTS.get("custom_api_file", "") != "": ARGUMENTS["target"] = "editor" env = SConscript("godot-cpp/SConstruct").Clone() -env.PrependENVPath("PATH", os.getenv("PATH")) # Prepend PATH, done upstream in recent godot-cpp verions. -# Force linking with LTO on windows MSVC, silence the linker complaining that libgit uses LTO but we are not linking with it. +# Prepend PATH to allow using custom toolchains, done upstream in recent godot-cpp verions. +env.PrependENVPath("PATH", os.getenv("PATH")) + if env["platform"] == "windows" and env.get("is_msvc", False): + # Force linking with LTO on windows MSVC, silence the linker complaining that libgit uses LTO but we are not linking with it. env.AppendUnique(LINKFLAGS=["/LTCG"]) + # Do not treat empty pdb as errors. + env.AppendUnique(LINKFLAGS=["/IGNORE:4099"]) env.Tool("cmake", toolpath=["tools"]) env.Tool("mbedtls", toolpath=["tools"]) env.Tool("ssh2", toolpath=["tools"]) env.Tool("git2", toolpath=["tools"]) -opts.Update(env) - mbedtls = env.BuildMbedTLS() ssh2 = env.BuildSSH2(mbedtls) mbedtls += ssh2 git2 = env.BuildGIT2(mbedtls) -Export("mbedtls") -Export("env") +env.Append(CPPPATH=[".", "src/"]) +env.Append(CPPPATH=["#thirdparty/git2/libgit2/include/"]) + +lib_sources = Glob("src/*.cpp") +env.Depends(lib_sources, mbedtls) -SConscript("godot-git-plugin/SCsub") +library = env.SharedLibrary( + target="bin/addons/godot-git-plugin/lib/libgit_plugin{}{}".format(env["suffix"], env["SHLIBSUFFIX"]), + source=lib_sources +) +extension = env.InstallAs("bin/addons/godot-git-plugin/git_plugin.gdextension", "misc/git_plugin.gdextension") -# Generates help for the -h scons option. -Help(opts.GenerateHelpText(env)) +Default(library + extension) diff --git a/addons/godot-git-plugin/git_plugin.gdextension b/addons/godot-git-plugin/git_plugin.gdextension deleted file mode 100644 index 62cf890a..00000000 --- a/addons/godot-git-plugin/git_plugin.gdextension +++ /dev/null @@ -1,10 +0,0 @@ -[configuration] - -entry_symbol = "git_plugin_init" -compatibility_minimum = "4.2.0" - -[libraries] - -linux.editor.x86_64 = "linux/libgit_plugin.linux.editor.x86_64.so" -macos.editor = "macos/libgit_plugin.macos.editor.universal.dylib" -windows.editor.x86_64 = "windows/libgit_plugin.windows.editor.x86_64.dll" diff --git a/godot-git-plugin/cpp.hint b/cpp.hint similarity index 100% rename from godot-git-plugin/cpp.hint rename to cpp.hint diff --git a/godot-git-plugin/SCsub b/godot-git-plugin/SCsub deleted file mode 100644 index f924ca66..00000000 --- a/godot-git-plugin/SCsub +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python - -import os - -env = {} -Import("env") -Import("mbedtls") - -env["target_path"] = "../" + env["target_path"] - -if not os.path.isdir(env["target_path"]): - os.mkdir(env["target_path"]) - -# Check our platform specifics -env["target_path"] += env["platform"] + "/" - -if env["platform"] == "macos": - if env["macos_deployment_target"] != "default": - env.Append(CCFLAGS=["-mmacosx-version-min=" + - env["macos_deployment_target"]]) - env.Append(LINKFLAGS=["-mmacosx-version-min=" + - env["macos_deployment_target"]]) -elif env["platform"] == "windows": - env.Append(LIBS=["advapi32", "winhttp", "rpcrt4", "crypt32", - "ole32", "user32", "wsock32", "ws2_32", "bcrypt"]) - -env.Append(CPPPATH=[".", "src/"]) - -env.Append(CPPPATH=["#thirdparty/git2/libgit2/include/"]) - -lib_sources = Glob("src/*.cpp") -env.Depends(lib_sources, mbedtls) -library = env.SharedLibrary( - target=env["target_path"] + - "{}{}{}".format(env["target_name"], env["suffix"], env["SHLIBSUFFIX"]), - source=lib_sources -) -Default(library) diff --git a/misc/git_plugin.gdextension b/misc/git_plugin.gdextension new file mode 100644 index 00000000..949a21a8 --- /dev/null +++ b/misc/git_plugin.gdextension @@ -0,0 +1,16 @@ +[configuration] + +entry_symbol = "git_plugin_init" +compatibility_minimum = "4.1.0" + +[libraries] + +macos.editor = "lib/libgit_plugin.macos.editor.universal.dylib" +windows.editor.x86_32 = "lib/libgit_plugin.windows.editor.x86_32.dll" +windows.editor.x86_64 = "lib/libgit_plugin.windows.editor.x86_64.dll" +windows.editor.arm64 = "lib/libgit_plugin.windows.editor.arm64.dll" +linux.editor.x86_32 = "lib/libgit_plugin.linux.editor.x86_32.so" +linux.editor.x86_64 = "lib/libgit_plugin.linux.editor.x86_64.so" +linux.editor.arm32 = "lib/libgit_plugin.linux.editor.arm32.so" +linux.editor.arm64 = "lib/libgit_plugin.linux.editor.arm64.so" +linux.editor.rv64 = "" diff --git a/godot-git-plugin/src/.clang-format b/src/.clang-format similarity index 100% rename from godot-git-plugin/src/.clang-format rename to src/.clang-format diff --git a/godot-git-plugin/src/gdlibrary.cpp b/src/gdlibrary.cpp similarity index 100% rename from godot-git-plugin/src/gdlibrary.cpp rename to src/gdlibrary.cpp diff --git a/godot-git-plugin/src/git_callbacks.cpp b/src/git_callbacks.cpp similarity index 100% rename from godot-git-plugin/src/git_callbacks.cpp rename to src/git_callbacks.cpp diff --git a/godot-git-plugin/src/git_callbacks.h b/src/git_callbacks.h similarity index 100% rename from godot-git-plugin/src/git_callbacks.h rename to src/git_callbacks.h diff --git a/godot-git-plugin/src/git_plugin.cpp b/src/git_plugin.cpp similarity index 100% rename from godot-git-plugin/src/git_plugin.cpp rename to src/git_plugin.cpp diff --git a/godot-git-plugin/src/git_plugin.h b/src/git_plugin.h similarity index 100% rename from godot-git-plugin/src/git_plugin.h rename to src/git_plugin.h diff --git a/godot-git-plugin/src/git_wrappers.cpp b/src/git_wrappers.cpp similarity index 100% rename from godot-git-plugin/src/git_wrappers.cpp rename to src/git_wrappers.cpp diff --git a/godot-git-plugin/src/git_wrappers.h b/src/git_wrappers.h similarity index 100% rename from godot-git-plugin/src/git_wrappers.h rename to src/git_wrappers.h diff --git a/thirdparty/git2/libgit2 b/thirdparty/libgit2 similarity index 100% rename from thirdparty/git2/libgit2 rename to thirdparty/libgit2 diff --git a/thirdparty/ssh2/libssh2 b/thirdparty/libssh2 similarity index 100% rename from thirdparty/ssh2/libssh2 rename to thirdparty/libssh2 diff --git a/thirdparty/ssh2/libssh2_config.h b/thirdparty/ssh2/libssh2_config.h deleted file mode 100644 index e69de29b..00000000 diff --git a/tools/git2.py b/tools/git2.py index 52de8f40..0cb3e928 100644 --- a/tools/git2.py +++ b/tools/git2.py @@ -23,7 +23,7 @@ def build_library(env, deps): "USE_SHA1": "mbedTLS", "BUILD_SHARED_LIBS": 0, "LINK_WITH_STATIC_LIBRARIES": 1, - "LIBSSH2_INCLUDE_DIR": env.Dir("#thirdparty/ssh2/libssh2/include").abspath, + "LIBSSH2_INCLUDE_DIR": env.Dir("#thirdparty/libssh2/include").abspath, "LIBSSH2_LIBRARY": deps[-1], "USE_WINHTTP": 0, "STATIC_CRT": env.get("use_static_cpp", True), @@ -31,28 +31,38 @@ def build_library(env, deps): if env["platform"] != "windows": config["CMAKE_C_FLAGS"] = "-fPIC" + elif env.get("is_msvc", False): + # For some reasons, libgit2 doesn't respect the linker flags + link_arch = { + "x86_64": "X64", + "x86_32": "X86", + "arm64": "ARM64", + }[env["arch"]] + #config["CMAKE_STATIC_LIBRARY_OPTIONS"] = "/MACHINE:" + link_arch + #config["CMAKE_STATIC_LIBRARY_FLAGS"] = "/MACHINE:" + link_arch + #config["CMAKE_STATIC_LINKER_FLAGS"] = "/MACHINE:" + link_arch + #config["CMAKE_MODULE_LINKER_FLAGS"] = "/MACHINE:" + link_arch + #config["CMAKE_LINK_OPTIONS"] = "/MACHINE:" + link_arch + #config["CMAKE_LINK_FLAGS"] = "/MACHINE:" + link_arch is_msvc = env.get("is_msvc", False) lib_ext = ".lib" if is_msvc else ".a" lib_prefix = "" if is_msvc else "lib" libs = ["{}git2{}".format(lib_prefix, lib_ext)] - source = env.Dir("#thirdparty/git2/libgit2").abspath - target = env.Dir("#bin/thirdparty/libgit2").abspath - git2 = env.CMakeBuild( - "#bin/thirdparty/git2/", - "#thirdparty/git2/libgit2", + env.Dir("bin/thirdparty/libgit2/"), + env.Dir("thirdparty/libgit2"), cmake_options=config, cmake_outputs=libs, cmake_targets=[], dependencies=deps, ) - env.Append(CPPPATH=["#thirdparty/git2/libgit2/include"]) + env.Append(CPPPATH=[env.Dir("thirdparty/libgit2/include")]) env.Prepend(LIBS=git2[1:]) if env["platform"] == "windows": - env.PrependUnique(LIBS=["secur32"]) + env.PrependUnique(LIBS=["secur32", "advapi32"]) return git2 diff --git a/tools/ssh2.py b/tools/ssh2.py index a67a219f..e71122ca 100644 --- a/tools/ssh2.py +++ b/tools/ssh2.py @@ -21,20 +21,19 @@ def build_library(env, deps): lib_ext = ".lib" if is_msvc else ".a" libs = ["src/libssh2{}".format(lib_ext)] - source = env.Dir("#thirdparty/ssh2/libssh2").abspath - target = env.Dir("#bin/thirdparty/libssh2").abspath - ssh2 = env.CMakeBuild( - "#bin/thirdparty/ssh2/", - "#thirdparty/ssh2/libssh2", + env.Dir("bin/thirdparty/libssh2/"), + env.Dir("thirdparty/libssh2"), cmake_options=config, cmake_outputs=libs, cmake_targets=[], dependencies=deps, ) - env.Append(CPPPATH=["#thirdparty/ssh2/libssh2/include"]) + env.Append(CPPPATH=[env.Dir("thirdparty/libssh2/include")]) env.Prepend(LIBS=ssh2[1:]) + if env["platform"] == "windows": + env.PrependUnique(LIBS=["user32"]) return ssh2 From 2fc893b0bcc0c259e430231f6bd58b3bcbeea144 Mon Sep 17 00:00:00 2001 From: Fabio Alessandrelli Date: Mon, 26 May 2025 19:52:51 +0200 Subject: [PATCH 3/3] CI: Refactor, android, more windows/linux targets Android: - arm64 - x86_64 Linux: - x86_64 - x86_32 - arm64 - arm32 MacOS: - universal (arm64 + x86_64) Windows: - x64_64 - x86_32 - arm64 --- .github/workflows/build.yml | 94 ------- .github/workflows/checks_build_release.yml | 216 +++++++++++++++ .github/workflows/clang-format.yml | 15 -- build_profile.json | 6 + misc/patches/build_profile.diff | 297 +++++++++++++++++++++ misc/patches/common_compiler_flags.py | 123 +++++++++ misc/patches/libgit_linker_flags.diff | 13 + misc/patches/windows.py | 221 +++++++++++++++ misc/scripts/package_release.sh | 23 ++ 9 files changed, 899 insertions(+), 109 deletions(-) delete mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/checks_build_release.yml delete mode 100644 .github/workflows/clang-format.yml create mode 100644 build_profile.json create mode 100644 misc/patches/build_profile.diff create mode 100644 misc/patches/common_compiler_flags.py create mode 100644 misc/patches/libgit_linker_flags.diff create mode 100644 misc/patches/windows.py create mode 100755 misc/scripts/package_release.sh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 8388febd..00000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,94 +0,0 @@ -name: C/C++ CI - -on: [push, pull_request] - -env: - # Only used for the cache key. Increment version to force clean build. - GODOT_BASE_BRANCH: master - SCONS_CACHE: ${{ github.workspace }}/.scons-cache/ - -jobs: - linux-x64: - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v4 - with: - submodules: recursive - - name: Setup Godot build cache - uses: ./godot-cpp/.github/actions/godot-cache - with: - cache-name: linux-x86_64 - continue-on-error: true - - name: Build for Linux editor x86_64 - run: | - pip3 install scons - scons platform=linux arch=x86_64 target=editor generate_bindings=yes - ldd addons/godot-git-plugin/linux/*.so - - name: Prepare artifact - run: | - mkdir out - mv addons out/ - - uses: actions/upload-artifact@v4 - with: - name: libgit_plugin.linux.x86_64.editor.so-${{ github.sha }} - if-no-files-found: error - path: | - out/ - - windows-x64: - runs-on: windows-2022 - steps: - - uses: actions/checkout@v4 - with: - submodules: recursive - - name: Setup Godot build cache - uses: ./godot-cpp/.github/actions/godot-cache - with: - cache-name: windows-x86_64 - - uses: ilammy/msvc-dev-cmd@v1 # For dumpbin. - - uses: ilammy/setup-nasm@v1 - - name: Build for Windows editor x86_64 - shell: powershell - run: | - pip3 install scons - scons platform=windows arch=x86_64 target=editor generate_bindings=yes - dumpbin /dependents .\addons\godot-git-plugin\windows\*.dll - - name: Prepare artifact - shell: bash - run: | - # Not needed to use the plugin. - rm -f addons/godot-git-plugin/windows/*.{exp,lib} - mkdir out - mv addons out/ - - uses: actions/upload-artifact@v4 - with: - name: libgit_plugin.windows.x86_64.editor.dll-${{ github.sha }} - if-no-files-found: error - path: | - out/ - - macos-universal: - runs-on: macos-15 - steps: - - uses: actions/checkout@v4 - with: - submodules: recursive - - name: Setup Godot build cache - uses: ./godot-cpp/.github/actions/godot-cache - with: - cache-name: macos-universal - - name: Build for macOS editor universal - run: | - pip install scons - scons platform=macos arch=universal target=editor generate_bindings=yes macos_deployment_target=10.13 - otool -L addons/godot-git-plugin/macos/*.dylib - - name: Prepare artifact - run: | - mkdir out - mv addons out/ - - uses: actions/upload-artifact@v4 - with: - name: libgit_plugin.macos.universal.editor.dylib-${{ github.sha }} - if-no-files-found: error - path: | - out/ diff --git a/.github/workflows/checks_build_release.yml b/.github/workflows/checks_build_release.yml new file mode 100644 index 00000000..c0597878 --- /dev/null +++ b/.github/workflows/checks_build_release.yml @@ -0,0 +1,216 @@ +name: 📊 Static Checks -> 🔧 Build -> 📦 Package +on: [push, pull_request] + +env: + # Only used for the cache key. Increment version to force clean build. + GODOT_BASE_BRANCH: master + SCONS_CACHE: ${{ github.workspace }}/.scons-cache/ + ANDROID_SDK_ROOT: /usr/local/lib/android/sdk/ + +jobs: + static-checks: + name: 📊 Static Checks + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - uses: DoozyX/clang-format-lint-action@v0.18.2 + with: + source: "src" + extensions: "h,cpp" + clangFormatVersion: 18 + + build: + runs-on: ${{ matrix.os }} + name: 🔧 Build + needs: static-checks + strategy: + fail-fast: false + matrix: + include: + # Anrdoid + - platform: android + arch: 'x86_64' + sconsflags: '' + os: 'ubuntu-22.04' + cache-name: android-x86_64 + - platform: android + arch: 'arm64' + sconsflags: '' + os: 'ubuntu-22.04' + cache-name: android-arm64 + + # Linux + - platform: linux + arch: x86_32 + buildroot: i686-godot-linux-gnu_sdk-buildroot + sconsflags: "" + os: ubuntu-22.04 + cache-name: linux-x86_32 + - platform: linux + arch: x86_64 + buildroot: x86_64-godot-linux-gnu_sdk-buildroot + sconsflags: "" + os: ubuntu-22.04 + cache-name: linux-x86_64 + - platform: linux + arch: arm32 + buildroot: arm-godot-linux-gnueabihf_sdk-buildroot + sconsflags: "" + os: ubuntu-22.04 + cache-name: linux-arm32 + - platform: linux + arch: arm64 + buildroot: aarch64-godot-linux-gnu_sdk-buildroot + sconsflags: "" + os: ubuntu-22.04 + cache-name: linux-arm64 + + # macOS + - platform: macos + arch: universal + sconsflags: "" + os: macos-latest + cache-name: macos-universal + + # Windows + - platform: windows + arch: x86_32 + msvc-arch: x86 + sconsflags: "" + os: windows-latest + cache-name: win-x86_32 + - platform: windows + arch: x86_64 + msvc-arch: x64 + sconsflags: "" + os: windows-latest + cache-name: win-x86_64 + - platform: windows + arch: arm64 + msvc-arch: amd64_arm64 + sconsflags: "" + os: windows-latest + cache-name: win-arm64 + + env: + SCONSFLAGS: ${{ matrix.sconsflags }} platform=${{ matrix.platform }} arch=${{ matrix.arch }} debug_symbols=no + + defaults: + run: + shell: bash + + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Setup Godot build cache + uses: ./godot-cpp/.github/actions/godot-cache + with: + cache-name: ${{ matrix.cache-name }} + continue-on-error: true + + - name: Install Linux build dependencies + if: ${{ matrix.platform == 'linux' }} + run: | + sudo apt-get update + sudo apt-get install build-essential gcc-multilib g++-multilib wget + + - name: Setup Linux buildroot toolchain cache + if: ${{ matrix.platform == 'linux' }} + uses: actions/cache@v4 + with: + path: | + ${{ matrix.buildroot }}.tar.bz2 + key: linux-${{ matrix.buildroot }}-buildroot + + - name: Setup Android dependencies + if: ${{ matrix.platform == 'android' }} + uses: nttld/setup-ndk@v1 + with: + ndk-version: r23c + link-to-sdk: true + + - name: Set up Python 3.x + uses: actions/setup-python@v5 + with: + python-version: 3.x + architecture: x64 + + - name: Configuring Python packages + run: | + python -c "import sys; print(sys.version)" + python -m pip install scons + + - name: Apply build profile patches + run: | + patch -p1 < misc/patches/build_profile.diff + + - name: Setup Linux toolchains + if: ${{ matrix.platform == 'linux' }} + run: | + if [ ! -f ${{ matrix.buildroot }}.tar.bz2 ]; then + wget https://github.com/godotengine/buildroot/releases/download/godot-2023.08.x-4/${{ matrix.buildroot }}.tar.bz2 + fi + tar -xjf ${{ matrix.buildroot }}.tar.bz2 + ${{ matrix.buildroot }}/relocate-sdk.sh + rm ${{ matrix.buildroot }}/bin/cmake + echo "$GITHUB_WORKSPACE/${{ matrix.buildroot }}/bin" >> $GITHUB_PATH + echo "PKG_CONFIG=$GITHUB_WORKSPACE/${{ matrix.buildroot }}/share/pkgconfig/" >> $GITHUB_ENV + + - name: Use updated windows tools, patch libgit2. + if: ${{ matrix.platform == 'windows' }} + run: | + rm /usr/bin/link # GNU linkker conflicts with MSVC linkes + patch -p1 < misc/patches/libgit_linker_flags.diff + cp misc/patches/windows.py godot-cpp/tools/windows.py + cp misc/patches/common_compiler_flags.py godot-cpp/tools/common_compiler_flags.py + + - name: Print tools versions + run: | + python --version + scons --version + cmake --version + + - name: Compile Extension - editor - ${{ matrix.platform }} - ${{ matrix.arch }} + run: | + scons target=editor build_profile=build_profile.json + + - uses: actions/upload-artifact@v4 + with: + name: ${{ github.job }}-${{ matrix.platform }}-${{ matrix.arch }} + path: | + bin/addons/godot-git-plugin/ + + package: + name: 📦 Package + needs: build + runs-on: "ubuntu-latest" + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - uses: actions/download-artifact@v4 + with: + path: artifacts + + - name: Bundle licenses. + run: | + cp LICENSE artifacts/LICENSE.godot-git-plugin + cp thirdparty/libgit2/COPYING artifacts/LICENSE.libgit2 + cp thirdparty/libssh2/COPYING artifacts/LICENSE.libssh2 + cp thirdparty/mbedtls/LICENSE artifacts/LICENSE.mbedtls + + - name: Package artifacts for release + env: + DESTINATION: "release" + run: | + mkdir release + ./misc/scripts/package_release.sh + ls -R release + + - uses: actions/upload-artifact@v4 + with: + name: godot-git-plugin + path: release diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml deleted file mode 100644 index 2ab44f75..00000000 --- a/.github/workflows/clang-format.yml +++ /dev/null @@ -1,15 +0,0 @@ -name: clang-format - -on: [push, pull_request] - -jobs: - clang-format: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - uses: DoozyX/clang-format-lint-action@v0.18.2 - with: - source: "src" - extensions: "h,cpp" - clangFormatVersion: 18 diff --git a/build_profile.json b/build_profile.json new file mode 100644 index 00000000..e54a8367 --- /dev/null +++ b/build_profile.json @@ -0,0 +1,6 @@ +{ + "enabled_classes": [ + "EditorVCSInterface", + "OS" + ] +} diff --git a/misc/patches/build_profile.diff b/misc/patches/build_profile.diff new file mode 100644 index 00000000..fedbbee0 --- /dev/null +++ b/misc/patches/build_profile.diff @@ -0,0 +1,297 @@ +diff --git a/godot-cpp/binding_generator.py b/godot-cpp/binding_generator.py +index 478dafe..ca0ee75 100644 +--- a/godot-cpp/binding_generator.py ++++ b/godot-cpp/binding_generator.py +@@ -72,9 +72,13 @@ def generate_wrappers(target): + + def get_file_list(api_filepath, output_dir, headers=False, sources=False): + api = {} +- files = [] + with open(api_filepath, encoding="utf-8") as api_file: + api = json.load(api_file) ++ return _get_file_list(api, output_dir, headers, sources) ++ ++ ++def _get_file_list(api, output_dir, headers=False, sources=False): ++ files = [] + + core_gen_folder = Path(output_dir) / "gen" / "include" / "godot_cpp" / "core" + include_gen_folder = Path(output_dir) / "gen" / "include" / "godot_cpp" +@@ -159,13 +163,15 @@ def scons_generate_bindings(target, source, env): + return None + + +-def generate_bindings(api_filepath, use_template_get_node, bits="64", precision="single", output_dir="."): +- api = None +- +- target_dir = Path(output_dir) / "gen" +- ++def _generate_bindings(api_filepath, use_template_get_node, bits="64", precision="single", output_dir="."): ++ api = {} + with open(api_filepath, encoding="utf-8") as api_file: + api = json.load(api_file) ++ _generate_bindings(api, use_template_get_node, bits, precision, output_dir) ++ ++ ++def _generate_bindings(api, use_template_get_node, bits="64", precision="single", output_dir="."): ++ target_dir = Path(output_dir) / "gen" + + shutil.rmtree(target_dir, ignore_errors=True) + target_dir.mkdir(parents=True) +diff --git a/godot-cpp/build_profile.py b/godot-cpp/build_profile.py +new file mode 100644 +index 0000000..b4d19de +--- /dev/null ++++ b/godot-cpp/build_profile.py +@@ -0,0 +1,183 @@ ++import json ++import sys ++ ++ ++def parse_build_profile(profile_filepath, api): ++ if profile_filepath == "": ++ return {} ++ ++ with open(profile_filepath, encoding="utf-8") as profile_file: ++ profile = json.load(profile_file) ++ ++ api_dict = {} ++ parents = {} ++ children = {} ++ for engine_class in api["classes"]: ++ api_dict[engine_class["name"]] = engine_class ++ parent = engine_class.get("inherits", "") ++ child = engine_class["name"] ++ parents[child] = parent ++ if parent == "": ++ continue ++ children[parent] = children.get(parent, []) ++ children[parent].append(child) ++ ++ included = [] ++ front = list(profile.get("enabled_classes", [])) ++ if front: ++ # These must always be included ++ front.append("WorkerThreadPool") ++ front.append("ClassDB") ++ front.append("ClassDBSingleton") ++ # In src/classes/low_level.cpp ++ front.append("FileAccess") ++ front.append("Image") ++ front.append("XMLParser") ++ # In include/godot_cpp/templates/thread_work_pool.hpp ++ front.append("Semaphore") ++ while front: ++ cls = front.pop() ++ if cls in included: ++ continue ++ included.append(cls) ++ parent = parents.get(cls, "") ++ if parent: ++ front.append(parent) ++ ++ excluded = [] ++ front = list(profile.get("disabled_classes", [])) ++ while front: ++ cls = front.pop() ++ if cls in excluded: ++ continue ++ excluded.append(cls) ++ front += children.get(cls, []) ++ ++ if included and excluded: ++ print( ++ "WARNING: Cannot specify both 'enabled_classes' and 'disabled_classes' in build profile. 'disabled_classes' will be ignored." ++ ) ++ ++ return { ++ "enabled_classes": included, ++ "disabled_classes": excluded, ++ } ++ ++ ++def generate_trimmed_api(source_api_filepath, profile_filepath): ++ with open(source_api_filepath, encoding="utf-8") as api_file: ++ api = json.load(api_file) ++ ++ if profile_filepath == "": ++ return api ++ ++ build_profile = parse_build_profile(profile_filepath, api) ++ ++ engine_classes = {} ++ for class_api in api["classes"]: ++ engine_classes[class_api["name"]] = class_api["is_refcounted"] ++ for native_struct in api["native_structures"]: ++ if native_struct["name"] == "ObjectID": ++ continue ++ engine_classes[native_struct["name"]] = False ++ ++ classes = [] ++ for class_api in api["classes"]: ++ if not is_class_included(class_api["name"], build_profile): ++ continue ++ if "methods" in class_api: ++ methods = [] ++ for method in class_api["methods"]: ++ if not is_method_included(method, build_profile, engine_classes): ++ continue ++ methods.append(method) ++ class_api["methods"] = methods ++ classes.append(class_api) ++ api["classes"] = classes ++ ++ return api ++ ++ ++def is_class_included(class_name, build_profile): ++ """ ++ Check if an engine class should be included. ++ This removes classes according to a build profile of enabled or disabled classes. ++ """ ++ included = build_profile.get("enabled_classes", []) ++ excluded = build_profile.get("disabled_classes", []) ++ if included: ++ return class_name in included ++ if excluded: ++ return class_name not in excluded ++ return True ++ ++ ++def is_method_included(method, build_profile, engine_classes): ++ """ ++ Check if an engine class method should be included. ++ This removes methods according to a build profile of enabled or disabled classes. ++ """ ++ included = build_profile.get("enabled_classes", []) ++ excluded = build_profile.get("disabled_classes", []) ++ ref_cls = set() ++ rtype = get_base_type(method.get("return_value", {}).get("type", "")) ++ args = [get_base_type(a["type"]) for a in method.get("arguments", [])] ++ if rtype in engine_classes: ++ ref_cls.add(rtype) ++ elif is_enum(rtype) and get_enum_class(rtype) in engine_classes: ++ ref_cls.add(get_enum_class(rtype)) ++ for arg in args: ++ if arg in engine_classes: ++ ref_cls.add(arg) ++ elif is_enum(arg) and get_enum_class(arg) in engine_classes: ++ ref_cls.add(get_enum_class(arg)) ++ for acls in ref_cls: ++ if len(included) > 0 and acls not in included: ++ return False ++ elif len(excluded) > 0 and acls in excluded: ++ return False ++ return True ++ ++ ++def is_enum(type_name): ++ return type_name.startswith("enum::") or type_name.startswith("bitfield::") ++ ++ ++def get_enum_class(enum_name: str): ++ if "." in enum_name: ++ if is_bitfield(enum_name): ++ return enum_name.replace("bitfield::", "").split(".")[0] ++ else: ++ return enum_name.replace("enum::", "").split(".")[0] ++ else: ++ return "GlobalConstants" ++ ++ ++def get_base_type(type_name): ++ if type_name.startswith("const "): ++ type_name = type_name[6:] ++ if type_name.endswith("*"): ++ type_name = type_name[:-1] ++ if type_name.startswith("typedarray::"): ++ type_name = type_name.replace("typedarray::", "") ++ return type_name ++ ++ ++def is_bitfield(type_name): ++ return type_name.startswith("bitfield::") ++ ++ ++if __name__ == "__main__": ++ if len(sys.argv) < 3 or len(sys.argv) > 4: ++ print("Usage: %s BUILD_PROFILE INPUT_JSON [OUTPUT_JSON]" % (sys.argv[0])) ++ sys.exit(1) ++ profile = sys.argv[1] ++ infile = sys.argv[2] ++ outfile = sys.argv[3] if len(sys.argv) > 3 else "" ++ api = generate_trimmed_api(infile, profile) ++ ++ if outfile: ++ with open(outfile, "w", encoding="utf-8") as f: ++ json.dump(api, f) ++ else: ++ json.dump(api, sys.stdout) +diff --git a/godot-cpp/tools/godotcpp.py b/godot-cpp/tools/godotcpp.py +index e445cad..c7ae0b5 100644 +--- a/godot-cpp/tools/godotcpp.py ++++ b/godot-cpp/tools/godotcpp.py +@@ -5,7 +5,8 @@ from SCons.Tool import Tool + from SCons.Builder import Builder + from SCons.Errors import UserError + +-from binding_generator import scons_generate_bindings, scons_emit_files ++from binding_generator import _generate_bindings, _get_file_list, get_file_list ++from build_profile import generate_trimmed_api + + + def add_sources(sources, dir, extension): +@@ -14,6 +15,37 @@ def add_sources(sources, dir, extension): + sources.append(dir + "/" + f) + + ++def scons_emit_files(target, source, env): ++ profile_filepath = env.get("build_profile", "") ++ if profile_filepath: ++ profile_filepath = normalize_path(profile_filepath, env) ++ ++ # Always clean all files ++ env.Clean(target, [env.File(f) for f in get_file_list(str(source[0]), target[0].abspath, True, True)]) ++ ++ api = generate_trimmed_api(str(source[0]), profile_filepath) ++ files = [env.File(f) for f in _get_file_list(api, target[0].abspath, True, True)] ++ env["godot_cpp_gen_dir"] = target[0].abspath ++ return files, source ++ ++ ++def scons_generate_bindings(target, source, env): ++ profile_filepath = env.get("build_profile", "") ++ if profile_filepath: ++ profile_filepath = normalize_path(profile_filepath, env) ++ ++ api = generate_trimmed_api(str(source[0]), profile_filepath) ++ ++ _generate_bindings( ++ api, ++ env["generate_template_get_node"], ++ "32" if "32" in env["arch"] else "64", ++ env["precision"], ++ env["godot_cpp_gen_dir"], ++ ) ++ return None ++ ++ + def normalize_path(val, env): + return val if os.path.isabs(val) else os.path.join(env.Dir("#").abspath, val) + +@@ -175,6 +207,15 @@ def options(opts, env): + ) + ) + ++ opts.Add( ++ PathVariable( ++ "build_profile", ++ "Path to a file containing a feature build profile", ++ default=env.get("build_profile", None), ++ validator=validate_file, ++ ) ++ ) ++ + opts.Add( + BoolVariable( + key="use_hot_reload", diff --git a/misc/patches/common_compiler_flags.py b/misc/patches/common_compiler_flags.py new file mode 100644 index 00000000..e645f390 --- /dev/null +++ b/misc/patches/common_compiler_flags.py @@ -0,0 +1,123 @@ +import os +import subprocess + + +def using_clang(env): + return "clang" in os.path.basename(env["CC"]) + + +def is_vanilla_clang(env): + if not using_clang(env): + return False + try: + version = subprocess.check_output([env.subst(env["CXX"]), "--version"]).strip().decode("utf-8") + except (subprocess.CalledProcessError, OSError): + print("Couldn't parse CXX environment variable to infer compiler version.") + return False + return not version.startswith("Apple") + + +def exists(env): + return True + + +def generate(env): + assert env["lto"] in ["thin", "full", "none"], "Unrecognized lto: {}".format(env["lto"]) + if env["lto"] != "none": + print("Using LTO: " + env["lto"]) + + # Require C++17 + if env.get("is_msvc", False): + env.Append(CXXFLAGS=["/std:c++17"]) + else: + env.Append(CXXFLAGS=["-std=c++17"]) + + # Disable exception handling. Godot doesn't use exceptions anywhere, and this + # saves around 20% of binary size and very significant build time. + if env["disable_exceptions"]: + if env.get("is_msvc", False): + env.Append(CPPDEFINES=[("_HAS_EXCEPTIONS", 0)]) + else: + env.Append(CXXFLAGS=["-fno-exceptions"]) + elif env.get("is_msvc", False): + env.Append(CXXFLAGS=["/EHsc"]) + + if not env.get("is_msvc", False): + if env["symbols_visibility"] == "visible": + env.Append(CCFLAGS=["-fvisibility=default"]) + env.Append(LINKFLAGS=["-fvisibility=default"]) + elif env["symbols_visibility"] == "hidden": + env.Append(CCFLAGS=["-fvisibility=hidden"]) + env.Append(LINKFLAGS=["-fvisibility=hidden"]) + + # Set optimize and debug_symbols flags. + # "custom" means do nothing and let users set their own optimization flags. + if env.get("is_msvc", False): + if env["debug_symbols"]: + env.Append(CCFLAGS=["/Zi", "/FS"]) + env.Append(LINKFLAGS=["/DEBUG:FULL"]) + + if env["optimize"] == "speed": + env.Append(CCFLAGS=["/O2"]) + env.Append(LINKFLAGS=["/OPT:REF"]) + elif env["optimize"] == "speed_trace": + env.Append(CCFLAGS=["/O2"]) + env.Append(LINKFLAGS=["/OPT:REF", "/OPT:NOICF"]) + elif env["optimize"] == "size": + env.Append(CCFLAGS=["/O1"]) + env.Append(LINKFLAGS=["/OPT:REF"]) + elif env["optimize"] == "debug" or env["optimize"] == "none": + env.Append(CCFLAGS=["/Od"]) + + if env["lto"] == "thin": + if not env["use_llvm"]: + print("ThinLTO is only compatible with LLVM, use `use_llvm=yes` or `lto=full`.") + env.Exit(255) + + env.Append(CCFLAGS=["-flto=thin"]) + env.Append(LINKFLAGS=["-flto=thin"]) + elif env["lto"] == "full": + if env["use_llvm"]: + env.Append(CCFLAGS=["-flto"]) + env.Append(LINKFLAGS=["-flto"]) + else: + env.AppendUnique(CCFLAGS=["/GL"]) + env.AppendUnique(ARFLAGS=["/LTCG"]) + env.AppendUnique(LINKFLAGS=["/LTCG"]) + else: + if env["debug_symbols"]: + # Adding dwarf-4 explicitly makes stacktraces work with clang builds, + # otherwise addr2line doesn't understand them. + env.Append(CCFLAGS=["-gdwarf-4"]) + if env.dev_build: + env.Append(CCFLAGS=["-g3"]) + else: + env.Append(CCFLAGS=["-g2"]) + else: + if using_clang(env) and not is_vanilla_clang(env) and not env["use_mingw"]: + # Apple Clang, its linker doesn't like -s. + env.Append(LINKFLAGS=["-Wl,-S", "-Wl,-x", "-Wl,-dead_strip"]) + else: + env.Append(LINKFLAGS=["-s"]) + + if env["optimize"] == "speed": + env.Append(CCFLAGS=["-O3"]) + # `-O2` is friendlier to debuggers than `-O3`, leading to better crash backtraces. + elif env["optimize"] == "speed_trace": + env.Append(CCFLAGS=["-O2"]) + elif env["optimize"] == "size": + env.Append(CCFLAGS=["-Os"]) + elif env["optimize"] == "debug": + env.Append(CCFLAGS=["-Og"]) + elif env["optimize"] == "none": + env.Append(CCFLAGS=["-O0"]) + + if env["lto"] == "thin": + if (env["platform"] == "windows" or env["platform"] == "linux") and not env["use_llvm"]: + print("ThinLTO is only compatible with LLVM, use `use_llvm=yes` or `lto=full`.") + env.Exit(255) + env.Append(CCFLAGS=["-flto=thin"]) + env.Append(LINKFLAGS=["-flto=thin"]) + elif env["lto"] == "full": + env.Append(CCFLAGS=["-flto"]) + env.Append(LINKFLAGS=["-flto"]) diff --git a/misc/patches/libgit_linker_flags.diff b/misc/patches/libgit_linker_flags.diff new file mode 100644 index 00000000..8c24e2de --- /dev/null +++ b/misc/patches/libgit_linker_flags.diff @@ -0,0 +1,13 @@ +diff --git a/thirdparty/libgit2/cmake/DefaultCFlags.cmake b/thirdparty/libgit2/cmake/DefaultCFlags.cmake +index a9c9ab972..5ba3466fc 100644 +--- a/thirdparty/libgit2/cmake/DefaultCFlags.cmake ++++ b/thirdparty/libgit2/cmake/DefaultCFlags.cmake +@@ -56,7 +56,7 @@ if(MSVC) + set(CMAKE_C_FLAGS_MINSIZEREL "/DNDEBUG /O1 /Oy /GL /Gy ${CRT_FLAG_RELEASE}") + + # /IGNORE:4221 - Ignore empty compilation units +- set(CMAKE_STATIC_LINKER_FLAGS "/IGNORE:4221") ++ set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /IGNORE:4221") + + # /DYNAMICBASE - Address space load randomization (ASLR) + # /NXCOMPAT - Data execution prevention (DEP) diff --git a/misc/patches/windows.py b/misc/patches/windows.py new file mode 100644 index 00000000..a5f2105d --- /dev/null +++ b/misc/patches/windows.py @@ -0,0 +1,221 @@ +import os +import sys + +import common_compiler_flags +import my_spawn +from SCons.Tool import mingw, msvc +from SCons.Variables import BoolVariable + + +def silence_msvc(env): + import os + import re + import tempfile + + # Ensure we have a location to write captured output to, in case of false positives. + capture_path = os.path.join(os.path.dirname(__file__), "..", "msvc_capture.log") + with open(capture_path, "wt", encoding="utf-8"): + pass + + old_spawn = env["SPAWN"] + re_redirect_stream = re.compile(r"^[12]?>") + re_cl_capture = re.compile(r"^.+\.(c|cc|cpp|cxx|c[+]{2})$", re.IGNORECASE) + re_link_capture = re.compile(r'\s{3}\S.+\s(?:"[^"]+.lib"|\S+.lib)\s.+\s(?:"[^"]+.exp"|\S+.exp)') + + def spawn_capture(sh, escape, cmd, args, env): + # We only care about cl/link, process everything else as normal. + if args[0] not in ["cl", "link"]: + return old_spawn(sh, escape, cmd, args, env) + + # Process as normal if the user is manually rerouting output. + for arg in args: + if re_redirect_stream.match(arg): + return old_spawn(sh, escape, cmd, args, env) + + tmp_stdout, tmp_stdout_name = tempfile.mkstemp() + os.close(tmp_stdout) + args.append(f">{tmp_stdout_name}") + ret = old_spawn(sh, escape, cmd, args, env) + + try: + with open(tmp_stdout_name, "r", encoding=sys.stdout.encoding, errors="replace") as tmp_stdout: + lines = tmp_stdout.read().splitlines() + os.remove(tmp_stdout_name) + except OSError: + pass + + # Early process no lines (OSError) + if not lines: + return ret + + is_cl = args[0] == "cl" + content = "" + caught = False + for line in lines: + # These conditions are far from all-encompassing, but are specialized + # for what can be reasonably expected to show up in the repository. + if not caught and (is_cl and re_cl_capture.match(line)) or (not is_cl and re_link_capture.match(line)): + caught = True + try: + with open(capture_path, "a", encoding=sys.stdout.encoding) as log: + log.write(line + "\n") + except OSError: + print(f'WARNING: Failed to log captured line: "{line}".') + continue + content += line + "\n" + # Content remaining assumed to be an error/warning. + if content: + sys.stderr.write(content) + + return ret + + env["SPAWN"] = spawn_capture + + +def options(opts): + mingw = os.getenv("MINGW_PREFIX", "") + + opts.Add(BoolVariable("use_mingw", "Use the MinGW compiler instead of MSVC - only effective on Windows", False)) + opts.Add(BoolVariable("use_static_cpp", "Link MinGW/MSVC C++ runtime libraries statically", True)) + opts.Add(BoolVariable("silence_msvc", "Silence MSVC's cl/link stdout bloat, redirecting errors to stderr.", True)) + opts.Add(BoolVariable("debug_crt", "Compile with MSVC's debug CRT (/MDd)", False)) + opts.Add(BoolVariable("use_llvm", "Use the LLVM compiler (MVSC or MinGW depending on the use_mingw flag)", False)) + opts.Add("mingw_prefix", "MinGW prefix", mingw) + + +def exists(env): + return True + + +def generate(env): + env["lto"] = "none" # Patched to work with the older godotcpp tool in webrtc-native. + + if not env["use_mingw"] and msvc.exists(env): + if env["arch"] == "x86_64": + env["TARGET_ARCH"] = "amd64" + elif env["arch"] == "arm64": + env["TARGET_ARCH"] = "arm64" + elif env["arch"] == "arm32": + env["TARGET_ARCH"] = "arm" + elif env["arch"] == "x86_32": + env["TARGET_ARCH"] = "x86" + + env["MSVC_SETUP_RUN"] = False # Need to set this to re-run the tool + env["MSVS_VERSION"] = None + env["MSVC_VERSION"] = None + + env["is_msvc"] = True + + # MSVC, linker, and archiver. + msvc.generate(env) + env.Tool("msvc") + env.Tool("mslib") + env.Tool("mslink") + + env.Append(CPPDEFINES=["TYPED_METHOD_BIND", "NOMINMAX"]) + env.Append(CCFLAGS=["/utf-8"]) + env.Append(LINKFLAGS=["/WX"]) + + if env["use_llvm"]: + env["CC"] = "clang-cl" + env["CXX"] = "clang-cl" + + if env["debug_crt"]: + # Always use dynamic runtime, static debug CRT breaks thread_local. + env.AppendUnique(CCFLAGS=["/MDd"]) + else: + if env["use_static_cpp"]: + env.AppendUnique(CCFLAGS=["/MT"]) + else: + env.AppendUnique(CCFLAGS=["/MD"]) + + if env["silence_msvc"] and not env.GetOption("clean"): + silence_msvc(env) + + if not env["use_llvm"]: + env.AppendUnique(CCFLAGS=["/experimental:external", "/external:anglebrackets"]) + env.AppendUnique(CCFLAGS=["/external:W0"]) + env["EXTINCPREFIX"] = "/external:I" + + elif (sys.platform == "win32" or sys.platform == "msys") and not env["mingw_prefix"]: + env["use_mingw"] = True + mingw.generate(env) + # Don't want lib prefixes + env["IMPLIBPREFIX"] = "" + env["SHLIBPREFIX"] = "" + # Want dll suffix + env["SHLIBSUFFIX"] = ".dll" + + env.Append(CCFLAGS=["-Wwrite-strings"]) + env.Append(LINKFLAGS=["-Wl,--no-undefined"]) + if env["use_static_cpp"]: + env.Append( + LINKFLAGS=[ + "-static", + "-static-libgcc", + "-static-libstdc++", + ] + ) + + # Long line hack. Use custom spawn, quick AR append (to avoid files with the same names to override each other). + my_spawn.configure(env) + + else: + env["use_mingw"] = True + # Cross-compilation using MinGW + prefix = "" + if env["mingw_prefix"]: + prefix = env["mingw_prefix"] + "/bin/" + + if env["arch"] == "x86_64": + prefix += "x86_64" + elif env["arch"] == "arm64": + prefix += "aarch64" + elif env["arch"] == "arm32": + prefix += "armv7" + elif env["arch"] == "x86_32": + prefix += "i686" + + if env["use_llvm"]: + env["CXX"] = prefix + "-w64-mingw32-clang++" + env["CC"] = prefix + "-w64-mingw32-clang" + env["AR"] = prefix + "-w64-mingw32-llvm-ar" + env["RANLIB"] = prefix + "-w64-mingw32-ranlib" + env["LINK"] = prefix + "-w64-mingw32-clang" + else: + env["CXX"] = prefix + "-w64-mingw32-g++" + env["CC"] = prefix + "-w64-mingw32-gcc" + env["AR"] = prefix + "-w64-mingw32-gcc-ar" + env["RANLIB"] = prefix + "-w64-mingw32-ranlib" + env["LINK"] = prefix + "-w64-mingw32-g++" + + # Want dll suffix + env["SHLIBSUFFIX"] = ".dll" + + env.Append(CCFLAGS=["-Wwrite-strings"]) + env.Append(LINKFLAGS=["-Wl,--no-undefined"]) + if env["use_static_cpp"]: + env.Append( + LINKFLAGS=[ + "-static", + "-static-libgcc", + "-static-libstdc++", + ] + ) + if env["use_llvm"]: + env.Append(LINKFLAGS=["-lstdc++"]) + + if sys.platform == "win32" or sys.platform == "msys": + my_spawn.configure(env) + + env.Append(CPPDEFINES=["WINDOWS_ENABLED"]) + + # Refer to https://github.com/godotengine/godot/blob/master/platform/windows/detect.py + if env["lto"] == "auto": + if env.get("is_msvc", False): + # No LTO by default for MSVC, doesn't help. + env["lto"] = "none" + else: # Release + env["lto"] = "full" + + common_compiler_flags.generate(env) diff --git a/misc/scripts/package_release.sh b/misc/scripts/package_release.sh new file mode 100755 index 00000000..7b3362b9 --- /dev/null +++ b/misc/scripts/package_release.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +set -e +set -x + +ARTIFACTS=${ARTIFACTS:-"artifacts"} +RELEASE=${RELEASE:-"release"} + +mkdir -p ${RELEASE}/addons/godot-git-plugin/lib +ls -R ${RELEASE} +ls -R ${ARTIFACTS} + +find "${ARTIFACTS}" -maxdepth 5 -wholename "*/lib/*" | xargs cp -r -t "${RELEASE}/addons/godot-git-plugin/lib/" +find "${ARTIFACTS}" -wholename "*/LICENSE*" | xargs cp -t "${RELEASE}/addons/godot-git-plugin" +cp misc/git_plugin.gdextension "${RELEASE}/addons/godot-git-plugin/" + +CURDIR=$(pwd) +cd "${RELEASE}/" +# Clear unneded windows files +rm addons/godot-git-plugin/lib/*.pdb addons/godot-git-plugin/lib/*.exp addons/godot-git-plugin/lib/*.lib || echo "Nothing to delete" +cd "$CURDIR" + +ls -R ${RELEASE}