diff --git a/.cmake-format.json b/.cmake-format.json
new file mode 100644
index 000000000..a4ae98bf4
--- /dev/null
+++ b/.cmake-format.json
@@ -0,0 +1,10 @@
+{
+    "format": {
+        "tab_size": 4,
+        "xcommand_case": "lower",
+        "xkeyword_case": "upper"
+    },
+    "markup": {
+        "first_comment_is_literal": true
+    }
+}
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0e928c9e2..925d840d1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,338 +1,193 @@
 # Copyright (c) 2017 - 2021 LiteSpeed Technologies Inc.  See LICENSE.
-cmake_minimum_required(VERSION 3.0...3.23)
+cmake_minimum_required(VERSION 3.1...3.23)
 
+project(lsquic C)
 
-PROJECT(lsquic C)
+option(LSQUIC_FIU "Use Fault Injection in Userspace (FIU)" OFF)
+option(LSQUIC_BIN "Compile example binaries that use the library" ON)
+option(LSQUIC_TESTS "Compile library unit tests" ON)
+option(LSQUIC_DEVEL "Compile in development mode" OFF)
+option(LSQUIC_DEBUG_NEXT_ADV_TICK "Define LSQUIC_DEBUG_NEXT_ADV_TICK to 1" ON)
+option(LSQUIC_CONN_STATS "Track and print some connection stats" ON)
+option(LSQUIC_ENABLE_HANDSHAKE_DISABLE "Disable crypto (plaintext only)" OFF)
+option(LSQUIC_COMPILE_OUT_DEBUG_MESSAGES "Disable debug messages" OFF)
+option(LSQUIC_SANITIZER "Enable address sanitizer" OFF)
+option(BUILD_SHARED_LIBS "Compile as shared library" OFF)
 
-OPTION(LSQUIC_FIU "Use Fault Injection in Userspace (FIU)" OFF)
-OPTION(LSQUIC_BIN "Compile example binaries that use the library" ON)
-OPTION(LSQUIC_TESTS "Compile library unit tests" ON)
-OPTION(LSQUIC_SHARED_LIB "Compile as shared librarry" OFF)
-OPTION(LSQUIC_DEVEL "Compile in development mode" OFF)
+include(GNUInstallDirs)
 
-INCLUDE(GNUInstallDirs)
+message(STATUS "CMake v${CMAKE_VERSION}")
 
-MESSAGE(STATUS "CMake v${CMAKE_VERSION}")
-
-IF (CMAKE_SYSTEM_NAME STREQUAL "Linux")
+set(NEED_LIBRT_FOR_clock_getres OFF)
+if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
     # If using older glibc, need to link with -lrt.  See clock_getres(2).
-    EXECUTE_PROCESS(
-        COMMAND ${PROJECT_SOURCE_DIR}/print-glibc-version.sh ${CMAKE_C_COMPILER}
-        OUTPUT_VARIABLE GLIBC_VERSION)
-    IF(NOT GLIBC_VERSION EQUAL "" AND GLIBC_VERSION VERSION_LESS 2.17)
-        SET(LIBS ${LIBS} rt)
-    ENDIF()
-ELSEIF (CMAKE_SYSTEM_NAME STREQUAL "Android")
+    include(CheckSymbolExists)
+    check_symbol_exists(clock_getres "time.h" HAS_clock_getres_WITHOUT_LIBRT)
+
+    if(NOT HAS_clock_getres_WITHOUT_LIBRT)
+        find_library(RT_LIBRARY rt)
+        set(NEED_LIBRT_FOR_clock_getres ON)
+    endif()
+elseif(CMAKE_SYSTEM_NAME STREQUAL "Android")
     # for android-ndk >= r19b
     set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY "BOTH")
     set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE "BOTH")
     set(CMAKE_FIND_ROOT_PATH_MODE_PATH "BOTH")
-ENDIF()
-
-IF("${CMAKE_BUILD_TYPE}" STREQUAL "")
-    SET(CMAKE_BUILD_TYPE Debug)
-ENDIF()
-
-MESSAGE(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
-
-IF (NOT "$ENV{EXTRA_CFLAGS}" MATCHES "-DLSQUIC_DEBUG_NEXT_ADV_TICK")
-    SET(MY_CMAKE_FLAGS "-DLSQUIC_DEBUG_NEXT_ADV_TICK=1")
-ENDIF()
-
-IF (NOT "$ENV{EXTRA_CFLAGS}" MATCHES "-DLSQUIC_CONN_STATS=")
-    SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -DLSQUIC_CONN_STATS=1")
-ENDIF()
-
-IF (NOT MSVC)
-
-SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -Wall -Wextra -Wno-unused-parameter")
-SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -fno-omit-frame-pointer")
+endif()
 
-IF(CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9.3)
-    SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -Wno-missing-field-initializers")
-ENDIF()
+if(NOT MSVC)
+    include(CheckCCompilerFlag)
+    check_c_compiler_flag(-Wno-implicit-fallthrough HAS_NO_IMPLICIT_FALLTHROUGH)
 
-IF(LSQUIC_FIU)
-    SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -DFIU_ENABLE=1")
-    SET(LIBS ${LIBS} fiu)
-ENDIF()
+    set(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -Wall -Wextra -Wno-unused-parameter")
 
-IF(CMAKE_BUILD_TYPE STREQUAL "Debug")
-    SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -O0 -g3")
-    IF(CMAKE_C_COMPILER MATCHES "clang" AND
-                        NOT "$ENV{TRAVIS}" MATCHES "^true$" AND
-                        NOT "$ENV{EXTRA_CFLAGS}" MATCHES "-fsanitize")
-        SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -fsanitize=address")
-        SET(LIBS ${LIBS} -fsanitize=address)
-    ENDIF()
-    # Uncomment to enable cleartext protocol mode (no crypto):
-    #SET (MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -DLSQUIC_ENABLE_HANDSHAKE_DISABLE=1")
-ELSE()
-    SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -O3 -g0")
-    # Comment out the following line to compile out debug messages:
-    #SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -DLSQUIC_LOWEST_LOG_LEVEL=LSQ_LOG_INFO")
-ENDIF()
+    if(CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS
+                                   4.9.3)
+        set(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -Wno-missing-field-initializers")
+    endif()
 
-IF (LSQUIC_DEVEL)
-    SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -DLSQUIC_DEVEL=1")
-ENDIF()
+    if(CMAKE_BUILD_TYPE STREQUAL "Debug")
+        set(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -O0 -g3")
+    else()
+        set(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -O3 -g0")
+    endif()
 
-IF(LSQUIC_PROFILE EQUAL 1)
-    SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -g -pg")
-ENDIF()
+    if(LSQUIC_SANITIZER)
+        message(STATUS "AddressSanitizer is ON")
+    else()
+        message(STATUS "AddressSanitizer is OFF")
+    endif()
 
-IF(LSQUIC_COVERAGE EQUAL 1)
-    SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -fprofile-arcs -ftest-coverage")
-ENDIF()
-
-IF(MY_CMAKE_FLAGS MATCHES "fsanitize=address")
-    MESSAGE(STATUS "AddressSanitizer is ON")
-ELSE()
-    MESSAGE(STATUS "AddressSanitizer is OFF")
-ENDIF()
-
-#MSVC
-ELSE()
-SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} /wd4100")	# unreferenced formal parameter
-SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} /wd4115")	# unnamed type definition in parentheses
-SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} /wd4116")	# named type definition in parentheses
-SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} /wd4146")	# unary minus operator applied to unsigned type, result still unsigned
-SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} /wd4132")	# const initialization
-SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} /wd4200")	# zero-sized array
-SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} /wd4204")	# non-constant aggregate initializer
-SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} /wd4244")	# integer conversion
-SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} /wd4245")	# conversion from 'int' to 'unsigned int', signed/unsigned mismatch
-SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} /wd4267")	# integer conversion
-SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} /wd4214")	# nonstandard extension used: bit field types other than int
-SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} /wd4295")	# array is too small to include a terminating null character
-SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} /wd4334")	# result of 32-bit shift implicitly converted to 64 bits (was 64-bit shift intended?)
-SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} /wd4456")	# hide previous local declaration
-SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} /wd4459")	# hide global declaration
-SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} /wd4706")	# assignment within conditional expression
-SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} /wd4090")	# different 'const' qualifier (TODO: debug ls-sfparser.c)
-SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} /wd4305")	# truncation from double to float
-SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -W4 -WX -Zi -DWIN32_LEAN_AND_MEAN -DNOMINMAX -D_CRT_SECURE_NO_WARNINGS -I${CMAKE_CURRENT_SOURCE_DIR}/wincompat")
-IF(LSQUIC_SHARED_LIB)
-    SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -DLSQUIC_SHARED_LIB")
-ENDIF()
-IF(CMAKE_BUILD_TYPE STREQUAL "Debug")
-    SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -Od")
-    #SET (MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -DFIU_ENABLE=1")
-    #SET(LIBS ${LIBS} fiu)
-ELSE()
-    SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -Ox")
-    # Comment out the following line to compile out debug messages:
-    #SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -DLSQUIC_LOWEST_LOG_LEVEL=LSQ_LOG_INFO")
-ENDIF()
-
-ENDIF() #MSVC
+endif()
 
 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}  ${MY_CMAKE_FLAGS} $ENV{EXTRA_CFLAGS}")
 
