Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 32 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* HTTPLIB_USE_ZLIB_IF_AVAILABLE (default on)
* HTTPLIB_USE_BROTLI_IF_AVAILABLE (default on)
* HTTPLIB_USE_ZSTD_IF_AVAILABLE (default on)
* HTTPLIB_BUILD_MODULES (default off)
* HTTPLIB_REQUIRE_OPENSSL (default off)
* HTTPLIB_REQUIRE_ZLIB (default off)
* HTTPLIB_REQUIRE_BROTLI (default off)
Expand Down Expand Up @@ -110,6 +111,15 @@ option(HTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN "Enable feature to load system cer
option(HTTPLIB_USE_NON_BLOCKING_GETADDRINFO "Enables the non-blocking alternatives for getaddrinfo." ON)
option(HTTPLIB_REQUIRE_ZSTD "Requires ZSTD to be found & linked, or fails build." OFF)
option(HTTPLIB_USE_ZSTD_IF_AVAILABLE "Uses ZSTD (if available) to enable zstd support." ON)
# C++20 modules support requires CMake 3.28 or later
if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.28")
option(HTTPLIB_BUILD_MODULES "Build httplib modules (requires HTTPLIB_COMPILE to be ON)." OFF)
else()
set(HTTPLIB_BUILD_MODULES OFF CACHE INTERNAL "Build httplib modules disabled (requires CMake 3.28+)" FORCE)
if(DEFINED CACHE{HTTPLIB_BUILD_MODULES} AND HTTPLIB_BUILD_MODULES)
message(WARNING "HTTPLIB_BUILD_MODULES requires CMake 3.28 or later. Current version is ${CMAKE_VERSION}. Modules support has been disabled.")
endif()
endif()
# Defaults to static library but respects standard BUILD_SHARED_LIBS if set
include(CMakeDependentOption)
cmake_dependent_option(HTTPLIB_SHARED "Build the library as a shared library instead of static. Has no effect if using header-only."
Expand Down Expand Up @@ -248,6 +258,13 @@ if(HTTPLIB_COMPILE)
$<BUILD_INTERFACE:${_httplib_build_includedir}/httplib.h>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/httplib.h>
)

# Add C++20 module support if requested
# Include from separate file to prevent parse errors on older CMake versions
if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.28")
include(cmake/modules.cmake)
endif()

set_target_properties(${PROJECT_NAME}
PROPERTIES
VERSION ${${PROJECT_NAME}_VERSION}
Expand All @@ -264,8 +281,12 @@ endif()
# Only useful if building in-tree, versus using it from an installation.
add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})

# Require C++11
target_compile_features(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC} cxx_std_11)
# Require C++11, or C++20 if modules are enabled
if(HTTPLIB_BUILD_MODULES)
target_compile_features(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC} cxx_std_20)
else()
target_compile_features(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC} cxx_std_11)
endif()

