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..59e1aeb 100644 --- a/src/include/inflatelib.h +++ b/src/include/inflatelib.h @@ -11,6 +11,38 @@ #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 +#define INFLATELIB_EXPORT __declspec(dllexport) +#elif defined(INFLATELIB_CONSUME_SHARED) +#define INFLATELIB_EXPORT __declspec(dllimport) +#else +#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 /* Not Windows and no visibility attribute... don't decorate function declarations */ +#endif + +#if defined(_WIN32) && defined(_MSC_VER) +#define INFLATELIB_CALLCONV __cdecl +#else +#define INFLATELIB_CALLCONV +#endif + #ifdef __cplusplus extern "C" { @@ -100,7 +132,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 +140,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..5912e91 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,15 @@ target_compile_options(inflatelib ${SANITIZER_FLAGS} ) +set_target_properties(inflatelib PROPERTIES + POSITION_INDEPENDENT_CODE ON + C_VISIBILITY_PRESET hidden + VISIBILITY_INLINES_HIDDEN ON + DEBUG_POSTFIX "d" + SOVERSION ${CMAKE_PROJECT_VERSION_MAJOR} + VERSION ${CMAKE_PROJECT_VERSION} + ) + target_link_libraries(inflatelib INTERFACE ${SANITIZER_LIBS} @@ -36,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 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.