-MESSAGE(STATUS "Compiler flags: ${CMAKE_C_FLAGS}")
+message(STATUS "Compiler flags: ${CMAKE_C_FLAGS}")
 
 find_package(Perl)
-IF(NOT PERL_FOUND)
-    MESSAGE(FATAL_ERROR "Perl not found -- need it to generate source code")
-ENDIF()
-
-IF (MSVC)
-    IF(LSQUIC_SHARED_LIB)
-        set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS YES CACHE BOOL "Export all symbols")
-        SET(LIB_SUFFIX .dll)
-    ELSE()
-        SET(LIB_SUFFIX .lib)
-    ENDIF()
-ELSE()
-    IF(LSQUIC_SHARED_LIB)
-        SET(LIB_SUFFIX .so)
-    ELSE()
-        SET(LIB_SUFFIX .a)
-    ENDIF()
-ENDIF()
-
-IF (NOT DEFINED BORINGSSL_INCLUDE AND DEFINED BORINGSSL_DIR)
-    FIND_PATH(BORINGSSL_INCLUDE NAMES openssl/ssl.h
-                PATHS ${BORINGSSL_DIR}/include
-                NO_DEFAULT_PATH)
-ENDIF()
-# This must be done before adding other include directories to take
-# precedence over header files from other SSL installs.
-
-IF (BORINGSSL_INCLUDE)
-    MESSAGE(STATUS "BoringSSL include directory ${BORINGSSL_INCLUDE}")
-    INCLUDE_DIRECTORIES(${BORINGSSL_INCLUDE})
-ELSE()
-    MESSAGE(FATAL_ERROR "BoringSSL headers not found")
-ENDIF()
-
-IF (NOT DEFINED BORINGSSL_LIB AND DEFINED BORINGSSL_DIR)
-    FOREACH(LIB_NAME ssl crypto decrepit)
-        IF (CMAKE_SYSTEM_NAME STREQUAL Windows)
-            FIND_LIBRARY(BORINGSSL_LIB_${LIB_NAME}
-                NAMES ${LIB_NAME}
-                PATHS ${BORINGSSL_DIR}/${LIB_NAME}
-		PATH_SUFFIXES Debug Release MinSizeRel RelWithDebInfo
-                NO_DEFAULT_PATH)
-        ELSE()
-            FIND_LIBRARY(BORINGSSL_LIB_${LIB_NAME}
-                NAMES lib${LIB_NAME}${LIB_SUFFIX}
-                PATHS ${BORINGSSL_DIR}/${LIB_NAME}
-                NO_DEFAULT_PATH)
-        ENDIF()
-        IF(BORINGSSL_LIB_${LIB_NAME})
-            MESSAGE(STATUS "Found ${LIB_NAME} library: ${BORINGSSL_LIB_${LIB_NAME}}")
-        ELSE()
-            MESSAGE(STATUS "${LIB_NAME} library not found")
-        ENDIF()
-    ENDFOREACH()
-
-ELSE()
-
-
-    FOREACH(LIB_NAME ssl crypto decrepit)
-        # If BORINGSSL_LIB is defined, try find each lib. Otherwise, user should define BORINGSSL_LIB_ssl,
-        # BORINGSSL_LIB_crypto and so on explicitly. For example, including boringssl and lsquic both via
-        # add_subdirectory:
-        #   add_subdirectory(third_party/boringssl)
-        #   set(BORINGSSL_LIB_ssl ssl)
-        #   set(BORINGSSL_LIB_crypto crypto)
-        #   set(BORINGSSL_LIB_decrepit decrepit)
-        #   add_subdirectory(third_party/lsquic)
-        IF (DEFINED BORINGSSL_LIB)
-            IF (CMAKE_SYSTEM_NAME STREQUAL Windows)
-                FIND_LIBRARY(BORINGSSL_LIB_${LIB_NAME}
-                    NAMES ${LIB_NAME}
-                    PATHS ${BORINGSSL_LIB}
-                    PATH_SUFFIXES Debug Release MinSizeRel RelWithDebInfo
-                    NO_DEFAULT_PATH)
-            ELSE()
-                FIND_LIBRARY(BORINGSSL_LIB_${LIB_NAME}
-                    NAMES lib${LIB_NAME}${LIB_SUFFIX}
-                    PATHS ${BORINGSSL_LIB}
-                    PATH_SUFFIXES ${LIB_NAME}
-                    NO_DEFAULT_PATH)
-            ENDIF()
-        ENDIF()
-        IF(BORINGSSL_LIB_${LIB_NAME})
-            MESSAGE(STATUS "Found ${LIB_NAME} library: ${BORINGSSL_LIB_${LIB_NAME}}")
-        ELSE()
-            MESSAGE(FATAL_ERROR "BORINGSSL_LIB_${LIB_NAME} library not found")
-        ENDIF()
-    ENDFOREACH()
-
-ENDIF()
-
-SET(CMAKE_INCLUDE_CURRENT_DIR ON)
-INCLUDE_DIRECTORIES(include)
-IF(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR CMAKE_SYSTEM_NAME STREQUAL "Darwin")
+if(NOT PERL_FOUND)
+    message(FATAL_ERROR "Perl not found -- need it to generate source code")
+endif()
+
+if(BUILD_SHARED_LIBS)
+    if(MSVC)
+        set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS
+            YES
+            CACHE BOOL "Export all symbols")
+        set(LIB_SUFFIX ${CMAKE_IMPORT_LIBRARY_SUFFIX})
+    else()
+        set(LIB_SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX})
+    endif()
+else()
+    set(LIB_SUFFIX ${CMAKE_STATIC_LIBRARY_SUFFIX})
+endif()
+
+if(NOT DEFINED BORINGSSL_INCLUDE AND DEFINED BORINGSSL_DIR)
+    find_path(
+        BORINGSSL_INCLUDE
+        NAMES openssl/ssl.h
+        PATHS ${BORINGSSL_DIR}/include
+        NO_DEFAULT_PATH)
+endif()
+# This must be done before adding other include directories to take precedence
+# over header files from other SSL installs.
+
+if(BORINGSSL_INCLUDE)
+    message(STATUS "BoringSSL include directory ${BORINGSSL_INCLUDE}")
+    include_directories(${BORINGSSL_INCLUDE})
+else()
+    message(FATAL_ERROR "BoringSSL headers not found")
+endif()
+
+foreach(LIB_NAME ssl crypto decrepit)
+    find_library(
+        BORINGSSL_LIB_${LIB_NAME}
+        NAMES ${LIB_NAME} lib${LIB_NAME}
+        PATHS ${BORINGSSL_DIR}/${LIB_NAME} ${BORINGSSL_LIB}
+        PATH_SUFFIXES Debug Release MinSizeRel RelWithDebInfo
+        NO_DEFAULT_PATH)
+    if(BORINGSSL_LIB_${LIB_NAME})
+        message(
+            STATUS "Found ${LIB_NAME} library: ${BORINGSSL_LIB_${LIB_NAME}}")
+    else()
+        message(STATUS "${LIB_NAME} library not found")
+    endif()
+endforeach()
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+include_directories(include)
+if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR CMAKE_SYSTEM_NAME STREQUAL "Darwin")
     # Find libevent on FreeBSD:
