From 78f4f5b3470e10c93394327542411d9450c8eae7 Mon Sep 17 00:00:00 2001 From: Duncan Horn Date: Thu, 20 Nov 2025 15:19:23 -0800 Subject: [PATCH 1/5] Shared library --- CMakeLists.txt | 4 ++++ scripts/init.cmd | 20 ++++++++++++++++++-- scripts/init.sh | 15 ++++++++++++--- src/include/inflatelib.h | 38 +++++++++++++++++++++++++++++++++----- src/lib/CMakeLists.txt | 16 +++++++++++++++- test/CMakeLists.txt | 6 +++++- 6 files changed, 87 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f0fe9a..956cad2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,10 @@ project(inflatelib LANGUAGES C CXX ) +# "Production" options +option(INFLATELIB_BUILD_SHARED "Build inflatelib as a shared library" OFF) + +# "Test" options option(INFLATELIB_TEST "Build tests for local development and CI validation" ON) option(INFLATELIB_ASAN "Build with Address Sanitizer" OFF) option(INFLATELIB_UBSAN "Build with Undefined Behavior Sanitizer" OFF) diff --git a/scripts/init.cmd b/scripts/init.cmd index bf0ecb8..45c9d79 100644 --- a/scripts/init.cmd +++ b/scripts/init.cmd @@ -10,7 +10,7 @@ goto :init :usage echo USAGE: echo init.cmd [--help] [-c^|--compiler ^] [-g^|--generator ^] - echo [-b^|--build-type ^] [-i^|--install-prefix install/path] + echo [-b^|--build-type ^] [-d|--dll] [-i^|--install-prefix install/path] echo [-s^|--sanitize ^] [-f^|--fuzz] [-p^|--vcpkg path/to/vcpkg/root] echo. echo ARGUMENTS @@ -18,6 +18,8 @@ goto :init echo -g^|--generator Controls the CMake generator used, either 'ninja' (the default) or 'msbuild' echo -b^|--build-type Controls the value of 'CMAKE_BUILD_TYPE', either 'debug' (the default), 'release', echo 'relwithdebinfo', or 'minsizerel' + echo -d^|--dll When present, builds the library as a shared library (DLL), otherwise the library is + echo built as a static library. echo -i^|--install-prefix Specifies the path used for 'CMAKE_INSTALL_PREFIX'. If this argument is not specified, echo 'CMAKE_INSTALL_PREFIX' will not be passed to CMake during initialization. echo -s^|--sanitize Specifies the sanitizer to use, either 'address' or 'ub'. If this argument is not @@ -36,6 +38,7 @@ goto :init set COMPILER= set GENERATOR= set BUILD_TYPE= + set BUILD_SHARED=0 set INSTALL_PREFIX= set CMAKE_ARGS= set VCPKG_ROOT_PATH= @@ -94,6 +97,15 @@ goto :init goto :parse ) + set BUILD_SHARED_SET=0 + if /I "%~1"=="-d" set BUILD_SHARED_SET=1 + if /I "%~1"=="--dll" set BUILD_SHARED_SET=1 + if %BUILD_SHARED_SET%==1 ( + set BUILD_SHARED=1 + shift + goto :parse + ) + set INSTALL_PREFIX_SET=0 if /I "%~1"=="-i" set INSTALL_PREFIX_SET=1 if /I "%~1"=="--install-prefix" set INSTALL_PREFIX_SET=1 @@ -172,7 +184,7 @@ goto :init if "%VCPKG_ROOT_PATH%"=="" ( REM First check for %VCPKG_ROOT% variable if defined VCPKG_ROOT ( - set VCPKG_ROOT_PATH=%VCPKG_ROOT% + set "VCPKG_ROOT_PATH=%VCPKG_ROOT%" ) else ( REM Next check the PATH for vcpkg.exe for %%i in (vcpkg.exe) do set VCPKG_ROOT_PATH=%%~dp$PATH:i @@ -221,6 +233,10 @@ goto :init if %BUILD_TYPE%==minsizerel set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_BUILD_TYPE=MinSizeRel ) + if %BUILD_SHARED%==1 ( + set CMAKE_ARGS=%CMAKE_ARGS% -DINFLATELIB_BUILD_SHARED=ON + ) + set SUFFIX= if "%SANITIZER%"=="asan" ( set CMAKE_ARGS=%CMAKE_ARGS% -DINFLATELIB_ASAN=ON -DVCPKG_OVERLAY_TRIPLETS=..\..\..\vcpkg\triplets\asan diff --git a/scripts/init.sh b/scripts/init.sh index 7d74a13..a546d21 100755 --- a/scripts/init.sh +++ b/scripts/init.sh @@ -12,6 +12,7 @@ fi compiler= generator= buildType= +buildShared=false installPrefix= vcpkgRoot= sanitizer= @@ -20,7 +21,7 @@ cmakeArgs=() function show_help { echo "USAGE:" - echo " init.sh [-c ] [-b ] [-g ] [-i ] [-s ] [-f]" + echo " init.sh [-c ] [-b ] [-g ] [-o] [-i ] [-s ] [-f]" echo " [-p ]" echo echo "ARGUMENTS:" @@ -28,6 +29,7 @@ function show_help { echo " -b Specifies the value of 'CMAKE_BUILD_TYPE', either 'debug' (the default)," echo " 'release', 'relwithdebinfo', or 'minsizerel'" echo " -g Specifies the generator to use, either 'ninja' (the default) or 'make'" + echo " -o Builds the library as a shared library. Otherwise builds the library as a static library." echo " -i Specifies the path used for 'CMAKE_INSTALL_PREFIX'. If this argument is not specified," echo " 'CMAKE_INSTALL_PREFIX' will not be passed to CMake during initialization." echo " -s Specifies the sanitizer to use, either 'address' or 'undefined'. If this value is not" @@ -38,7 +40,7 @@ function show_help { echo " will be to check for the presence of the VCPKG_ROOT environment variable" } -while getopts :hc:b:g:i:s:fp: opt; do +while getopts :hc:b:g:oi:s:fp: opt; do if [ -n "${OPTARG}" ]; then arg=${OPTARG,,} fi @@ -93,6 +95,9 @@ while getopts :hc:b:g:i:s:fp: opt; do exit 1 fi ;; + o) + buildShared=true + ;; i) if [ "$installPrefix" != "" ]; then echo>&2 "Error: Install prefix already specified. Cannot specify more than one install prefix." @@ -137,7 +142,7 @@ while getopts :hc:b:g:i:s:fp: opt; do vcpkgRoot="$OPTARG" ;; *) - echo>&2 "ERROR: Invalid argument '-$opt'" + echo>&2 "ERROR: Invalid argument '-$OPTARG'" show_help exit 1 ;; @@ -196,6 +201,10 @@ elif [ "$buildType" == "minsizerel" ]; then cmakeArgs+=(-DCMAKE_BUILD_TYPE=MinSizeRel) fi +if [ "$buildShared" = true ]; then + cmakeArgs+=(-DINFLATELIB_BUILD_SHARED=ON) +fi + if [ "$installPrefix" != "" ]; then cmakeArgs+=("-DCMAKE_INSTALL_PREFIX=$installPrefix") fi diff --git a/src/include/inflatelib.h b/src/include/inflatelib.h index 3c034fe..e0886b3 100644 --- a/src/include/inflatelib.h +++ b/src/include/inflatelib.h @@ -11,6 +11,34 @@ #include +/* + * INFLATELIB_BUILD_SHARED The library is being built as a shared library + * INFLATELIB_CONSUME_SHARED The library is being consumed as a shared library + * Otherwise... The library is either being built as a static library or it is being consumed in an + * unspecified manner; either as a shared or static library. + */ +#ifdef INFLATELIB_BUILD_SHARED +#ifdef _WIN32 +#define INFLATELIB_EXPORT __declspec(dllexport) +#else +#define INFLATELIB_EXPORT +#endif +#elif defined(INFLATELIB_CONSUME_SHARED) +#ifdef _WIN32 +#define INFLATELIB_EXPORT __declspec(dllimport) +#else +#define INFLATELIB_EXPORT +#endif +#else +#define INFLATELIB_EXPORT /* Unknown or static - either way nothing to tag here */ +#endif + +#ifdef _WIN32 +#define INFLATELIB_CALLCONV __cdecl +#else +#define INFLATELIB_CALLCONV +#endif + #ifdef __cplusplus extern "C" { @@ -100,7 +128,7 @@ extern "C" * Initializes the stream. The 'user_data', 'alloc', and 'free' members MUST be set prior to the init call and MUST * NOT be changed after the init call completes. This function returns one of the status values specified above. */ - int inflatelib_init(inflatelib_stream* stream); + INFLATELIB_EXPORT int INFLATELIB_CALLCONV inflatelib_init(inflatelib_stream* stream); /* * Resets the stream's state back to its initialized state. This allows the stream to be reused for multiple inflate @@ -108,24 +136,24 @@ extern "C" * call returns EOF, however it can also be used to reset the stream after an error or even during the middle of * inflating a stream. This function also allows the caller to switch between Deflate and Deflate64. */ - int inflatelib_reset(inflatelib_stream* stream); + INFLATELIB_EXPORT int INFLATELIB_CALLCONV inflatelib_reset(inflatelib_stream* stream); /* * Cleans up any data allocated/initialized by the library. This function must be called if 'inflatelib_init' * returns success. After this function is called, the 'inflatelib_stream' cannot be used for any function call * unless 'inflatelib_init' is called again. This function can only return success. */ - int inflatelib_destroy(inflatelib_stream* stream); + INFLATELIB_EXPORT int INFLATELIB_CALLCONV inflatelib_destroy(inflatelib_stream* stream); /* * */ - int inflatelib_inflate(inflatelib_stream* stream); + INFLATELIB_EXPORT int INFLATELIB_CALLCONV inflatelib_inflate(inflatelib_stream* stream); /* * */ - int inflatelib_inflate64(inflatelib_stream* stream); + INFLATELIB_EXPORT int INFLATELIB_CALLCONV inflatelib_inflate64(inflatelib_stream* stream); #ifdef __cplusplus } // extern "C" diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index 7ae37aa..cd815c5 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -2,7 +2,19 @@ include(CMakePackageConfigHelpers) include(GNUInstallDirs) -add_library(inflatelib) +if (${INFLATELIB_BUILD_SHARED}) + add_library(inflatelib SHARED) + + target_compile_definitions(inflatelib + PRIVATE + INFLATELIB_BUILD_SHARED + INTERFACE + INFLATELIB_CONSUME_SHARED + ) +else() + add_library(inflatelib STATIC) +endif() + add_library(inflatelib::inflatelib ALIAS inflatelib) target_compile_features(inflatelib @@ -15,6 +27,8 @@ target_compile_options(inflatelib ${SANITIZER_FLAGS} ) +set_property(TARGET inflatelib PROPERTY POSITION_INDEPENDENT_CODE ON) + target_link_libraries(inflatelib INTERFACE ${SANITIZER_LIBS} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 1f502f0..a53fb8b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -19,7 +19,11 @@ if (BUILD_TEST_FILES) endif() if (NOT INFLATELIB_FUZZ) - add_subdirectory(cpp) + # Building as a shared library does not export internal symbols, which we test in the unit tests. It's still useful + # to test building as a shared library, so just exclude the unit tests in this configuration + if (NOT INFLATELIB_BUILD_SHARED) + add_subdirectory(cpp) + endif() # The perf tests are pretty useless when sanitizers are enabled. Note that the tests will consume the same input as the # perf tests, so it's not like we're missing potential coverage here. From e241c8378274374bab7bbec6f2fe191d0c6467bd Mon Sep 17 00:00:00 2001 From: Duncan Horn Date: Mon, 1 Dec 2025 15:42:31 -0800 Subject: [PATCH 2/5] Visibility attribute --- src/include/inflatelib.h | 20 ++++++++++++-------- src/lib/CMakeLists.txt | 6 +++++- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/include/inflatelib.h b/src/include/inflatelib.h index e0886b3..284b0a5 100644 --- a/src/include/inflatelib.h +++ b/src/include/inflatelib.h @@ -11,29 +11,33 @@ #include +#ifdef __has_attribute +#if __has_attribute(visibility) +#define INFLATELIB_HAS_VISIBILITY_ATTR 1 +#endif +#endif + /* * INFLATELIB_BUILD_SHARED The library is being built as a shared library * INFLATELIB_CONSUME_SHARED The library is being consumed as a shared library * Otherwise... The library is either being built as a static library or it is being consumed in an * unspecified manner; either as a shared or static library. */ +#if defined(_WIN32) && defined(_MSC_VER) #ifdef INFLATELIB_BUILD_SHARED -#ifdef _WIN32 #define INFLATELIB_EXPORT __declspec(dllexport) -#else -#define INFLATELIB_EXPORT -#endif #elif defined(INFLATELIB_CONSUME_SHARED) -#ifdef _WIN32 #define INFLATELIB_EXPORT __declspec(dllimport) #else -#define INFLATELIB_EXPORT +#define INFLATELIB_EXPORT /* Unknown or static - don't decorate function declarations */ #endif +#elif INFLATELIB_HAS_VISIBILITY_ATTR +#define INFLATELIB_EXPORT __attribute__((visibility("default"))) #else -#define INFLATELIB_EXPORT /* Unknown or static - either way nothing to tag here */ +#define INFLATELIB_EXPORT /* Not Windows and no visibility attribute... dont' decorate function declarations */ #endif -#ifdef _WIN32 +#if defined(_WIN32) && defined(_MSC_VER) #define INFLATELIB_CALLCONV __cdecl #else #define INFLATELIB_CALLCONV diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index cd815c5..2b57a1a 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -27,7 +27,11 @@ target_compile_options(inflatelib ${SANITIZER_FLAGS} ) -set_property(TARGET inflatelib PROPERTY POSITION_INDEPENDENT_CODE ON) +set_target_properties(inflatelib PROPERTIES + POSITION_INDEPENDENT_CODE ON + C_VISIBILITY_PRESET hidden + VISIBILITY_INLINES_HIDDEN ON + ) target_link_libraries(inflatelib INTERFACE From 4e6dbec96cd351b95271564e3efffabdcb182ffd Mon Sep 17 00:00:00 2001 From: Duncan Horn Date: Tue, 2 Dec 2025 11:08:40 -0800 Subject: [PATCH 3/5] debug postfix --- src/lib/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index 2b57a1a..67f8117 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -31,6 +31,7 @@ set_target_properties(inflatelib PROPERTIES POSITION_INDEPENDENT_CODE ON C_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN ON + DEBUG_POSTFIX "d" ) target_link_libraries(inflatelib From 9de9e0d0f0380ea9fa49a921300191071e24adc5 Mon Sep 17 00:00:00 2001 From: Duncan Horn Date: Tue, 2 Dec 2025 23:07:28 -0800 Subject: [PATCH 4/5] Versioning and binary info --- src/lib/CMakeLists.txt | 3 +++ src/lib/inflatelib.rc | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 src/lib/inflatelib.rc diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index 67f8117..5912e91 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -32,6 +32,8 @@ set_target_properties(inflatelib PROPERTIES C_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN ON DEBUG_POSTFIX "d" + SOVERSION ${CMAKE_PROJECT_VERSION_MAJOR} + VERSION ${CMAKE_PROJECT_VERSION} ) target_link_libraries(inflatelib @@ -55,6 +57,7 @@ target_sources(inflatelib huffman_tree.c inflate.c window.c + $<$,$>:inflatelib.rc> ) # Installation diff --git a/src/lib/inflatelib.rc b/src/lib/inflatelib.rc new file mode 100644 index 0000000..33650df --- /dev/null +++ b/src/lib/inflatelib.rc @@ -0,0 +1,40 @@ +#include +#include + +//--------------------------------------------------------------- +// Version Information +//--------------------------------------------------------------- +VS_VERSION_INFO VERSIONINFO + FILEVERSION INFLATELIB_VERSION_MAJOR,INFLATELIB_VERSION_MINOR,INFLATELIB_VERSION_PATCH,0 + PRODUCTVERSION INFLATELIB_VERSION_MAJOR,INFLATELIB_VERSION_MINOR,INFLATELIB_VERSION_PATCH,0 + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS 1 +#else + FILEFLAGS 0 +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" // US English + ANSI Latin 1 + BEGIN + VALUE "CompanyName", "Microsoft Corporation\0" + VALUE "FileDescription", "InflateLib decompression library\0" + VALUE "FileVersion", INFLATELIB_VERSION_STRING "\0" + VALUE "InternalName", "inflatelib.dll\0" + VALUE "LegalCopyright", "Copyright (c) Microsoft Corporation. All rights reserved.\0" + VALUE "OriginalFilename", "inflatelib.dll\0" + VALUE "ProductName", "InflateLib\0" + VALUE "ProductVersion", INFLATELIB_VERSION_STRING "\0" + END + END + + BLOCK "VarFileInfo" + BEGIN + // Language ID, Code Page (0409 = US English, 1252 = ANSI Latin 1; Western European) + VALUE "Translation", 0x0409, 1252 + END +END From 7cabb32f8bfae51ebf41b29b0b04c7141efeb81d Mon Sep 17 00:00:00 2001 From: Duncan Horn <40036384+dunhor@users.noreply.github.com> Date: Wed, 3 Dec 2025 10:46:46 -0800 Subject: [PATCH 5/5] Apply suggestions from code review --- src/include/inflatelib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/include/inflatelib.h b/src/include/inflatelib.h index 284b0a5..59e1aeb 100644 --- a/src/include/inflatelib.h +++ b/src/include/inflatelib.h @@ -34,7 +34,7 @@ #elif INFLATELIB_HAS_VISIBILITY_ATTR #define INFLATELIB_EXPORT __attribute__((visibility("default"))) #else -#define INFLATELIB_EXPORT /* Not Windows and no visibility attribute... dont' decorate function declarations */ +#define INFLATELIB_EXPORT /* Not Windows and no visibility attribute... don't decorate function declarations */ #endif #if defined(_WIN32) && defined(_MSC_VER)