target_include_directories(${PROJECT_NAME} SYSTEM ${_INTERFACE_OR_PUBLIC}
$<BUILD_INTERFACE:${_httplib_build_includedir}>
Expand Down Expand Up @@ -337,7 +358,11 @@ if(HTTPLIB_INSTALL)
# Creates the export httplibTargets.cmake
# This is strictly what holds compilation requirements
# and linkage information (doesn't find deps though).
install(TARGETS ${PROJECT_NAME} EXPORT httplibTargets)
if(HTTPLIB_BUILD_MODULES)
install(TARGETS ${PROJECT_NAME} EXPORT httplibTargets FILE_SET CXX_MODULES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/httplib/modules CXX_MODULES_BMI DESTINATION ${CMAKE_INSTALL_LIBDIR}/httplib/modules)
else()
install(TARGETS ${PROJECT_NAME} EXPORT httplibTargets)
endif()

install(FILES "${_httplib_build_includedir}/httplib.h" TYPE INCLUDE)

Expand Down Expand Up @@ -366,6 +391,10 @@ if(HTTPLIB_INSTALL)
include(CPack)
endif()

if(HTTPLIB_BUILD_MODULES AND NOT HTTPLIB_COMPILE)
message(FATAL_ERROR "HTTPLIB_BUILD_MODULES requires HTTPLIB_COMPILE to be ON.")
endif()

if(HTTPLIB_TEST)
include(CTest)
add_subdirectory(test)
Expand Down
16 changes: 16 additions & 0 deletions cmake/modules.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# This file contains C++20 module support requiring CMake 3.28+
# Included conditionally to prevent parse errors on older CMake versions

if(HTTPLIB_BUILD_MODULES)
if(POLICY CMP0155)
cmake_policy(SET CMP0155 NEW)
endif()

set(CMAKE_CXX_SCAN_FOR_MODULES ON)

target_sources(${PROJECT_NAME}
PUBLIC
FILE_SET CXX_MODULES FILES
"${_httplib_build_includedir}/httplib.cppm"
)
endif()
61 changes: 46 additions & 15 deletions split.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
#!/usr/bin/env python3

"""This script splits httplib.h into .h and .cc parts."""
"""This script splits httplib.h into .h, .cc, and .cppm parts."""

import os
import sys
from argparse import ArgumentParser, Namespace
from typing import List

BORDER: str = '// ----------------------------------------------------------------------------'

def main() -> None:
"""Main entry point for the script."""
BORDER: str = '// ----------------------------------------------------------------------------'

args_parser: ArgumentParser = ArgumentParser(description=__doc__)
args_parser.add_argument(
"-e", "--extension", help="extension of the implementation file (default: cc)",
default="cc"
"-e", "--extension", help="extension of the implementation file (default: cc)", default="cc"
)
args_parser.add_argument(
"-o", "--out", help="where to write the files (default: out)", default="out"
Expand All @@ -25,14 +24,14 @@ def main() -> None:
cur_dir: str = os.path.dirname(sys.argv[0])
if not cur_dir:
cur_dir = '.'
lib_name: str = 'httplib'
lib_name: str = "httplib"
header_name: str = f"/{lib_name}.h"
source_name: str = f"/{lib_name}.{args.extension}"
# get the input file
in_file: str = cur_dir + header_name
in_file: str = f"{cur_dir}{header_name}"
# get the output file
h_out: str = args.out + header_name
cc_out: str = args.out + source_name
h_out: str = f"{args.out}{header_name}"
cc_out: str = f"{args.out}{source_name}"

# if the modification time of the out file is after the in file,
# don't split (as it is already finished)
Expand All @@ -49,24 +48,56 @@ def main() -> None:

os.makedirs(args.out, exist_ok=True)

# Find the Headers and Declaration comment markers
headers_start: int = -1
declaration_start: int = -1
for i, line in enumerate(lines):
if ' * Headers' in line:
headers_start = i - 1 # Include the /* line
elif ' * Declaration' in line:
declaration_start = i - 1 # Stop before the /* line
break

in_implementation: bool = False
cc_out: str = args.out + source_name
with open(h_out, 'w') as fh, open(cc_out, 'w') as fc:
fc.write('#include "httplib.h"\n')
fc.write('namespace httplib {\n')
cppm_out: str = args.out + f"/{lib_name}.cppm"

with open(h_out, 'w') as fh, open(cc_out, 'w') as fc, open(cppm_out, 'w') as fm:
# Write source file
fc.write("#include \"httplib.h\"\n")
fc.write("namespace httplib {\n")

# Write module file
fm.write("module;\n\n")

# Write global module fragment (from Headers to Declaration comment)
# Filter out 'using' declarations to avoid conflicts
if headers_start >= 0 and declaration_start >= 0:
for i in range(headers_start, declaration_start):
line: str = lines[i]
if 'using' not in line:
fm.write(line)

fm.write("\nexport module httplib;\n\n")
fm.write("export {\n")
fm.write(f"{' ' * 4}#include \"httplib.h\"\n")
fm.write("}\n")

# Process lines for header and source split
for line in lines:
is_border_line: bool = BORDER in line
if is_border_line:
in_implementation: bool = not in_implementation
elif in_implementation:
fc.write(line.replace('inline ', ''))
fc.write(line.replace("inline ", ""))
else:
fh.write(line)
fc.write('} // namespace httplib\n')

fc.write("} // namespace httplib\n")

print(f"Wrote {h_out} and {cc_out}")
print(f"Wrote {h_out}, {cc_out}, and {cppm_out}")
else:
print(f"{h_out} and {cc_out} are up to date")
print(f"{h_out}, {cc_out}, and module files are up to date")


if __name__ == "__main__":
Expand Down
Loading