-    include_directories( /usr/local/include )
-    link_directories( /usr/local/lib )
-ENDIF()
-
-IF (CMAKE_SYSTEM_NAME STREQUAL Windows)
-    FIND_PATH(GETOPT_INCLUDE_DIR NAMES getopt.h)
-    IF (GETOPT_INCLUDE_DIR)
-        INCLUDE_DIRECTORIES(${GETOPT_INCLUDE_DIR})
-    ELSE()
-        MESSAGE(FATAL_ERROR "getopt.h was not found")
-    ENDIF()
-        FIND_LIBRARY(GETOPT_LIB getopt)
-    IF(GETOPT_LIB)
-        MESSAGE(STATUS "Found getopt: ${GETOPT_LIB}")
-    ELSE()
-        MESSAGE(STATUS "getopt not found")
-    ENDIF()
-ENDIF()
-
-# Find zlib and libevent header files and library files
-# TODO: libevent is not strictly necessary to build the library.
-FIND_PATH(ZLIB_INCLUDE_DIR NAMES zlib.h)
-IF (ZLIB_INCLUDE_DIR)
-    INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIR})
-ELSE()
-    MESSAGE(FATAL_ERROR "zlib.h was not found")
-ENDIF()
-IF (CMAKE_SYSTEM_NAME STREQUAL Windows)
-    FIND_LIBRARY(ZLIB_LIB zlib)
-ELSEIF(CMAKE_SYSTEM_NAME STREQUAL Darwin)
-    # XXX somehow FIND_LIBRARY() does not find zlib on Travis?
-    SET(ZLIB_LIB z)
-ELSE()
-    FIND_LIBRARY(ZLIB_LIB libz${LIB_SUFFIX})
-ENDIF()
-IF(ZLIB_LIB)
-    MESSAGE(STATUS "Found zlib: ${ZLIB_LIB}")
-ELSE()
-    MESSAGE(STATUS "zlib not found")
-ENDIF()
-
-SET(LIBS lsquic ${BORINGSSL_LIB_ssl} ${BORINGSSL_LIB_crypto} ${ZLIB_LIB} ${LIBS})
-
-IF (LSQUIC_BIN)
-    FIND_PATH(EVENT_INCLUDE_DIR NAMES event2/event.h)
-    IF (EVENT_INCLUDE_DIR)
-        INCLUDE_DIRECTORIES(${EVENT_INCLUDE_DIR})
-    ELSE()
-        MESSAGE(WARNING "event2/event.h was not found: binaries won't be built")
-        SET(LSQUIC_BIN OFF)
-    ENDIF()
-ENDIF()
-
-IF (LSQUIC_BIN)
-    IF (CMAKE_SYSTEM_NAME STREQUAL Windows)
-        FIND_LIBRARY(EVENT_LIB event)
-    ELSE()
-        FIND_LIBRARY(EVENT_LIB libevent${LIB_SUFFIX})
-        IF(NOT EVENT_LIB)
-            FIND_LIBRARY(EVENT_LIB libevent.so)
-        ENDIF()
-    ENDIF()
-    IF(EVENT_LIB)
-        MESSAGE(STATUS "Found event: ${EVENT_LIB}")
-    ELSE()
-        MESSAGE(WARNING "libevent not found: binaries won't be built")
-        SET(LSQUIC_BIN OFF)
-    ENDIF()
-ENDIF()
-
-
-IF (NOT MSVC)
-    LIST(APPEND LIBS pthread m)
-ELSE()
-    LIST(APPEND LIBS ws2_32)
-ENDIF()
-
-IF (LSQUIC_BIN)
-    ADD_SUBDIRECTORY(bin)
-ENDIF()
+    include_directories(/usr/local/include)
+    link_directories(/usr/local/lib)
+endif()
+
+if(CMAKE_SYSTEM_NAME STREQUAL Windows)
+    find_path(GETOPT_INCLUDE_DIR NAMES getopt.h)
+    if(GETOPT_INCLUDE_DIR)
+        include_directories(${GETOPT_INCLUDE_DIR})
+    else()
+        message(FATAL_ERROR "getopt.h was not found")
+    endif()
+    find_library(GETOPT_LIB getopt)
+    if(GETOPT_LIB)
+        message(STATUS "Found getopt: ${GETOPT_LIB}")
+    else()
+        message(STATUS "getopt not found")
+    endif()
+endif()
+
+# Find zlib and libevent header files and library files TODO: libevent is not
+# strictly necessary to build the library.
+find_package(ZLIB REQUIRED)
+
+set(LIBS lsquic ${BORINGSSL_LIB_ssl} ${BORINGSSL_LIB_crypto} ${ZLIB_LIB})
+
+if(LSQUIC_BIN)
+    find_path(EVENT_INCLUDE_DIR NAMES event2/event.h)
+    if(NOT EVENT_INCLUDE_DIR)
+        message(WARNING "event2/event.h was not found: binaries won't be built")
+        set(LSQUIC_BIN OFF)
+    endif()
+endif()
+
+if(LSQUIC_BIN)
+    find_library(EVENT_LIB NAMES event libevent)
+    if(EVENT_LIB)
+        message(STATUS "Found event: ${EVENT_LIB}")
+    else()
+        message(WARNING "libevent not found: binaries won't be built")
+        set(LSQUIC_BIN OFF)
+    endif()
+endif()
+find_package(Threads REQUIRED)
+
+if(NOT MSVC)
+    list(APPEND LIBS m)
+else()
+    list(APPEND LIBS ws2_32)
+endif()
+
+if(LSQUIC_BIN)
+    add_subdirectory(bin)
+endif()
 
 add_subdirectory(src)
 
