diff --git a/cmake/SelectZlib.cmake b/cmake/SelectZlib.cmake index fb4361abc..25c7b2f94 100644 --- a/cmake/SelectZlib.cmake +++ b/cmake/SelectZlib.cmake @@ -4,6 +4,7 @@ include(SanitizeBool) SanitizeBool(USE_BUNDLED_ZLIB) if(USE_BUNDLED_ZLIB STREQUAL ON) set(USE_BUNDLED_ZLIB "Bundled") + set(GIT_COMPRESSION_BUILTIN) endif() if(USE_BUNDLED_ZLIB STREQUAL "OFF") @@ -17,6 +18,7 @@ if(USE_BUNDLED_ZLIB STREQUAL "OFF") list(APPEND LIBGIT2_PC_REQUIRES "zlib") endif() add_feature_info(zlib ON "using system zlib") + set(GIT_COMPRESSION_ZLIB 1) else() message(STATUS "zlib was not found; using bundled 3rd-party sources." ) endif() @@ -26,9 +28,11 @@ if(USE_BUNDLED_ZLIB STREQUAL "Chromium") list(APPEND LIBGIT2_DEPENDENCY_INCLUDES "${PROJECT_SOURCE_DIR}/deps/chromium-zlib") list(APPEND LIBGIT2_DEPENDENCY_OBJECTS $) add_feature_info(zlib ON "using (Chromium) bundled zlib") + set(GIT_COMPRESSION_BUILTIN 1) elseif(USE_BUNDLED_ZLIB OR NOT ZLIB_FOUND) add_subdirectory("${PROJECT_SOURCE_DIR}/deps/zlib" "${PROJECT_BINARY_DIR}/deps/zlib") list(APPEND LIBGIT2_DEPENDENCY_INCLUDES "${PROJECT_SOURCE_DIR}/deps/zlib") list(APPEND LIBGIT2_DEPENDENCY_OBJECTS $) add_feature_info(zlib ON "using bundled zlib") + set(GIT_COMPRESSION_BUILTIN 1) endif() diff --git a/include/git2/common.h b/include/git2/common.h index dda821c7c..006f7eeb1 100644 --- a/include/git2/common.h +++ b/include/git2/common.h @@ -130,57 +130,81 @@ GIT_EXTERN(int) git_libgit2_version(int *major, int *minor, int *rev); GIT_EXTERN(const char *) git_libgit2_prerelease(void); /** - * Combinations of these values describe the features with which libgit2 - * was compiled + * Configurable features of libgit2; either optional settings (like + * threading), or features that can be enabled by one of a number of + * different backend "providers" (like HTTPS, which can be provided by + * OpenSSL, mbedTLS, or system libraries). */ typedef enum { - /** - * If set, libgit2 was built thread-aware and can be safely used from multiple - * threads. - */ - GIT_FEATURE_THREADS = (1 << 0), - /** - * If set, libgit2 was built with and linked against a TLS implementation. - * Custom TLS streams may still be added by the user to support HTTPS - * regardless of this. - */ - GIT_FEATURE_HTTPS = (1 << 1), - /** - * If set, libgit2 was built with and linked against libssh2. A custom - * transport may still be added by the user to support libssh2 regardless of - * this. - */ - GIT_FEATURE_SSH = (1 << 2), - /** - * If set, libgit2 was built with support for sub-second resolution in file - * modification times. - */ - GIT_FEATURE_NSEC = (1 << 3) + /** + * libgit2 is thread-aware and can be used from multiple threads + * (as described in the documentation). + */ + GIT_FEATURE_THREADS = (1 << 0), + + /** HTTPS remotes */ + GIT_FEATURE_HTTPS = (1 << 1), + + /** SSH remotes */ + GIT_FEATURE_SSH = (1 << 2), + + /** Sub-second resolution in index timestamps */ + GIT_FEATURE_NSEC = (1 << 3), + + /** HTTP parsing; always available */ + GIT_FEATURE_HTTP_PARSER = (1 << 4), + + /** Regular expression support; always available */ + GIT_FEATURE_REGEX = (1 << 5), + + /** Internationalization support for filename translation */ + GIT_FEATURE_I18N = (1 << 6), + + /** NTLM support over HTTPS */ + GIT_FEATURE_AUTH_NTLM = (1 << 7), + + /** Kerberos (SPNEGO) authentication support over HTTPS */ + GIT_FEATURE_AUTH_NEGOTIATE = (1 << 8), + + /** zlib support; always available */ + GIT_FEATURE_COMPRESSION = (1 << 9), + + /** SHA1 object support; always available */ + GIT_FEATURE_SHA1 = (1 << 10), + + /** SHA256 object support */ + GIT_FEATURE_SHA256 = (1 << 11) } git_feature_t; /** * Query compile time options for libgit2. * * @return A combination of GIT_FEATURE_* values. - * - * - GIT_FEATURE_THREADS - * Libgit2 was compiled with thread support. Note that thread support is - * still to be seen as a 'work in progress' - basic object lookups are - * believed to be threadsafe, but other operations may not be. - * - * - GIT_FEATURE_HTTPS - * Libgit2 supports the https:// protocol. This requires the openssl - * library to be found when compiling libgit2. - * - * - GIT_FEATURE_SSH - * Libgit2 supports the SSH protocol for network operations. This requires - * the libssh2 library to be found when compiling libgit2 - * - * - GIT_FEATURE_NSEC - * Libgit2 supports the sub-second resolution in file modification times. */ GIT_EXTERN(int) git_libgit2_features(void); +/** + * Query the backend details for the compile-time feature in libgit2. + * + * This will return the "backend" for the feature, which is useful for + * things like HTTPS or SSH support, that can have multiple backends + * that could be compiled in. + * + * For example, when libgit2 is compiled with dynamic OpenSSL support, + * the feature backend will be `openssl-dynamic`. The feature backend + * names reflect the compilation options specified to the build system + * (though in all lower case). The backend _may_ be "builtin" for + * features that are provided by libgit2 itself. + * + * If the feature is not supported by the library, this API returns + * `NULL`. + * + * @param feature the feature to query details for + * @return the provider details, or NULL if the feature is not supported + */ +GIT_EXTERN(const char *) git_libgit2_feature_backend( + git_feature_t feature); + /** * Global library options * diff --git a/src/libgit2/libgit2.c b/src/libgit2/libgit2.c index 1375d87af..de72cfe7d 100644 --- a/src/libgit2/libgit2.c +++ b/src/libgit2/libgit2.c @@ -92,6 +92,177 @@ int git_libgit2_features(void) #endif #ifdef GIT_USE_NSEC | GIT_FEATURE_NSEC +#endif + | GIT_FEATURE_HTTP_PARSER + | GIT_FEATURE_REGEX +#ifdef GIT_USE_ICONV + | GIT_FEATURE_I18N +#endif +#if defined(GIT_NTLM) || defined(GIT_WIN32) + | GIT_FEATURE_AUTH_NTLM +#endif +#if defined(GIT_GSSAPI) || defined(GIT_GSSFRAMEWORK) || defined(GIT_WIN32) + | GIT_FEATURE_AUTH_NEGOTIATE +#endif + | GIT_FEATURE_COMPRESSION + | GIT_FEATURE_SHA1 +#ifdef GIT_EXPERIMENTAL_SHA256 + | GIT_FEATURE_SHA256 #endif ; } + +const char *git_libgit2_feature_backend(git_feature_t feature) +{ + switch (feature) { + case GIT_FEATURE_THREADS: +#if defined(GIT_THREADS) && defined(GIT_WIN32) + return "win32"; +#elif defined(GIT_THREADS) + return "pthread"; +#endif + break; + + case GIT_FEATURE_HTTPS: +#if defined(GIT_HTTPS) && defined(GIT_OPENSSL) + return "openssl"; +#elif defined(GIT_HTTPS) && defined(GIT_OPENSSL_DYNAMIC) + return "openssl-dynamic"; +#elif defined(GIT_HTTPS) && defined(GIT_MBEDTLS) + return "mbedtls"; +#elif defined(GIT_HTTPS) && defined(GIT_SECURE_TRANSPORT) + return "securetransport"; +#elif defined(GIT_HTTPS) && defined(GIT_SCHANNEL) + return "schannel"; +#elif defined(GIT_HTTPS) && defined(GIT_WINHTTP) + return "winhttp"; +#elif defined(GIT_HTTPS) + GIT_ASSERT_WITH_RETVAL(!"Unknown HTTPS backend", NULL); +#endif + break; + + case GIT_FEATURE_SSH: +#if defined(GIT_SSH_EXEC) + return "exec"; +#elif defined(GIT_SSH_LIBSSH2) + return "libssh2"; +#elif defined(GIT_SSH) + GIT_ASSERT_WITH_RETVAL(!"Unknown SSH backend", NULL); +#endif + break; + + case GIT_FEATURE_NSEC: +#if defined(GIT_USE_NSEC) && defined(GIT_USE_STAT_MTIMESPEC) + return "mtimespec"; +#elif defined(GIT_USE_NSEC) && defined(GIT_USE_STAT_MTIM) + return "mtim"; +#elif defined(GIT_USE_NSEC) && defined(GIT_USE_STAT_MTIME_NSEC) + return "mtime"; +#elif defined(GIT_USE_NSEC) && defined(GIT_WIN32) + return "win32"; +#elif defined(GIT_USE_NSEC) + GIT_ASSERT_WITH_RETVAL(!"Unknown high-resolution time backend", NULL); +#endif + break; + + case GIT_FEATURE_HTTP_PARSER: +#if defined(GIT_HTTPPARSER_HTTPPARSER) + return "httpparser"; +#elif defined(GIT_HTTPPARSER_LLHTTP) + return "llhttp"; +#elif defined(GIT_HTTPPARSER_BUILTIN) + return "builtin"; +#endif + GIT_ASSERT_WITH_RETVAL(!"Unknown HTTP parser backend", NULL); + break; + + case GIT_FEATURE_REGEX: +#if defined(GIT_REGEX_REGCOMP_L) + return "regcomp_l"; +#elif defined(GIT_REGEX_REGCOMP) + return "regcomp"; +#elif defined(GIT_REGEX_PCRE) + return "pcre"; +#elif defined(GIT_REGEX_PCRE2) + return "pcre2"; +#elif defined(GIT_REGEX_BUILTIN) + return "builtin"; +#endif + GIT_ASSERT_WITH_RETVAL(!"Unknown regular expression backend", NULL); + break; + + case GIT_FEATURE_I18N: +#if defined(GIT_USE_ICONV) + return "iconv"; +#endif + break; + + case GIT_FEATURE_AUTH_NTLM: +#if defined(GIT_NTLM) + return "ntlmclient"; +#elif defined(GIT_WIN32) + return "sspi"; +#endif + break; + + case GIT_FEATURE_AUTH_NEGOTIATE: +#if defined(GIT_GSSAPI) + return "gssapi"; +#elif defined(GIT_WIN32) + return "sspi"; +#endif + break; + + case GIT_FEATURE_COMPRESSION: +#if defined(GIT_COMPRESSION_ZLIB) + return "zlib"; +#elif defined(GIT_COMPRESSION_BUILTIN) + return "builtin"; +#else + GIT_ASSERT_WITH_RETVAL(!"Unknown compression backend", NULL); +#endif + break; + + case GIT_FEATURE_SHA1: +#if defined(GIT_SHA1_COLLISIONDETECT) + return "builtin"; +#elif defined(GIT_SHA1_OPENSSL) + return "openssl"; +#elif defined(GIT_SHA1_OPENSSL_FIPS) + return "openssl-fips"; +#elif defined(GIT_SHA1_OPENSSL_DYNAMIC) + return "openssl-dynamic"; +#elif defined(GIT_SHA1_MBEDTLS) + return "mbedtls"; +#elif defined(GIT_SHA1_COMMON_CRYPTO) + return "commoncrypto"; +#elif defined(GIT_SHA1_WIN32) + return "win32"; +#else + GIT_ASSERT_WITH_RETVAL(!"Unknown SHA1 backend", NULL); +#endif + break; + + case GIT_FEATURE_SHA256: +#if defined(GIT_EXPERIMENTAL_SHA256) && defined(GIT_SHA256_BUILTIN) + return "builtin"; +#elif defined(GIT_EXPERIMENTAL_SHA256) && defined(GIT_SHA256_OPENSSL) + return "openssl"; +#elif defined(GIT_EXPERIMENTAL_SHA256) && defined(GIT_SHA256_OPENSSL_FIPS) + return "openssl-fips"; +#elif defined(GIT_EXPERIMENTAL_SHA256) && defined(GIT_SHA256_OPENSSL_DYNAMIC) + return "openssl-dynamic"; +#elif defined(GIT_EXPERIMENTAL_SHA256) && defined(GIT_SHA256_MBEDTLS) + return "mbedtls"; +#elif defined(GIT_EXPERIMENTAL_SHA256) && defined(GIT_SHA256_COMMON_CRYPTO) + return "commoncrypto"; +#elif defined(GIT_EXPERIMENTAL_SHA256) && defined(GIT_SHA256_WIN32) + return "win32"; +#elif defined(GIT_EXPERIMENTAL_SHA256) + GIT_ASSERT_WITH_RETVAL(!"Unknown SHA256 backend", NULL); +#endif + break; + } + + return NULL; +} diff --git a/src/util/git2_features.h.in b/src/util/git2_features.h.in index 9408f9b00..cd14cc234 100644 --- a/src/util/git2_features.h.in +++ b/src/util/git2_features.h.in @@ -66,6 +66,9 @@ #cmakedefine GIT_SHA256_OPENSSL_DYNAMIC 1 #cmakedefine GIT_SHA256_MBEDTLS 1 +#cmakedefine GIT_COMPRESSION_BUILTIN 1 +#cmakedefine GIT_COMPRESSION_ZLIB 1 + #cmakedefine GIT_RAND_GETENTROPY 1 #cmakedefine GIT_RAND_GETLOADAVG 1 diff --git a/tests/libgit2/core/features.c b/tests/libgit2/core/features.c index a0f18b659..b7cd6014a 100644 --- a/tests/libgit2/core/features.c +++ b/tests/libgit2/core/features.c @@ -1,15 +1,8 @@ #include "clar_libgit2.h" -void test_core_features__0(void) +void test_core_features__basic(void) { - int major, minor, rev, caps; - - git_libgit2_version(&major, &minor, &rev); - cl_assert_equal_i(LIBGIT2_VERSION_MAJOR, major); - cl_assert_equal_i(LIBGIT2_VERSION_MINOR, minor); - cl_assert_equal_i(LIBGIT2_VERSION_REVISION, rev); - - caps = git_libgit2_features(); + int caps = git_libgit2_features(); #ifdef GIT_THREADS cl_assert((caps & GIT_FEATURE_THREADS) != 0); @@ -32,4 +25,202 @@ void test_core_features__0(void) #else cl_assert((caps & GIT_FEATURE_NSEC) == 0); #endif + + cl_assert((caps & GIT_FEATURE_HTTP_PARSER) != 0); + cl_assert((caps & GIT_FEATURE_REGEX) != 0); + +#if defined(GIT_USE_ICONV) + cl_assert((caps & GIT_FEATURE_I18N) != 0); +#endif + +#if defined(GIT_NTLM) || defined(GIT_WIN32) + cl_assert((caps & GIT_FEATURE_AUTH_NTLM) != 0); +#endif +#if defined(GIT_GSSAPI) || defined(GIT_GSSFRAMEWORK) || defined(GIT_WIN32) + cl_assert((caps & GIT_FEATURE_AUTH_NEGOTIATE) != 0); +#endif + + cl_assert((caps & GIT_FEATURE_COMPRESSION) != 0); + cl_assert((caps & GIT_FEATURE_SHA1) != 0); + +#if defined(GIT_EXPERIMENTAL_SHA256) + cl_assert((caps & GIT_FEATURE_SHA256) != 0); +#endif + + /* + * Ensure that our tests understand all the features; + * this test tries to ensure that if there's a new feature + * added that the backends test (below) is updated as well. + */ + cl_assert((caps & ~(GIT_FEATURE_THREADS | + GIT_FEATURE_HTTPS | + GIT_FEATURE_SSH | + GIT_FEATURE_NSEC | + GIT_FEATURE_HTTP_PARSER | + GIT_FEATURE_REGEX | + GIT_FEATURE_I18N | + GIT_FEATURE_AUTH_NTLM | + GIT_FEATURE_AUTH_NEGOTIATE | + GIT_FEATURE_COMPRESSION | + GIT_FEATURE_SHA1 | + GIT_FEATURE_SHA256 + )) == 0); +} + +void test_core_features__backends(void) +{ + const char *threads = git_libgit2_feature_backend(GIT_FEATURE_THREADS); + const char *https = git_libgit2_feature_backend(GIT_FEATURE_HTTPS); + const char *ssh = git_libgit2_feature_backend(GIT_FEATURE_SSH); + const char *nsec = git_libgit2_feature_backend(GIT_FEATURE_NSEC); + const char *http_parser = git_libgit2_feature_backend(GIT_FEATURE_HTTP_PARSER); + const char *regex = git_libgit2_feature_backend(GIT_FEATURE_REGEX); + const char *i18n = git_libgit2_feature_backend(GIT_FEATURE_I18N); + const char *ntlm = git_libgit2_feature_backend(GIT_FEATURE_AUTH_NTLM); + const char *negotiate = git_libgit2_feature_backend(GIT_FEATURE_AUTH_NEGOTIATE); + const char *compression = git_libgit2_feature_backend(GIT_FEATURE_COMPRESSION); + const char *sha1 = git_libgit2_feature_backend(GIT_FEATURE_SHA1); + const char *sha256 = git_libgit2_feature_backend(GIT_FEATURE_SHA256); + +#if defined(GIT_THREADS) && defined(GIT_WIN32) + cl_assert_equal_s("win32", threads); +#elif defined(GIT_THREADS) + cl_assert_equal_s("pthread", threads); +#else + cl_assert(threads == NULL); +#endif + +#if defined(GIT_HTTPS) && defined(GIT_OPENSSL) + cl_assert_equal_s("openssl", https); +#elif defined(GIT_HTTPS) && defined(GIT_OPENSSL_DYNAMIC) + cl_assert_equal_s("openssl-dynamic", https); +#elif defined(GIT_HTTPS) && defined(GIT_MBEDTLS) + cl_assert_equal_s("mbedtls", https); +#elif defined(GIT_HTTPS) && defined(GIT_SECURE_TRANSPORT) + cl_assert_equal_s("securetransport", https); +#elif defined(GIT_HTTPS) && defined(GIT_SCHANNEL) + cl_assert_equal_s("schannel", https); +#elif defined(GIT_HTTPS) && defined(GIT_WINHTTP) + cl_assert_equal_s("winhttp", https); +#elif defined(GIT_HTTPS) + cl_assert(0); +#else + cl_assert(https == NULL); +#endif + +#if defined(GIT_SSH) && defined(GIT_SSH_EXEC) + cl_assert_equal_s("exec", ssh); +#elif defined(GIT_SSH) && defined(GIT_SSH_LIBSSH2) + cl_assert_equal_s("libssh2", ssh); +#elif defined(GIT_SSH) + cl_assert(0); +#else + cl_assert(ssh == NULL); +#endif + +#if defined(GIT_USE_NSEC) && defined(GIT_USE_STAT_MTIMESPEC) + cl_assert_equal_s("mtimespec", nsec); +#elif defined(GIT_USE_NSEC) && defined(GIT_USE_STAT_MTIM) + cl_assert_equal_s("mtim", nsec); +#elif defined(GIT_USE_NSEC) && defined(GIT_USE_STAT_MTIME_NSEC) + cl_assert_equal_s("mtime", nsec); +#elif defined(GIT_USE_NSEC) && defined(GIT_WIN32) + cl_assert_equal_s("win32", nsec); +#elif defined(GIT_USE_NSEC) + cl_assert(0); +#else + cl_assert(nsec == NULL); +#endif + +#if defined(GIT_HTTPPARSER_HTTPPARSER) + cl_assert_equal_s("httpparser", http_parser); +#elif defined(GIT_HTTPPARSER_LLHTTP) + cl_assert_equal_s("llhttp", http_parser); +#elif defined(GIT_HTTPPARSER_BUILTIN) + cl_assert_equal_s("builtin", http_parser); +#else + cl_assert(0); +#endif + +#if defined(GIT_REGEX_REGCOMP_L) + cl_assert_equal_s("regcomp_l", regex); +#elif defined(GIT_REGEX_REGCOMP) + cl_assert_equal_s("regcomp", regex); +#elif defined(GIT_REGEX_PCRE) + cl_assert_equal_s("pcre", regex); +#elif defined(GIT_REGEX_PCRE2) + cl_assert_equal_s("pcre2", regex); +#elif defined(GIT_REGEX_BUILTIN) + cl_assert_equal_s("builtin", regex); +#else + cl_assert(0); +#endif + +#if defined(GIT_USE_ICONV) + cl_assert_equal_s("iconv", i18n); +#else + cl_assert(i18n == NULL); +#endif + +#if defined(GIT_NTLM) + cl_assert_equal_s("ntlmclient", ntlm); +#elif defined(GIT_WIN32) + cl_assert_equal_s("sspi", ntlm); +#else + cl_assert(ntlm == NULL); +#endif + +#if defined(GIT_GSSAPI) + cl_assert_equal_s("gssapi", negotiate); +#elif defined(GIT_WIN32) + cl_assert_equal_s("sspi", negotiate); +#else + cl_assert(negotiate == NULL); +#endif + +#if defined(GIT_COMPRESSION_BUILTIN) + cl_assert_equal_s("builtin", compression); +#elif defined(GIT_COMPRESSION_ZLIB) + cl_assert_equal_s("zlib", compression); +#else + cl_assert(0); +#endif + +#if defined(GIT_SHA1_COLLISIONDETECT) + cl_assert_equal_s("builtin", sha1); +#elif defined(GIT_SHA1_OPENSSL) + cl_assert_equal_s("openssl", sha1); +#elif defined(GIT_SHA1_OPENSSL_FIPS) + cl_assert_equal_s("openssl-fips", sha1); +#elif defined(GIT_SHA1_OPENSSL_DYNAMIC) + cl_assert_equal_s("openssl-dynamic", sha1); +#elif defined(GIT_SHA1_MBEDTLS) + cl_assert_equal_s("mbedtls", sha1); +#elif defined(GIT_SHA1_COMMON_CRYPTO) + cl_assert_equal_s("commoncrypto", sha1); +#elif defined(GIT_SHA1_WIN32) + cl_assert_equal_s("win32", sha1); +#else + cl_assert(0); +#endif + +#if defined(GIT_EXPERIMENTAL_SHA256) && defined(GIT_SHA256_BUILTIN) + cl_assert_equal_s("builtin", sha256); +#elif defined(GIT_EXPERIMENTAL_SHA256) && defined(GIT_SHA256_OPENSSL) + cl_assert_equal_s("openssl", sha256); +#elif defined(GIT_EXPERIMENTAL_SHA256) && defined(GIT_SHA256_OPENSSL_FIPS) + cl_assert_equal_s("openssl-fips", sha256); +#elif defined(GIT_EXPERIMENTAL_SHA256) && defined(GIT_SHA256_OPENSSL_DYNAMIC) + cl_assert_equal_s("openssl-dynamic", sha256); +#elif defined(GIT_EXPERIMENTAL_SHA256) && defined(GIT_SHA256_MBEDTLS) + cl_assert_equal_s("mbedtls", sha256); +#elif defined(GIT_EXPERIMENTAL_SHA256) && defined(GIT_SHA256_COMMON_CRYPTO) + cl_assert_equal_s("commoncrypto", sha256); +#elif defined(GIT_EXPERIMENTAL_SHA256) && defined(GIT_SHA256_WIN32) + cl_assert_equal_s("win32", sha256); +#elif defined(GIT_EXPERIMENTAL_SHA256) + cl_assert(0); +#else + cl_assert(sha256 == NULL); +#endif } diff --git a/tests/libgit2/core/version.c b/tests/libgit2/core/version.c index 32498c41d..9fd375c69 100644 --- a/tests/libgit2/core/version.c +++ b/tests/libgit2/core/version.c @@ -1,5 +1,15 @@ #include "clar_libgit2.h" +void test_core_version__query(void) +{ + int major, minor, rev; + + git_libgit2_version(&major, &minor, &rev); + cl_assert_equal_i(LIBGIT2_VERSION_MAJOR, major); + cl_assert_equal_i(LIBGIT2_VERSION_MINOR, minor); + cl_assert_equal_i(LIBGIT2_VERSION_REVISION, rev); +} + void test_core_version__check(void) { #if !LIBGIT2_VERSION_CHECK(1,6,3)