diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0593767..d9850b4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,6 +29,7 @@ jobs: python-version: "3.x" architecture: "x64" - uses: ilammy/msvc-dev-cmd@v1 + - uses: ilammy/setup-nasm@v1 - name: build-windows-editor-x64 run: | git submodule update --init --recursive diff --git a/.gitignore b/.gitignore index e6d3031..45ed369 100644 --- a/.gitignore +++ b/.gitignore @@ -17,7 +17,11 @@ extension_api.json # Mac stuff .DS_Store +# Vim +*.swp + # Binaries +__pycache__/ build/ bin/ macos/ @@ -29,4 +33,4 @@ win64/ *.os *.ilk *.pdb -!thirdparty/openssl/* +*.pyc diff --git a/.gitmodules b/.gitmodules index 9118248..4d5dd20 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,10 +2,13 @@ path = godot-cpp url = https://github.com/godotengine/godot-cpp branch = 3.x -[submodule "libgit2"] +[submodule "thirdparty/git2/libgit2"] path = thirdparty/git2/libgit2 url = https://github.com/libgit2/libgit2 ignore = untracked [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 diff --git a/SConstruct b/SConstruct index 2047cdf..d5c5f0e 100644 --- a/SConstruct +++ b/SConstruct @@ -14,16 +14,6 @@ opts.Add(PathVariable("target_path", "The path where the lib is installed.", "demo/addons/godot-git-plugin/")) opts.Add(PathVariable("target_name", "The library name.", "libgit_plugin", PathVariable.PathAccept)) -opts.Add(PathVariable("macos_openssl", "Path to OpenSSL library root - only used in macOS builds.", - "/usr/local/opt/openssl@1.1/", PathVariable.PathAccept)) # TODO: Find a way to configure this to use the cloned OpenSSL source code, based on `macos_arch`. -opts.Add(PathVariable("macos_openssl_static_ssl", "Path to OpenSSL libssl.a library - only used in macOS builds.", - os.path.join(os.path.abspath(os.getcwd()), "thirdparty/openssl/libssl.a"), PathVariable.PathAccept)) -opts.Add(PathVariable("macos_openssl_static_crypto", "Path to OpenSSL libcrypto.a library - only used in macOS builds.", - os.path.join(os.path.abspath(os.getcwd()), "thirdparty/openssl/libcrypto.a"), PathVariable.PathAccept)) -opts.Add(PathVariable("linux_openssl_static_ssl", "Path to OpenSSL libssl.a library - only used in Linux builds.", - "/usr/lib/x86_64-linux-gnu/libssl.a", PathVariable.PathAccept)) -opts.Add(PathVariable("linux_openssl_static_crypto", "Path to OpenSSL libcrypto.a library - only used in Linux builds.", - "/usr/lib/x86_64-linux-gnu/libcrypto.a", PathVariable.PathAccept)) # Updates the environment with the option variables. opts.Update(env) @@ -33,11 +23,25 @@ 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. + +# OpenSSL Builder +env.Tool("openssl", toolpath=["tools"]) + +# SSH2 Builder +env.Tool("cmake", toolpath=["tools"]) +env.Tool("ssh2", toolpath=["tools"]) +env.Tool("git2", toolpath=["tools"]) + opts.Update(env) -Export("env") +ssl = env.OpenSSL() +ssh2 = env.BuildSSH2(ssl) +ssl += ssh2 +git2 = env.BuildGIT2(ssl) -SConscript("thirdparty/SCsub") +Export("ssl") +Export("env") SConscript("godot-git-plugin/SCsub") diff --git a/THIRDPARTY.md b/THIRDPARTY.md index 837488f..b9b8b52 100644 --- a/THIRDPARTY.md +++ b/THIRDPARTY.md @@ -5,10 +5,7 @@ The Godot Git Plugin source code uses the following third-party source code: 1. godotengine/godot-cpp - MIT License - https://github.com/godotengine/godot-cpp/tree/02336831735fd6affbe0a6fa252ec98d3e78120c 2. libgit2/libgit2 - GPLv2 with a special Linking Exception - https://github.com/libgit2/libgit2/tree/b7bad55e4bb0a285b073ba5e02b01d3f522fc95d 3. libssh2/libssh2 - BSD-3-Clause License - https://github.com/libssh2/libssh2/tree/635caa90787220ac3773c1d5ba11f1236c22eae8 - -We also link to these third-party libraries (only in the compiled binary form): - -1. OpenSSL - Only on Linux and MacOS - OpenSSL License - http://www.openssl.org/source/openssl-1.1.1s.tar.gz +4. openssl (only on Linux and MacOS) - OpenSSL License - https://github.com/openssl/openssl/tree/26baecb28ce461696966dac9ac889629db0b3b96 ## License Texts diff --git a/godot-git-plugin/SCsub b/godot-git-plugin/SCsub index 796cc97..3f3d0bf 100644 --- a/godot-git-plugin/SCsub +++ b/godot-git-plugin/SCsub @@ -4,6 +4,7 @@ import os env = {} Import("env") +Import("ssl") # Process some arguments if env["use_llvm"]: @@ -24,8 +25,6 @@ if env["platform"] == "macos": env["target_path"] += "macos/" # Force static linkage (https://stackoverflow.com/a/2995999/7370948) - env.Append(LIBS=[File(env["macos_openssl_static_ssl"]), - File(env["macos_openssl_static_crypto"])]) if env["macos_deployment_target"] != "default": env.Append(CCFLAGS=["-mmacosx-version-min=" + @@ -35,8 +34,6 @@ if env["platform"] == "macos": elif env["platform"] == "linux": env["target_path"] += "linux/" - env.Append(LIBS=[File(env["linux_openssl_static_ssl"]), - File(env["linux_openssl_static_crypto"])]) elif env["platform"] == "windows": env["target_path"] += "win64/" @@ -46,13 +43,13 @@ elif env["platform"] == "windows": env.Append(CPPPATH=[".", "src/"]) -env.Append(CPPPATH=["../thirdparty/git2/libgit2/include/"]) -env.Append(LIBPATH=["../thirdparty/bin/"]) -env.Prepend(LIBS=["git2", "ssh2"]) +env.Append(CPPPATH=["#thirdparty/git2/libgit2/include/"]) +lib_sources = Glob("src/*.cpp") +env.Depends(lib_sources, ssl) library = env.SharedLibrary( target=env["target_path"] + "{}{}{}".format(env["target_name"], env["suffix"], env["SHLIBSUFFIX"]), - source=Glob("src/*.cpp") + source=lib_sources ) Default(library) diff --git a/thirdparty/SCsub b/thirdparty/SCsub deleted file mode 100644 index 9b9252e..0000000 --- a/thirdparty/SCsub +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env python - -Import("env") - -SConscript("ssh2/SCsub") -SConscript("git2/SCsub") diff --git a/thirdparty/git2/SCsub b/thirdparty/git2/SCsub deleted file mode 100644 index 3f75044..0000000 --- a/thirdparty/git2/SCsub +++ /dev/null @@ -1,135 +0,0 @@ -#!/usr/bin/env python - -# Adopted from https://github.com/goostengine/goost/blob/20d8ce4c7d74c26832d69283305b25a72165784a/modules/git/SCsub - -Import("env") - -env_git = env.Clone() - -# Thirdparty source files. -libgit2_sources = [] -libgit2_sources += Glob("libgit2/src/" + "*.c") -libgit2_sources += Glob("libgit2/src/allocators/" + "*.c") -libgit2_sources += Glob("libgit2/src/hash/sha1/sha1dc/sha1.c") -libgit2_sources += Glob("libgit2/src/hash/sha1/sha1dc/ubc_check.c") -libgit2_sources += Glob("libgit2/src/hash/sha1/collisiondetect.c") -libgit2_sources += Glob("libgit2/src/transports/" + "*.c") -libgit2_sources += Glob("libgit2/src/hash/transports/" + "*.c") -libgit2_sources += Glob("libgit2/src/hash/xdiff/" + "*.c") -libgit2_sources += Glob("libgit2/src/xdiff/" + "*.c") -libgit2_sources += Glob("libgit2/src/streams/" + "*.c") - -libgit2_sources += Glob("libgit2/deps/http-parser/" + "*.c") -libgit2_sources += Glob("libgit2/deps/pcre/" + "*.c") -libgit2_sources += Glob("libgit2/deps/zlib/" + "*.c") - -if "32" in env["arch"]: - env_git.Prepend(CPPDEFINES=["GIT_ARCH_32"]) -else: - env_git.Prepend(CPPDEFINES=["GIT_ARCH_64"]) - -env_git.Prepend( - CPPPATH=[ - "libgit2/include/", - "libgit2/src/", - "libgit2/deps/http-parser/", - "libgit2/deps/pcre/", - "libgit2/deps/zlib/", - "libgit2/deps/ntlmclient/", - "../ssh2/libssh2/include/", - ] -) - -env_git.Prepend( - CPPDEFINES=[ - "GIT_THREADS", - "GIT_SSH", - "GIT_SSH_MEMORY_CREDENTIALS", - "LIBGIT2_NO_FEATURES_H", - "GIT_SHA1_COLLISIONDETECT", - "GIT_HTTPS", - "SRC_UTIL_H_", - "GIT_REGEX_BUILTIN", - ] -) - -if env_git["platform"] == "windows": - libgit2_sources += Glob("libgit2/src/win32/" + "*.c") - env_git.Prepend( - CPPDEFINES=[ - "GIT_WINHTTP", - "HAVE_SYS_STAT_H", - "HAVE_SYS_TYPES_H", - "HAVE_WINDOWS_H", - "HAVE_STDINT_H", - "HAVE_INTTYPES_H", - "HAVE_MEMMOVE", - "HAVE_STRERROR", - "HAVE_STRTOLL", - "HAVE__STRTOI64", - "SUPPORT_PCRE8", - "NO_RECURSE", - "HAVE_LONG_LONG", - "HAVE_UNSIGNED_LONG_LONG", - ("NEWLINE", "10"), - ("POSIX_MALLOC_THRESHOLD", "10"), - ("LINK_SIZE", "2"), - ("PARENS_NEST_LIMIT", "250"), - ("MATCH_LIMIT", "10000000"), - ("MATCH_LIMIT_RECURSION", "10000000"), - "PCREGREP_BUFSIZE", - ("MAX_NAME_SIZE", "32"), - ("MAX_NAME_COUNT", "10000"), - "HAVE_WINCNG", - "LIBSSH2_WINCNG" - ] - ) - -if env_git["platform"] in ["linux", "macos"]: - env_git.Append(CCFLAGS="-fPIC") - libgit2_sources += Glob("libgit2/src/unix/" + "*.c") - libgit2_sources += [ - "libgit2/deps/ntlmclient/crypt_openssl.c", - "libgit2/deps/ntlmclient/unicode_builtin.c" - ] - env_git.Prepend( - CPPDEFINES=[ - "HAVE_DIRENT_H", - "HAVE_SYS_STAT_H", - "HAVE_SYS_TYPES_H", - "HAVE_UNISTD_H", - "HAVE_STDINT_H", - "HAVE_INTTYPES_H", - "HAVE_BCOPY", - "HAVE_MEMMOVE", - "HAVE_STRERROR", - "HAVE_STRTOLL", - "HAVE_STRTOQ", - "SUPPORT_PCRE8", - "NO_RECURSE", - "HAVE_LONG_LONG", - "HAVE_UNSIGNED_LONG_LONG", - ("NEWLINE", "10"), - ("POSIX_MALLOC_THRESHOLD", "10"), - ("LINK_SIZE", "2"), - ("PARENS_NEST_LIMIT", "250"), - ("MATCH_LIMIT", "10000000"), - ("MATCH_LIMIT_RECURSION", "10000000"), - "PCREGREP_BUFSIZE", - ("MAX_NAME_SIZE", "32"), - ("MAX_NAME_COUNT", "10000"), - "GIT_OPENSSL", - "GIT_USE_FUTIMENS", - "GIT_USE_STAT_MTIM", - "CRYPT_OPENSSL", - "UNICODE_BUILTIN" - ] - ) - -if env_git["platform"] == "macos": - env_git.Prepend(CPPPATH=[env_git["macos_openssl"] + "include/"]) - static_ssl = File(env_git["macos_openssl_static_ssl"]) - static_crypto = File(env_git["macos_openssl_static_crypto"]) - env_git.Append(LIBS=[static_ssl, static_crypto]) - -env_git.StaticLibrary(target="../bin/" + "git2", source=libgit2_sources) diff --git a/thirdparty/git2/libgit2 b/thirdparty/git2/libgit2 index b7bad55..3e2baa6 160000 --- a/thirdparty/git2/libgit2 +++ b/thirdparty/git2/libgit2 @@ -1 +1 @@ -Subproject commit b7bad55e4bb0a285b073ba5e02b01d3f522fc95d +Subproject commit 3e2baa6d0bfb42f9016e24cba1733a6ae26a8ae6 diff --git a/thirdparty/openssl b/thirdparty/openssl new file mode 160000 index 0000000..2cf4e90 --- /dev/null +++ b/thirdparty/openssl @@ -0,0 +1 @@ +Subproject commit 2cf4e90eaaf7402bf038b158dbdacd0a15561fb7 diff --git a/thirdparty/openssl/libcrypto.a b/thirdparty/openssl/libcrypto.a deleted file mode 100644 index 8189ef1..0000000 Binary files a/thirdparty/openssl/libcrypto.a and /dev/null differ diff --git a/thirdparty/openssl/libssl.a b/thirdparty/openssl/libssl.a deleted file mode 100644 index 5e73808..0000000 Binary files a/thirdparty/openssl/libssl.a and /dev/null differ diff --git a/thirdparty/ssh2/SCsub b/thirdparty/ssh2/SCsub deleted file mode 100644 index ed27565..0000000 --- a/thirdparty/ssh2/SCsub +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/env python - -# Adopted from https://github.com/nodegit/nodegit/blob/4561dcb7c120474a4553baa27e4c4c2f4be23a2b/vendor/libgit2.gyp - -Import("env") - -env_ssh2 = env.Clone() - -# Thirdparty source files. -libssh2_sources = [ - "libssh2/src/agent.c", - "libssh2/src/bcrypt_pbkdf.c", - "libssh2/src/blowfish.c", - "libssh2/src/crypt.c", - "libssh2/src/keepalive.c", - "libssh2/src/libgcrypt.c", - "libssh2/src/openssl.c", - "libssh2/src/publickey.c", - "libssh2/src/sftp.c", - "libssh2/src/version.c", - "libssh2/src/channel.c", - "libssh2/src/global.c", - "libssh2/src/kex.c", - "libssh2/src/mac.c", - "libssh2/src/packet.c", - "libssh2/src/scp.c", - "libssh2/src/transport.c", - "libssh2/src/comp.c", - "libssh2/src/hostkey.c", - "libssh2/src/knownhost.c", - "libssh2/src/misc.c", - "libssh2/src/pem.c", - "libssh2/src/session.c", - "libssh2/src/userauth_kbd_packet.c", - "libssh2/src/userauth.c", -] - -env_ssh2.Prepend( - CPPPATH=[ - "libssh2/include/" - "libssh2/src/" - ] -) - -if env_ssh2["platform"] == "windows": - env_ssh2.Append( - CPPPATH=[ - "libssh2/include/", - "libssh2/win32/" - ] - ) - libssh2_sources += ["libssh2/src/agent_win.c", "libssh2/src/wincng.c"] - env_ssh2.Append( - CPPDEFINES=[ - "LIBSSH2_WINCNG", - "HAVE_WINCNG" - ] - ) - env_ssh2.Append(LIBS=["crypt32", "user32"]) - -if env_ssh2["platform"] in ["linux", "macos"]: - env_ssh2.Append(CCFLAGS="-fPIC") - env_ssh2.Append( - CPPPATH=[ - ".", - "libssh2/include/", - ] - ) - libssh2_sources += ["libssh2/src/openssl.c"] - env_ssh2.Append( - CPPDEFINES=[ - "LIBSSH2_CONFIG_H", - "HAVE_SYS_UIO_H", - ("HAVE_ALLOCA", 1), - ("HAVE_ALLOCA_H", 1), - ("HAVE_ARPA_INET_H", 1), - ("HAVE_ERRNO_H", 1), - ("HAVE_FCNTL_H", 1), - ("HAVE_GETTIMEOFDAY", 1), - ("HAVE_GETTIMEOFDAY", 1), - ("HAVE_INTTYPES_H", 1), - ("HAVE_LONGLONG", 1), - ("HAVE_NETINET_IN_H", 1), - ("HAVE_O_NONBLOCK", 1), - ("HAVE_SELECT", 1), - ("HAVE_STDINT_H", 1), - ("HAVE_STDIO_H", 1), - ("HAVE_STDLIB_H", 1), - ("HAVE_STRINGS_H", 1), - ("HAVE_STRING_H", 1), - ("HAVE_STRTOLL", 1), - ("HAVE_SYS_IOCTL_H", 1), - ("HAVE_SYS_SOCKET_H", 1), - ("HAVE_SYS_STAT_H", 1), - ("HAVE_SYS_TIME_H", 1), - ("HAVE_SYS_TYPES_H", 1), - ("HAVE_SYS_UIO_H", 1), - ("HAVE_SYS_UN_H", 1), - ("HAVE_UNISTD_H", 1), - ("LIBSSH2_CLEAR_MEMORY", 1), - ("LIBSSH2_DH_GEX_NEW", 1), - ("LIBSSH2_OPENSSL", 1), - ("LT_OBJDIR", ".libs/"), - ("STDC_HEADERS", 1) - ] - ) - -if env_ssh2["platform"] == "macos": - env_ssh2.Append(CPPPATH=[env_ssh2["macos_openssl"] + "include/"]) - static_ssl = File(env_ssh2["macos_openssl_static_ssl"]) - static_crypto = File(env_ssh2["macos_openssl_static_crypto"]) - env_ssh2.Append(LIBS=[static_ssl, static_crypto]) - -env_ssh2.StaticLibrary(target="../bin/" + "ssh2", source=libssh2_sources) diff --git a/thirdparty/ssh2/libssh2 b/thirdparty/ssh2/libssh2 index 6cba487..1c3f1b7 160000 --- a/thirdparty/ssh2/libssh2 +++ b/thirdparty/ssh2/libssh2 @@ -1 +1 @@ -Subproject commit 6cba487395e79775482fc80dcbc4146e3b17c773 +Subproject commit 1c3f1b7da588f2652260285529ec3c1f1125eb4e diff --git a/tools/cmake.py b/tools/cmake.py new file mode 100644 index 0000000..39957d8 --- /dev/null +++ b/tools/cmake.py @@ -0,0 +1,135 @@ +import os, sys + +import SCons.Util +import SCons.Builder +import SCons.Action + + +def cmake_default_flags(env): + if env.get("cmake_default_flags", ""): + return SCons.Util.CLVar(env["cmake_default_flags"]) + + config = {} + + if "CC" in env: + config["CMAKE_C_COMPILER"] = env["CC"] + if "CXX" in env: + config["CMAKE_CXX_COMPILER"] = env["CXX"] + + if env["platform"] == "android": + api = env["android_api_level"] + abi = { + "arm64": "arm64-v8a", + "arm32": "armeabi-v7a", + "x86_32": "x86", + "x86_64": "x86_64", + }[env["arch"]] + config["CMAKE_SYSTEM_NAME"] = "Android" + config["CMAKE_SYSTEM_VERSION"] = api + config["CMAKE_ANDROID_ARCH_ABI"] = abi + config["ANDROID_ABI"] = abi + config["CMAKE_TOOLCHAIN_FILE"] = "%s/build/cmake/android.toolchain.cmake" % env.get( + "ANDROID_NDK_ROOT", os.environ.get("ANDROID_NDK_ROOT", "") + ) + config["CMAKE_ANDROID_STL_TYPE"] = "c++_static" + + elif env["platform"] == "linux": + linux_flags = { + "x86_64": "-m64", + "x86_32": "-m32", + }.get(env["arch"], "") + if linux_flags: + config["CMAKE_C_FLAGS"] = linux_flags + config["CMAKE_CXX_FLAGS"] = linux_flags + + elif env["platform"] == "macos": + if env["arch"] == "universal": + config["CMAKE_OSX_ARCHITECTURES"] = '"x86_64;arm64"' + else: + config["CMAKE_OSX_ARCHITECTURES"] = env["arch"] + if env["macos_deployment_target"] != "default": + config["CMAKE_OSX_DEPLOYMENT_TARGET"] = env["macos_deployment_target"] + + if env["platform"] == "macos" and sys.platform != "darwin" and "OSXCROSS_ROOT" in os.environ: + config["CMAKE_AR"] = env["AR"] + config["CMAKE_RANLIB"] = env["RANLIB"] + if env["arch"] == "universal": + flags = "-arch x86_64 -arch arm64" + else: + flags = "-arch " + env["arch"] + if env["macos_deployment_target"] != "default": + flags += " -mmacosx-version-min=" + env["macos_deployment_target"] + config["CMAKE_C_FLAGS"] = flags + config["CMAKE_CXX_FLAGS"] = flags + + elif env["platform"] == "ios": + if env["arch"] == "universal": + raise ValueError("iOS architecture not supported: %s" % env["arch"]) + config["CMAKE_SYSTEM_NAME"] = "iOS" + config["CMAKE_OSX_ARCHITECTURES"] = env["arch"] + if env.get("ios_min_version", "default") != "default": + config["CMAKE_OSX_DEPLOYMENT_TARGET"] = env["ios_min_version"] + if env["ios_simulator"]: + config["CMAKE_OSX_SYSROOT"] = "iphonesimulator" + + elif env["platform"] == "windows": + config["CMAKE_SYSTEM_NAME"] = "Windows" + + flags = ["-D%s=%s" % it for it in config.items()] + if env["CMAKEGENERATOR"]: + flags.extend(["-G", env["CMAKEGENERATOR"]]) + elif env["platform"] == "windows": + if env.get("is_msvc", False): + flags.extend(["-G", "NMake Makefiles"]) + elif sys.platform in ["win32", "msys", "cygwin"]: + flags.extend(["-G", "Ninja"]) + else: + flags.extend(["-G", "Unix Makefiles"]) + return flags + + +def cmake_emitter(target, source, env): + return [str(target[0]) + "/CMakeCache.txt"] + target[1:], [str(source[0]) + "/CMakeLists.txt"] + source[1:] + + +def cmake_generator(target, source, env, for_signature): + # Strip the -j option for signature to avoid rebuilding when num_jobs changes. + build = env["CMAKEBUILDCOM"].replace("-j$CMAKEBUILDJOBS", "") if for_signature else env["CMAKEBUILDCOM"] + return [ + SCons.Action.Action("$CMAKECONFCOM", "$CMAKECONFCOMSTR"), + SCons.Action.Action(build, "$CMAKEBUILDCOMSTR"), + ] + + +def cmake_build(env, target_dir, source_dir, cmake_outputs=[], cmake_targets=[], cmake_options=[], dependencies=[]): + cmake_env = env.Clone() + target = env.Dir("{}/{}/{}".format(target_dir, env["platform"], env["arch"])) + source = env.Dir(source_dir) + builder_targets = [target] + [str(target) + "/" + f for f in cmake_outputs] + builder_sources = [source] + dependencies + cmake_env.Append(CMAKECONFFLAGS=["-D%s=%s" % it for it in cmake_options.items()]) + if len(cmake_targets) > 0: + cmake_env.Append(CMAKEBUILDFLAGS=["-t"] + [t for t in cmake_targets]) + return cmake_env.CMake(builder_targets, builder_sources) + + +def options(opts): + opts.Add("cmake_default_flags", "Default CMake platform flags override, will be autodetected if not specified.", "") + + +def exists(env): + return True + + +def generate(env): + env["CMAKE"] = "cmake" + env["_cmake_default_flags"] = cmake_default_flags + env["CMAKEDEFAULTFLAGS"] = "${_cmake_default_flags(__env__)}" + env["CMAKEGENERATOR"] = "" + env["CMAKECONFFLAGS"] = SCons.Util.CLVar("") + env["CMAKECONFCOM"] = "$CMAKE -B ${TARGET.dir} $CMAKEDEFAULTFLAGS $CMAKECONFFLAGS ${SOURCE.dir}" + env["CMAKEBUILDJOBS"] = "${__env__.GetOption('num_jobs')}" + env["CMAKEBUILDFLAGS"] = SCons.Util.CLVar("") + env["CMAKEBUILDCOM"] = "$CMAKE --build ${TARGET.dir} $CMAKEBUILDFLAGS -j$CMAKEBUILDJOBS" + env["BUILDERS"]["CMake"] = SCons.Builder.Builder(generator=cmake_generator, emitter=cmake_emitter) + env.AddMethod(cmake_build, "CMakeBuild") diff --git a/tools/git2.py b/tools/git2.py new file mode 100644 index 0000000..bba3d6d --- /dev/null +++ b/tools/git2.py @@ -0,0 +1,65 @@ +import os + + +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"], + "BUILD_TESTS": 0, + "BUILD_CLI": 0, + "BUILD_EXAMPLES": 0, + "BUILD_FUZZERS": 0, + "USE_SSH": 1, + "USE_HTTPS": 1, + "USE_SHA1": 1, + "USE_BUNDLED_ZLIB": 1, + "USE_HTTP_PARSER": "builtin", + "REGEX_BACKEND": "builtin", + "USE_HTTPS": "OpenSSL", + "USE_SHA1": "OpenSSL", + "BUILD_SHARED_LIBS": 0, + "LINK_WITH_STATIC_LIBRARIES": 1, + "LIBSSH2_INCLUDE_DIR": env.Dir("#thirdparty/ssh2/libssh2/include").abspath, + "LIBSSH2_LIBRARY": deps[-1], + "USE_WINHTTP": 0, + "STATIC_CRT": 0, + } + + 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" + 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", + cmake_options=config, + cmake_outputs=libs, + cmake_targets=[], + dependencies=deps, + ) + + env.Append(CPPPATH=["#thirdparty/git2/libgit2/include"]) + env.Prepend(LIBS=git2[1:]) + + return git2 + + +def exists(env): + return "CMake" in env + + +def generate(env): + env.AddMethod(build_library, "BuildGIT2") diff --git a/tools/openssl.py b/tools/openssl.py new file mode 100644 index 0000000..f42928f --- /dev/null +++ b/tools/openssl.py @@ -0,0 +1,286 @@ +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": + # 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 new file mode 100644 index 0000000..a5cbe60 --- /dev/null +++ b/tools/ssh2.py @@ -0,0 +1,50 @@ +import os + + +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"], + "BUILD_EXAMPLES": 0, + "BUILD_TESTING": 0, + "BUILD_SHARED_LIBS": 0, + "CRYPTO_BACKEND": "OpenSSL", + } + + 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" + 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", + cmake_options=config, + cmake_outputs=libs, + cmake_targets=[], + dependencies=deps, + ) + + env.Append(CPPPATH=["#thirdparty/ssh2/libssh2/include"]) + env.Prepend(LIBS=ssh2[1:]) + + return ssh2 + + +def exists(env): + return "CMake" in env + + +def generate(env): + env.AddMethod(build_library, "BuildSSH2")