-IF(LSQUIC_TESTS AND CMAKE_BUILD_TYPE STREQUAL "Debug")
+if(LSQUIC_TESTS AND CMAKE_BUILD_TYPE STREQUAL "Debug")
     # Our test framework relies on assertions, only compile if assertions are
     # enabled.
     #
     enable_testing()
     add_subdirectory(tests)
-ENDIF()
-
+endif()
 
-FIND_PROGRAM(SPHINX NAMES sphinx-build)
-IF(SPHINX)
-    ADD_CUSTOM_TARGET(docs
-        ${SPHINX} -b html
-        docs
-        docs/_build
-    )
-ELSE()
-    MESSAGE(STATUS "sphinx-build not found: docs won't be made")
-ENDIF()
+find_program(SPHINX NAMES sphinx-build)
+if(SPHINX)
+    add_custom_target(docs ${SPHINX} -b html docs docs/_build)
+else()
+    message(STATUS "sphinx-build not found: docs won't be made")
+endif()
 
-INSTALL(FILES
-    include/lsquic.h
-    include/lsquic_types.h
-    include/lsxpack_header.h
-    DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/lsquic
-)
+install(FILES include/lsquic.h include/lsquic_types.h include/lsxpack_header.h
+        DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/lsquic)
diff --git a/bin/CMakeLists.txt b/bin/CMakeLists.txt
index 34f9cacf1..414ad331a 100644
--- a/bin/CMakeLists.txt
+++ b/bin/CMakeLists.txt
@@ -1,114 +1,90 @@
 # Copyright (c) 2017 - 2021 LiteSpeed Technologies Inc.  See LICENSE.
 
 include_directories(${CMAKE_CURRENT_BINARY_DIR})
-LIST(APPEND LIBS ${EVENT_LIB})
-
-IF(MSVC)
-    FIND_LIBRARY(PCRE_LIB pcre)
-    IF(PCRE_LIB)
-        MESSAGE(STATUS "Found pcre: ${PCRE_LIB}")
-        LIST(APPEND LIBS ${PCRE_LIB})
-    ELSE()
-        MESSAGE(STATUS "pcre not found: http_server won't work")
-    ENDIF()
-    FIND_LIBRARY(PCREPOSIX_LIB pcreposix)
-    IF(PCREPOSIX_LIB)
-        MESSAGE(STATUS "Found pcreposix: ${PCREPOSIX_LIB}")
-        LIST(APPEND LIBS ${PCREPOSIX_LIB})
-    ELSE()
-        MESSAGE(STATUS "pcreposix not found: http_server won't work")
-    ENDIF()
-    LIST(APPEND LIBS ws2_32)
-    LIST(APPEND LIBS iphlpapi)
-    LIST(APPEND LIBS ${GETOPT_LIB})
-ENDIF()
-
-add_executable(http_server http_server.c prog.c test_common.c test_cert.c)
-IF(NOT MSVC)   #   TODO: port MD5 server and client to Windows
-add_executable(md5_server md5_server.c prog.c test_common.c test_cert.c)
-add_executable(md5_client md5_client.c prog.c test_common.c test_cert.c)
-ENDIF()
-add_executable(echo_server echo_server.c prog.c test_common.c test_cert.c)
-add_executable(echo_client echo_client.c prog.c test_common.c test_cert.c)
-add_executable(duck_server duck_server.c prog.c test_common.c test_cert.c)
-add_executable(duck_client duck_client.c prog.c test_common.c test_cert.c)
-add_executable(perf_client perf_client.c prog.c test_common.c test_cert.c)
-add_executable(perf_server perf_server.c prog.c test_common.c test_cert.c)
-
-
-IF (NOT MSVC)
-
-add_executable(http_client
-    http_client.c
-    prog.c
-    test_common.c
-    test_cert.c
-)
-
-#MSVC
-ELSE()
-
-add_executable(http_client
-    http_client.c
-    prog.c
-    test_common.c
-    test_cert.c
-)
-
-ENDIF()
-
-TARGET_LINK_LIBRARIES(http_client ${LIBS})
-TARGET_LINK_LIBRARIES(http_server ${LIBS})
-IF(NOT MSVC)
-TARGET_LINK_LIBRARIES(md5_server  ${LIBS})
-TARGET_LINK_LIBRARIES(md5_client  ${LIBS})
-ENDIF()
-TARGET_LINK_LIBRARIES(echo_server ${LIBS})
-TARGET_LINK_LIBRARIES(echo_client ${LIBS})
-TARGET_LINK_LIBRARIES(duck_server ${LIBS})
-TARGET_LINK_LIBRARIES(duck_client ${LIBS})
-TARGET_LINK_LIBRARIES(perf_client ${LIBS})
-TARGET_LINK_LIBRARIES(perf_server ${LIBS})
-
-
-INCLUDE(CheckFunctionExists)
-CHECK_FUNCTION_EXISTS(sendmmsg HAVE_SENDMMSG)
-CHECK_FUNCTION_EXISTS(recvmmsg HAVE_RECVMMSG)
-CHECK_FUNCTION_EXISTS(open_memstream HAVE_OPEN_MEMSTREAM)
-
-
-INCLUDE(CheckSymbolExists)
-
-CHECK_SYMBOL_EXISTS(
-    IP_MTU_DISCOVER
-    "netinet/in.h"
-    HAVE_IP_MTU_DISCOVER
-)
-
-CHECK_SYMBOL_EXISTS(
-    IP_DONTFRAG
-    "netinet/in.h"
-    HAVE_IP_DONTFRAG
-)
-
-CHECK_SYMBOL_EXISTS(
-    preadv
-    "sys/uio.h"
-    HAVE_PREADV
-)
-
-INCLUDE(CheckIncludeFiles)
-
-IF (MSVC AND PCRE_LIB)
-FIND_PATH(EVENT_INCLUDE_DIR NAMES pcreposix.h)
-IF (EVENT_INCLUDE_DIR)
-    MESSAGE(STATUS "found pcreposix.h")
-    SET(HAVE_REGEX 1)
-ELSE()
-    MESSAGE(FATAL_ERROR "event2/event.h was not found")
-ENDIF()
-ELSE()
-CHECK_INCLUDE_FILES(regex.h HAVE_REGEX)
-ENDIF()
-
-CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/test_config.h.in ${CMAKE_CURRENT_BINARY_DIR}/test_config.h)
+add_library(example_common_objects STATIC prog.c test_common.c test_cert.c)
+target_link_libraries(example_common_objects PUBLIC ${LIBS} ${EVENT_LIB} Threads::Threads)
+target_include_directories(example_common_objects PUBLIC ${EVENT_INCLUDE_DIR})
+
+if(MSVC)
+    find_library(PCRE_LIB pcre)
+    if(PCRE_LIB)
+        message(STATUS "Found pcre: ${PCRE_LIB}")
+    else()
+        message(STATUS "pcre not found: http_server won't work")
+    endif()
+    find_library(PCREPOSIX_LIB pcreposix)
+    if(PCREPOSIX_LIB)
+        message(STATUS "Found pcreposix: ${PCREPOSIX_LIB}")
+    else()
+        message(STATUS "pcreposix not found: http_server won't work")
+    endif()
+    target_link_libraries(example_common_objects PUBLIC ws2_32 iphlpapi
+                                                        ${GETOPT_LIB})
+endif()
+
+add_executable(http_client http_client.c)
+target_link_libraries(http_client PRIVATE example_common_objects)
+
+if(NOT MSVC OR (PCRE_LIB AND PCREPOSIX_LIB))
+    add_executable(http_server http_server.c)
+    target_link_libraries(http_server PRIVATE example_common_objects)
+    if(MSVC)
+        target_link_libraries(http_server PRIVATE ${PCRE_LIB} ${PCREPOSIX_LIB})
+    endif()
+endif()
+
+if(NOT MSVC) # TODO: port MD5 server and client to Windows
+    add_executable(md5_server md5_server.c)
+    target_link_libraries(md5_server PRIVATE example_common_objects)
+
+    add_executable(md5_client md5_client.c)
+    target_link_libraries(md5_client PRIVATE example_common_objects)
+endif()
+
+add_executable(echo_server echo_server.c)
+target_link_libraries(echo_server PRIVATE example_common_objects)
+
+add_executable(echo_client echo_client.c)
+target_link_libraries(echo_client PRIVATE example_common_objects)
+
+add_executable(duck_server duck_server.c)
+target_link_libraries(duck_server PRIVATE example_common_objects)
+
+add_executable(duck_client duck_client.c)
+target_link_libraries(duck_client PRIVATE example_common_objects)
+
+add_executable(perf_client perf_client.c)
+target_link_libraries(perf_client PRIVATE example_common_objects)
+
+add_executable(perf_server perf_server.c)
+target_link_libraries(perf_server PRIVATE example_common_objects)
+
+include(CheckFunctionExists)
+check_function_exists(sendmmsg HAVE_SENDMMSG)
+check_function_exists(recvmmsg HAVE_RECVMMSG)
+check_function_exists(open_memstream HAVE_OPEN_MEMSTREAM)
+
+include(CheckSymbolExists)
+
+check_symbol_exists(IP_MTU_DISCOVER "netinet/in.h" HAVE_IP_MTU_DISCOVER)
+
+check_symbol_exists(IP_DONTFRAG "netinet/in.h" HAVE_IP_DONTFRAG)
+
+check_symbol_exists(preadv "sys/uio.h" HAVE_PREADV)
+
+include(CheckIncludeFiles)
+
+if(MSVC AND PCRE_LIB)
+    find_path(EVENT_INCLUDE_DIR NAMES pcreposix.h)
+    if(EVENT_INCLUDE_DIR)
+        message(STATUS "found pcreposix.h")
+        set(HAVE_REGEX 1)
+    else()
+        message(FATAL_ERROR "event2/event.h was not found")
+    endif()
+else()
+    check_include_files(regex.h HAVE_REGEX)
+endif()
+
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test_config.h.in
+               ${CMAKE_CURRENT_BINARY_DIR}/test_config.h)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 356322f3f..389497a4b 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,4 +1,4 @@
 # Copyright (c) 2017 - 2021 LiteSpeed Technologies Inc.  See LICENSE.
-cmake_minimum_required(VERSION 3.0...3.23)
+cmake_minimum_required(VERSION 3.1...3.23)
 
 add_subdirectory(liblsquic)
diff --git a/src/liblsquic/CMakeLists.txt b/src/liblsquic/CMakeLists.txt
index 87fce00b4..e544c672e 100644
--- a/src/liblsquic/CMakeLists.txt
+++ b/src/liblsquic/CMakeLists.txt
@@ -1,5 +1,15 @@
 # Copyright (c) 2017 - 2021 LiteSpeed Technologies Inc.  See LICENSE.
-SET(lsquic_STAT_SRCS
+set(lsquic_EXTRA_SRCS)
+
+if(NOT (PROJECT_NAME STREQUAL "openlitespeed"))
+    list(APPEND lsquic_EXTRA_SRCS lsquic_xxhash.c ../lshpack/lshpack.c)
+endif()
+
+# library type controlled by BUILD_SHARED_LIBS if STATIC or SHARED not
+# explicitly specified
+add_library(
+    lsquic
+    ${lsquic_EXTRA_SRCS}
     ls-qpack/lsqpack.c
     lsquic_adaptive_cc.c
     lsquic_alarmset.c
@@ -82,57 +92,160 @@ SET(lsquic_STAT_SRCS
     lsquic_util.c
     lsquic_varint.c
     lsquic_version.c
-)
-
-IF(NOT MSVC)
-    SET(QPACK_FLAGS "-Wno-uninitialized")
-    INCLUDE(CheckCCompilerFlag)
-    CHECK_C_COMPILER_FLAG(-Wno-implicit-fallthrough HAS_NO_IMPLICIT_FALLTHROUGH)
-    IF (HAS_NO_IMPLICIT_FALLTHROUGH)
-        SET(QPACK_FLAGS "${QPACK_FLAGS} -Wno-implicit-fallthrough")
-    ENDIF()
-set_source_files_properties(ls-qpack/lsqpack.c PROPERTIES COMPILE_FLAGS ${QPACK_FLAGS})
-ENDIF()
-
-include_directories(ls-qpack)
-
-IF(PROJECT_NAME STREQUAL "openlitespeed")
-    INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/src/spdy)
-ELSE()
-    INCLUDE_DIRECTORIES(../lshpack)
-    SET(lsquic_STAT_SRCS ${lsquic_STAT_SRCS}
-        lsquic_xxhash.c
-        ../lshpack/lshpack.c
-    )
-ENDIF()
+    ls-sfparser.c
+    ${CMAKE_CURRENT_BINARY_DIR}/lsquic_versions_to_string.c)
+target_link_libraries(lsquic PRIVATE ${BORINGSSL_LIB_ssl}
+                                     ${BORINGSSL_LIB_crypto} ZLIB::ZLIB)
+target_compile_definitions(
+    lsquic
+    PRIVATE "LSQPACK_DEC_LOGGER_HEADER=\"lsquic_qpack_dec_logger.h\""
+            "LSQPACK_ENC_LOGGER_HEADER=\"lsquic_qpack_enc_logger.h\""
+            "XXH_HEADER_NAME=\"lsquic_xxhash.h\"")
+target_include_directories(lsquic PRIVATE ls-qpack)
 
-ADD_CUSTOM_COMMAND(
+if(PROJECT_NAME STREQUAL "openlitespeed")
+    target_include_directories(lsquic PRIVATE ${PROJECT_SOURCE_DIR}/src/spdy)
+else()
+    target_include_directories(lsquic PRIVATE ../lshpack)
+endif()
+if(MSVC)
+    target_compile_definitions(lsquic PRIVATE LSQUIC_EXPORTS)
+    target_link_libraries(lsquic PRIVATE ws2_32)
+endif()
+
+add_custom_command(
     OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/lsquic_versions_to_string.c
-    COMMAND ${PERL}
-    ARGS ${CMAKE_CURRENT_SOURCE_DIR}/gen-verstrs.pl ${CMAKE_CURRENT_SOURCE_DIR}/../../include/lsquic.h ${CMAKE_CURRENT_BINARY_DIR}/lsquic_versions_to_string.c
-    DEPENDS ./gen-verstrs.pl ${CMAKE_CURRENT_SOURCE_DIR}/../../include/lsquic.h
-)
-SET(lsquic_STAT_SRCS ${lsquic_STAT_SRCS} ${CMAKE_CURRENT_BINARY_DIR}/lsquic_versions_to_string.c)
-SET(lsquic_STAT_SRCS ${lsquic_STAT_SRCS} ls-sfparser.c)
-
-SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DXXH_HEADER_NAME=\\\"lsquic_xxhash.h\\\"")
-SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DLSQPACK_ENC_LOGGER_HEADER=\\\"lsquic_qpack_enc_logger.h\\\"")
-SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DLSQPACK_DEC_LOGGER_HEADER=\\\"lsquic_qpack_dec_logger.h\\\"")
-
-IF(LSQUIC_SHARED_LIB)
-  add_library(lsquic SHARED ${lsquic_STAT_SRCS})
-  TARGET_LINK_LIBRARIES(lsquic PRIVATE ${BORINGSSL_LIB_ssl} ${BORINGSSL_LIB_crypto} ${ZLIB_LIB})
-  IF(MSVC)
-    SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DLSQUIC_EXPORTS")
-  TARGET_LINK_LIBRARIES(lsquic PRIVATE ws2_32.lib)
-  ENDIF()
-ELSE()
-  add_library(lsquic STATIC ${lsquic_STAT_SRCS})
-ENDIF()
-
-install(TARGETS lsquic
+    COMMAND
+        ${PERL} ARGS ${CMAKE_CURRENT_SOURCE_DIR}/gen-verstrs.pl
+        ${CMAKE_CURRENT_SOURCE_DIR}/../../include/lsquic.h
+        ${CMAKE_CURRENT_BINARY_DIR}/lsquic_versions_to_string.c
+    DEPENDS ./gen-verstrs.pl ${CMAKE_CURRENT_SOURCE_DIR}/../../include/lsquic.h)
+
+if(WIN32)
+    target_compile_definitions(lsquic PRIVATE WIN32_LEAN_AND_MEAN NOMINMAX)
+endif()
+if(MSVC)
+    target_compile_options(lsquic PRIVATE -W4 -WX -Zi
+                                          $<IF:$<CONFIG:Debug>,-Od,-Ox>)
+    target_include_directories(
+        lsquic
+        PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../wincompat
+        PUBLIC $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/lsquic/wincompat>
+    )
+    target_compile_definitions(lsquic PRIVATE _CRT_SECURE_NO_WARNINGS)
+
+    if(BUILD_SHARED_LIBS)
+        target_compile_definitions(lsquic PRIVATE LSQUIC_SHARED_LIB)
+    endif()
+    target_compile_options(lsquic PUBLIC /wd4100) # unreferenced formal
+                                                  # parameter
+    target_compile_options(lsquic PUBLIC /wd4115) # unnamed type definition in
+                                                  # parentheses
+    target_compile_options(lsquic PUBLIC /wd4116) # named type definition in
+                                                  # parentheses
+    target_compile_options(
+        lsquic PUBLIC /wd4146) # unary minus operator applied to unsigned type,
+                               # result still unsigned
+    target_compile_options(lsquic PUBLIC /wd4132) # const initialization
+    target_compile_options(lsquic PUBLIC /wd4200) # zero-sized array
+    target_compile_options(lsquic PUBLIC /wd4204) # non-constant aggregate
+                                                  # initializer
+    target_compile_options(lsquic PUBLIC /wd4244) # integer conversion
+    target_compile_options(
+        lsquic PUBLIC /wd4245) # conversion from 'int' to 'unsigned int',
+                               # signed/unsigned mismatch
+    target_compile_options(lsquic PUBLIC /wd4267) # integer conversion
+    target_compile_options(
+        lsquic PUBLIC /wd4214) # nonstandard extension used: bit field types
+                               # other than int
+    target_compile_options(
+        lsquic PUBLIC /wd4295) # array is too small to include a terminating
+                               # null character
+    target_compile_options(
+        lsquic PUBLIC /wd4334) # result of 32-bit shift implicitly converted to
+                               # 64 bits (was 64-bit shift intended?)
+    target_compile_options(lsquic PUBLIC /wd4456) # hide previous local
+                                                  # declaration
+    target_compile_options(lsquic PUBLIC /wd4459) # hide global declaration
+    target_compile_options(lsquic PUBLIC /wd4706) # assignment within
+                                                  # conditional expression
+    target_compile_options(lsquic PUBLIC /wd4090) # different 'const' qualifier
+                                                  # (TODO: debug ls-sfparser.c)
+    target_compile_options(lsquic PUBLIC /wd4305) # truncation from double to
+                                                  # float
+else()
+    # configure flags for lsqpack.c
+    set(QPACK_FLAGS "-Wno-uninitialized")
+    if(HAS_NO_IMPLICIT_FALLTHROUGH)
+        string(APPEND QPACK_FLAGS " -Wno-implicit-fallthrough")
+    endif()
+    set_source_files_properties(ls-qpack/lsqpack.c PROPERTIES COMPILE_FLAGS
+                                                              ${QPACK_FLAGS})
+
+    # flags for the whole library
+    target_compile_options(lsquic PRIVATE -fno-omit-frame-pointer)
+
+    if(LSQUIC_SANITIZER)
+        target_compile_options(lsquic PRIVATE $<DEBUG:-fsanitize=address>)
+        # This command is introduced in CMake 3.13, so 3.13 is required for
+        # ASan.
+        target_link_options(lsquic PRIVATE $<DEBUG:-fsanitize=address>)
+    endif()
+
+    if(LSQUIC_PROFILE)
+        target_compile_options(lsquic PRIVATE -g -pg)
+    endif()
+
+    if(LSQUIC_COVERAGE)
+        target_compile_options(lsquic PUBLIC -fprofile-arcs -ftest-coverage)
+    endif()
+endif()
+
+if(LSQUIC_FIU)
+    target_compile_definitions(lsquic PRIVATE FIU_ENABLE=1)
+    target_link_libraries(lsquic PRIVATE fiu)
+endif()
+if(NEED_LIBRT_FOR_clock_getres AND RT_LIBRARY)
+    target_link_libraries(lsquic PRIVATE ${RT_LIBRARY})
+endif()
+
+if(LSQUIC_DEBUG_NEXT_ADV_TICK)
+    target_compile_definitions(lsquic PRIVATE LSQUIC_DEBUG_NEXT_ADV_TICK=1)
+else()
+    target_compile_definitions(lsquic PRIVATE LSQUIC_DEBUG_NEXT_ADV_TICK=0)
+endif()
+if(LSQUIC_CONN_STATS)
+    target_compile_definitions(lsquic PRIVATE LSQUIC_CONN_STATS=1)
+else()
+    target_compile_definitions(lsquic PRIVATE LSQUIC_CONN_STATS=0)
+endif()
+if(LSQUIC_ENABLE_HANDSHAKE_DISABLE)
+    target_compile_definitions(lsquic PRIVATE LSQUIC_ENABLE_HANDSHAKE_DISABLE=1)
+endif()
+if(LSQUIC_COMPILE_OUT_DEBUG_MESSAGES)
+    target_compile_definitions(lsquic
+                               PRIVATE LSQUIC_LOWEST_LOG_LEVEL=LSQ_LOG_WARN)
+endif()
+if(LSQUIC_DEVEL)
+    target_compile_definitions(lsquic PRIVATE LSQUIC_DEVEL=1)
+endif()
+
+install(
+    TARGETS lsquic
     EXPORT lsquic-targets
     LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
-    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
-)
+    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
+
+target_include_directories(
+    lsquic PUBLIC $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/lsquic>)
 
+configure_file(lsquic-config.cmake
+               ${CMAKE_CURRENT_BINARY_DIR}/lsquic-config.cmake @ONLY)
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/lsquic-config.cmake
+        DESTINATION share/lsquic)
+
+install(
+    EXPORT lsquic-targets
+    DESTINATION share/lsquic
+    NAMESPACE lsquic::
+    FILE lsquic-targets.cmake)
diff --git a/src/liblsquic/lsquic-config.cmake b/src/liblsquic/lsquic-config.cmake
new file mode 100644
index 000000000..d42007182
--- /dev/null
+++ b/src/liblsquic/lsquic-config.cmake
@@ -0,0 +1,4 @@
+include("${CMAKE_CURRENT_LIST_DIR}/lsquic-targets.cmake")
+
+include(CMakeFindDependencyMacro)
+find_dependency(ZLIB)
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 7aee69b62..b0870bf9a 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -1,26 +1,26 @@
 # Copyright (c) 2017 - 2021 LiteSpeed Technologies Inc.  See LICENSE.
-INCLUDE_DIRECTORIES(../src/liblsquic)
-
-ENABLE_TESTING()
-
-SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DLSQUIC_TEST=1")
-
-IF (MSVC)
-    SET(LIB_FLAGS "-FORCE:MULTIPLE")
-ELSE()
-    SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-value")
-    IF (CMAKE_C_COMPILER_ID STREQUAL GNU)
-        SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-override-init")
-    ENDIF()
-    IF (CMAKE_C_COMPILER_ID STREQUAL Clang)
-        SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-initializer-overrides")
-    ENDIF()
-ENDIF()
+include_directories(../src/liblsquic)
+
+enable_testing()
+
+add_definitions(-DLSQUIC_TEST=1)
+set(_warning_disable_options)
+if(MSVC)
+    set(LIB_FLAGS "-FORCE:MULTIPLE")
+else()
+    string(APPEND CMAKE_C_FLAGS " -Wno-unused-value")
+    if(CMAKE_C_COMPILER_ID STREQUAL GNU)
+        string(APPEND CMAKE_C_FLAGS " -Wno-override-init")
+    endif()
+    if(CMAKE_C_COMPILER_ID STREQUAL Clang)
+        string(APPEND CMAKE_C_FLAGS " -Wno-initializer-overrides")
+    endif()
+endif()
 
 include_directories(../src/liblsquic/ls-qpack)
-INCLUDE_DIRECTORIES(../src/lshpack)
+include_directories(../src/lshpack)
 
-SET(TESTS
+set(TESTS
     ack
     ackgen_gquic_be
     ackparse_gquic_be
@@ -69,93 +69,96 @@ SET(TESTS
     trapa
     varint
     ver_nego
-    wuf_gquic_be
-)
+    wuf_gquic_be)
 
-IF (CMAKE_SYSTEM_NAME STREQUAL "Linux")
+if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
     # Linux has fmemopen
-    SET(TESTS ${TESTS} frame_rw)
-ENDIF()
-
-IF (NOT CMAKE_SYSTEM_NAME STREQUAL "Windows")
-    # No regexes on Windows
-    SET(TESTS ${TESTS} ack_merge)
-    # No open_memstream() on Windows
-    SET(TESTS ${TESTS} hcsi_reader)
-    # Takes forever on Windows, for whatever reason.  Or maybe it's the
-    # MS C compilers.  Something to investigate... later.
-    LIST(APPEND TESTS h3_framing)
-ENDIF()
-
-
-FOREACH(TEST_NAME ${TESTS})
-    ADD_EXECUTABLE(test_${TEST_NAME} test_${TEST_NAME}.c ${ADDL_SOURCES})
-    IF(NOT MSVC)
-        TARGET_LINK_LIBRARIES(test_${TEST_NAME} ${LIBS} ${LIB_FLAGS})
-    ELSE()
-        TARGET_LINK_LIBRARIES(test_${TEST_NAME} ${LIBS} ${GETOPT_LIB} ${LIB_FLAGS})
-        # copy any dependencies local to the tests
-      #IF (${CMAKE_VERSION} VERSION_LESS "3.21.0")
-        ADD_CUSTOM_COMMAND(TARGET test_${TEST_NAME} POST_BUILD
-          COMMAND ${CMAKE_COMMAND} -E copy \"$ENV{VCPKG_ROOT}/installed/x64-windows$<$<CONFIG:Debug>:/debug>/bin/getopt.dll\" \"$<TARGET_FILE_DIR:test_${TEST_NAME}>\"
-          COMMAND_EXPAND_LISTS
-        )
-      #ELSE()
-      #  ADD_CUSTOM_COMMAND(TARGET test_${TEST_NAME} POST_BUILD
-      #    COMMAND if not \"\"=="$<TARGET_RUNTIME_DLLS:test_${TEST_NAME}>" ${CMAKE_COMMAND} -E copy $<TARGET_RUNTIME_DLLS:test_${TEST_NAME}> $<TARGET_FILE_DIR:test_${TEST_NAME}>
-      #    COMMAND_EXPAND_LISTS
-      #  )
-      #ENDIF()
-    ENDIF()
-    ADD_TEST(${TEST_NAME} test_${TEST_NAME})
-ENDFOREACH()
-
-ADD_EXECUTABLE(test_stream test_stream.c ${ADDL_SOURCES})
-TARGET_LINK_LIBRARIES(test_stream ${LIBS} ${LIB_FLAGS})
-IF(MSVC)
-    TARGET_LINK_LIBRARIES(test_stream ${GETOPT_LIB})
-ENDIF()
-ADD_TEST(stream test_stream)
-ADD_TEST(stream_hash test_stream -h)
-ADD_TEST(stream_A test_stream -A)
-ADD_TEST(stream_hash_A test_stream -A -h)
-
-IF(NOT MSVC)
-ADD_EXECUTABLE(graph_cubic graph_cubic.c ${ADDL_SOURCES})
-TARGET_LINK_LIBRARIES(graph_cubic ${LIBS})
-
-ADD_EXECUTABLE(mini_parse mini_parse.c ${ADDL_SOURCES})
-TARGET_LINK_LIBRARIES(mini_parse ${LIBS})
-ENDIF()
-
-ADD_EXECUTABLE(test_min_heap test_min_heap.c ../src/liblsquic/lsquic_min_heap.c)
-ADD_TEST(min_heap test_min_heap)
-
-SET(MALO_SRC test_malo.c ../src/liblsquic/lsquic_malo.c)
-ADD_EXECUTABLE(test_malo_pooled ${MALO_SRC})
-IF(MSVC)
-    TARGET_LINK_LIBRARIES(test_malo_pooled ${GETOPT_LIB})
-ENDIF()
-SET_TARGET_PROPERTIES(test_malo_pooled
-    PROPERTIES COMPILE_FLAGS "${CMAKE_C_FLAGS} -DLSQUIC_USE_POOLS=1")
-ADD_TEST(malo_pooled test_malo_pooled)
-
-ADD_EXECUTABLE(test_malo_nopool ${MALO_SRC})
-IF(MSVC)
-    TARGET_LINK_LIBRARIES(test_malo_nopool ${GETOPT_LIB})
-ENDIF()
-SET_TARGET_PROPERTIES(test_malo_nopool
-    PROPERTIES COMPILE_FLAGS "${CMAKE_C_FLAGS} -DLSQUIC_USE_POOLS=0")
-ADD_TEST(malo_nopool test_malo_nopool)
-
-ADD_EXECUTABLE(test_minmax test_minmax.c ../src/liblsquic/lsquic_minmax.c)
-IF(MSVC)
-    TARGET_LINK_LIBRARIES(test_minmax ${GETOPT_LIB})
-ENDIF()
-ADD_TEST(minmax test_minmax)
-
-ADD_EXECUTABLE(test_rechist test_rechist.c ../src/liblsquic/lsquic_rechist.c)
-ADD_TEST(rechist test_rechist)
-
-ADD_EXECUTABLE(test_trechist test_trechist.c ../src/liblsquic/lsquic_trechist.c)
-ADD_TEST(trechist test_trechist)
+    list(APPEND TESTS frame_rw)
+endif()
+
+if(NOT CMAKE_SYSTEM_NAME STREQUAL "Windows")
+
+    list(
+        APPEND
+        TESTS
+        # No regexes on Windows
+        ack_merge
+        # No open_memstream() on Windows
+        hcsi_reader
+        # Takes forever on Windows, for whatever reason.  Or maybe it's the MS C
+        # compilers.  Something to investigate... later.
+        h3_framing)
+endif()
+
+foreach(TEST_NAME ${TESTS})
+    add_executable(test_${TEST_NAME} test_${TEST_NAME}.c ${ADDL_SOURCES})
+    if(NOT MSVC)
+        target_link_libraries(test_${TEST_NAME} ${LIBS} ${LIB_FLAGS})
+    else()
+        target_link_libraries(test_${TEST_NAME} ${LIBS} ${GETOPT_LIB}
+                              ${LIB_FLAGS})
+        # copy any dependencies local to the tests IF (${CMAKE_VERSION}
+        # VERSION_LESS "3.21.0")
+        add_custom_command(
+            TARGET test_${TEST_NAME}
+            POST_BUILD
+            COMMAND
+                ${CMAKE_COMMAND} -E copy
+                \"$ENV{VCPKG_ROOT}/installed/x64-windows$<$<CONFIG:Debug>:/debug>/bin/getopt.dll\"
+                \"$<TARGET_FILE_DIR:test_${TEST_NAME}>\"
+            COMMAND_EXPAND_LISTS)
+        # ELSE() ADD_CUSTOM_COMMAND(TARGET test_${TEST_NAME} POST_BUILD COMMAND
+        # if not \"\"=="$<TARGET_RUNTIME_DLLS:test_${TEST_NAME}>"
+        # ${CMAKE_COMMAND} -E copy $<TARGET_RUNTIME_DLLS:test_${TEST_NAME}>
+        # $<TARGET_FILE_DIR:test_${TEST_NAME}> COMMAND_EXPAND_LISTS ) ENDIF()
+    endif()
+    add_test(${TEST_NAME} test_${TEST_NAME})
+endforeach()
+
+add_executable(test_stream test_stream.c ${ADDL_SOURCES})
+target_link_libraries(test_stream ${LIBS} ${LIB_FLAGS})
+if(MSVC)
+    target_link_libraries(test_stream ${GETOPT_LIB})
+endif()
+add_test(stream test_stream)
+add_test(stream_hash test_stream -h)
+add_test(stream_A test_stream -A)
+add_test(stream_hash_A test_stream -A -h)
+
+if(NOT MSVC)
+    add_executable(graph_cubic graph_cubic.c ${ADDL_SOURCES})
+    target_link_libraries(graph_cubic ${LIBS})
+
+    add_executable(mini_parse mini_parse.c ${ADDL_SOURCES})
+    target_link_libraries(mini_parse ${LIBS})
+endif()
+
+add_executable(test_min_heap test_min_heap.c ../src/liblsquic/lsquic_min_heap.c)
+add_test(min_heap test_min_heap)
+
+set(MALO_SRC test_malo.c ../src/liblsquic/lsquic_malo.c)
+add_executable(test_malo_pooled ${MALO_SRC})
+if(MSVC)
+    target_link_libraries(test_malo_pooled ${GETOPT_LIB})
+endif()
+target_compile_definitions(test_malo_pooled PRIVATE LSQUIC_USE_POOLS=2)
+add_test(malo_pooled test_malo_pooled)
+
+add_executable(test_malo_nopool ${MALO_SRC})
+if(MSVC)
+    target_link_libraries(test_malo_nopool ${GETOPT_LIB})
+endif()
+target_compile_definitions(test_malo_nopool PRIVATE LSQUIC_USE_POOLS=0)
+add_test(malo_nopool test_malo_nopool)
+
+add_executable(test_minmax test_minmax.c ../src/liblsquic/lsquic_minmax.c)
+if(MSVC)
+    target_link_libraries(test_minmax ${GETOPT_LIB})
+endif()
+add_test(minmax test_minmax)
+
+add_executable(test_rechist test_rechist.c ../src/liblsquic/lsquic_rechist.c)
+add_test(rechist test_rechist)
+
+add_executable(test_trechist test_trechist.c ../src/liblsquic/lsquic_trechist.c)
+add_test(trechist test_trechist)