Browse Source

Add HTTPLIB_COMPILE option to Cmake (#493)

This option (default OFF) automatically splits the file (with split.py)
into a header & source file, then compiles it as a shared/static
library. This requires an installed Python v3 executable to work.

This also adds a HTTPLIB_IS_COMPILED boolean that's available after a
finfind_package(httplib) call.

Note that the minimum Cmake version increased to 3.12 because of FindPython3.
Hopefully this isn't a problem, as it's already 3 years old at this point.
KTGH 5 years ago
parent
commit
8cad160c0a
2 changed files with 112 additions and 47 deletions
  1. 96 26
      CMakeLists.txt
  2. 16 21
      httplibConfig.cmake.in

+ 96 - 26
CMakeLists.txt

@@ -4,6 +4,9 @@
 	* HTTPLIB_USE_ZLIB_IF_AVAILABLE (default on)
 	* HTTPLIB_USE_ZLIB_IF_AVAILABLE (default on)
 	* HTTPLIB_REQUIRE_OPENSSL (default off)
 	* HTTPLIB_REQUIRE_OPENSSL (default off)
 	* HTTPLIB_REQUIRE_ZLIB (default off)
 	* HTTPLIB_REQUIRE_ZLIB (default off)
+	* HTTPLIB_COMPILE (default off)
+
+	-------------------------------------------------------------------------------
 
 
 	After installation with Cmake, a find_package(httplib) is available.
 	After installation with Cmake, a find_package(httplib) is available.
 	This creates a httplib::httplib target (if found).
 	This creates a httplib::httplib target (if found).
@@ -27,17 +30,24 @@
 	cmake ..
 	cmake ..
 	runas /user:Administrator "cmake --build . --config Release --target install"
 	runas /user:Administrator "cmake --build . --config Release --target install"
 
 
+	-------------------------------------------------------------------------------
+
 	These three variables are available after you run find_package(httplib)
 	These three variables are available after you run find_package(httplib)
 	* HTTPLIB_HEADER_PATH - this is the full path to the installed header.
 	* HTTPLIB_HEADER_PATH - this is the full path to the installed header.
 	* HTTPLIB_IS_USING_OPENSSL - a bool for if OpenSSL support is enabled.
 	* HTTPLIB_IS_USING_OPENSSL - a bool for if OpenSSL support is enabled.
 	* HTTPLIB_IS_USING_ZLIB - a bool for if ZLIB support is enabled.
 	* HTTPLIB_IS_USING_ZLIB - a bool for if ZLIB support is enabled.
+	* HTTPLIB_IS_COMPILED - a bool for if the library is header-only or compiled.
 
 
 	Want to use precompiled headers (Cmake feature since v3.16)?
 	Want to use precompiled headers (Cmake feature since v3.16)?
 	It's as simple as doing the following (before linking):
 	It's as simple as doing the following (before linking):
 
 
 	target_precompile_headers(httplib::httplib INTERFACE "${HTTPLIB_HEADER_PATH}")
 	target_precompile_headers(httplib::httplib INTERFACE "${HTTPLIB_HEADER_PATH}")
+
+	-------------------------------------------------------------------------------
+
+	FindPython3 requires Cmake v3.12
 ]]
 ]]
-cmake_minimum_required(VERSION 3.7.0 FATAL_ERROR)
+cmake_minimum_required(VERSION 3.12.0 FATAL_ERROR)
 project(httplib LANGUAGES CXX)
 project(httplib LANGUAGES CXX)
 
 
 # Change as needed to set an OpenSSL minimum version.
 # Change as needed to set an OpenSSL minimum version.
@@ -51,17 +61,23 @@ option(HTTPLIB_REQUIRE_ZLIB "Requires ZLIB to be found & linked, or fails build.
 # Make these options so their automatic use can be specifically disabled (as needed)
 # Make these options so their automatic use can be specifically disabled (as needed)
 option(HTTPLIB_USE_OPENSSL_IF_AVAILABLE "Uses OpenSSL (if available) to enable HTTPS support." ON)
 option(HTTPLIB_USE_OPENSSL_IF_AVAILABLE "Uses OpenSSL (if available) to enable HTTPS support." ON)
 option(HTTPLIB_USE_ZLIB_IF_AVAILABLE "Uses ZLIB (if available) to enable compression support." ON)
 option(HTTPLIB_USE_ZLIB_IF_AVAILABLE "Uses ZLIB (if available) to enable compression support." ON)
-
-# TODO: implement the option of option to correctly split, building, and export with the split.py script.
-# option(HTTPLIB_SPLIT "Uses a Python script to split the header into a header & source file." OFF)
+# Lets you compile the program as a regular library instead of header-only
+option(HTTPLIB_COMPILE "If ON, uses a Python script to split the header into a compilable header & source file (requires Python v3)." OFF)
+# Defaults to static library
+option(BUILD_SHARED_LIBS "Build the library as a shared library instead of static. Has no effect if using header-only." OFF)
+if (BUILD_SHARED_LIBS AND WIN32 AND HTTPLIB_COMPILE)
+	# Necessary for Windows if building shared libs
+	# See https://stackoverflow.com/a/40743080
+	set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
+endif()
 
 
 # Threads needed for <thread> on some systems, and for <pthread.h> on Linux
 # Threads needed for <thread> on some systems, and for <pthread.h> on Linux
 find_package(Threads REQUIRED)
 find_package(Threads REQUIRED)
+# Since Cmake v3.11, Crypto & SSL became optional when not specified as COMPONENTS.
 if(HTTPLIB_REQUIRE_OPENSSL)
 if(HTTPLIB_REQUIRE_OPENSSL)
-	find_package(OpenSSL ${_HTTPLIB_OPENSSL_MIN_VER} REQUIRED)
+	find_package(OpenSSL ${_HTTPLIB_OPENSSL_MIN_VER} COMPONENTS Crypto SSL REQUIRED)
 elseif(HTTPLIB_USE_OPENSSL_IF_AVAILABLE)
 elseif(HTTPLIB_USE_OPENSSL_IF_AVAILABLE)
-	# Look quietly since it's optional are optional
-	find_package(OpenSSL ${_HTTPLIB_OPENSSL_MIN_VER} QUIET)
+	find_package(OpenSSL ${_HTTPLIB_OPENSSL_MIN_VER} COMPONENTS Crypto SSL QUIET)
 endif()
 endif()
 if(HTTPLIB_REQUIRE_ZLIB)
 if(HTTPLIB_REQUIRE_ZLIB)
 	find_package(ZLIB REQUIRED)
 	find_package(ZLIB REQUIRED)
@@ -73,16 +89,53 @@ endif()
 # like CMAKE_INSTALL_INCLUDEDIR or CMAKE_INSTALL_DATADIR
 # like CMAKE_INSTALL_INCLUDEDIR or CMAKE_INSTALL_DATADIR
 include(GNUInstallDirs)
 include(GNUInstallDirs)
 
 
-add_library(${PROJECT_NAME} INTERFACE)
+if(HTTPLIB_COMPILE)
+	# Put the split script into the build dir
+	configure_file(split.py "${CMAKE_CURRENT_BINARY_DIR}/split.py"
+		COPYONLY
+	)
+	# Needs to be in the same dir as the python script
+	configure_file(httplib.h "${CMAKE_CURRENT_BINARY_DIR}/httplib.h"
+		COPYONLY
+	)
+
+	# Used outside of this if-else
+	set(_INTERFACE_OR_PUBLIC PUBLIC)
+	# Brings in the Python3_EXECUTABLE path we can use.
+	find_package(Python3 REQUIRED)
+	# Actually split the file
+	# Keeps the output in the build dir to not pollute the main dir
+	execute_process(COMMAND ${Python3_EXECUTABLE} "${CMAKE_CURRENT_BINARY_DIR}/split.py"
+		WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+		ERROR_VARIABLE _httplib_split_error
+	)
+	if(_httplib_split_error)
+		message(FATAL_ERROR "Failed when trying to split Cpp-httplib with the Python script.\n${_httplib_split_error}")
+	endif()
+
+	# split.py puts output in "out"
+	set(_httplib_build_includedir "${CMAKE_CURRENT_BINARY_DIR}/out")
+	# This will automatically be either static or shared based on the value of BUILD_SHARED_LIBS
+	add_library(${PROJECT_NAME} "${_httplib_build_includedir}/httplib.cc")
+	target_sources(${PROJECT_NAME}
+		PUBLIC
+			$<BUILD_INTERFACE:${_httplib_build_includedir}/httplib.h>
+			$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/httplib.h>
+	)
+else()
+	# This is for header-only.
+	set(_INTERFACE_OR_PUBLIC INTERFACE)
+	add_library(${PROJECT_NAME} INTERFACE)
+	set(_httplib_build_includedir "${CMAKE_CURRENT_SOURCE_DIR}")
+endif()
 # Lets you address the target with httplib::httplib
 # Lets you address the target with httplib::httplib
 # Only useful if building in-tree, versus using it from an installation.
 # Only useful if building in-tree, versus using it from an installation.
 add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
 add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
 
 
 # Might be missing some, but this list is somewhat comprehensive
 # Might be missing some, but this list is somewhat comprehensive
-target_compile_features(${PROJECT_NAME} INTERFACE
+target_compile_features(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
 	cxx_std_11
 	cxx_std_11
 	cxx_nullptr
 	cxx_nullptr
-	cxx_noexcept
 	cxx_lambdas
 	cxx_lambdas
 	cxx_override
 	cxx_override
 	cxx_defaulted_functions
 	cxx_defaulted_functions
@@ -94,25 +147,42 @@ target_compile_features(${PROJECT_NAME} INTERFACE
 	cxx_sizeof_member
 	cxx_sizeof_member
 )
 )
 
 
-target_include_directories(${PROJECT_NAME} INTERFACE
-    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
-    $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
-
-
-target_link_libraries(${PROJECT_NAME} INTERFACE
-	# Always require threads
-	Threads::Threads
-	# Only link zlib & openssl if they're found
-	$<$<BOOL:${ZLIB_FOUND}>:ZLIB::ZLIB>
-	$<$<BOOL:${OPENSSL_FOUND}>:OpenSSL::SSL OpenSSL::Crypto>
+target_include_directories(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
+    $<BUILD_INTERFACE:${_httplib_build_includedir}>
+    $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
 )
 )
 
 
-# Auto-define the optional support if those packages were found
-target_compile_definitions(${PROJECT_NAME} INTERFACE
-	$<$<BOOL:${ZLIB_FOUND}>:CPPHTTPLIB_ZLIB_SUPPORT>
-	$<$<BOOL:${OPENSSL_FOUND}>:CPPHTTPLIB_OPENSSL_SUPPORT>
+# Always require threads
+target_link_libraries(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
+		Threads::Threads
 )
 )
 
 
+# We check for the target when using IF_AVAILABLE since it's possible we didn't find it.
+if(HTTPLIB_USE_OPENSSL_IF_AVAILABLE AND TARGET OpenSSL::SSL AND TARGET OpenSSL::Crypto OR HTTPLIB_REQUIRE_OPENSSL)
+	target_link_libraries(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
+		OpenSSL::SSL OpenSSL::Crypto
+	)
+	target_compile_definitions(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
+		CPPHTTPLIB_OPENSSL_SUPPORT
+	)
+	set(HTTPLIB_IS_USING_OPENSSL TRUE)
+else()
+	set(HTTPLIB_IS_USING_OPENSSL FALSE)
+endif()
+
+# We check for the target when using IF_AVAILABLE since it's possible we didn't find it.
+if(HTTPLIB_USE_ZLIB_IF_AVAILABLE AND TARGET ZLIB::ZLIB OR HTTPLIB_REQUIRE_ZLIB)
+	target_link_libraries(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
+		ZLIB::ZLIB
+	)
+	target_compile_definitions(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
+		CPPHTTPLIB_ZLIB_SUPPORT
+	)
+	set(HTTPLIB_IS_USING_ZLIB TRUE)
+else()
+	set(HTTPLIB_IS_USING_ZLIB FALSE)
+endif()
+
 # Cmake's find_package search path is different based on the system
 # Cmake's find_package search path is different based on the system
 # See https://cmake.org/cmake/help/latest/command/find_package.html for the list
 # See https://cmake.org/cmake/help/latest/command/find_package.html for the list
 if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
 if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
@@ -144,7 +214,7 @@ install(TARGETS ${PROJECT_NAME}
 	ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
 	ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
 )
 )
 
 
-install(FILES httplib.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
+install(FILES "${_httplib_build_includedir}/httplib.h" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
 
 
 install(FILES
 install(FILES
 		"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
 		"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"

+ 16 - 21
httplibConfig.cmake.in

@@ -1,33 +1,28 @@
 # Generates a macro to auto-configure everything
 # Generates a macro to auto-configure everything
 @PACKAGE_INIT@
 @PACKAGE_INIT@
 
 
+# Setting these here so they're accessible after install.
+# Might be useful for some users to check which settings were used.
+set(HTTPLIB_IS_USING_OPENSSL @HTTPLIB_IS_USING_OPENSSL@)
+set(HTTPLIB_IS_USING_ZLIB @HTTPLIB_IS_USING_ZLIB@)
+set(HTTPLIB_IS_COMPILED @HTTPLIB_COMPILE@)
+
 include(CMakeFindDependencyMacro)
 include(CMakeFindDependencyMacro)
 
 
 # We add find_dependency calls here to not make the end-user have to call them.
 # We add find_dependency calls here to not make the end-user have to call them.
 find_dependency(Threads REQUIRED)
 find_dependency(Threads REQUIRED)
-if(@HTTPLIB_REQUIRE_OPENSSL@)
-	find_dependency(OpenSSL @_HTTPLIB_OPENSSL_MIN_VER@ REQUIRED)
-	# Lets you check if these options were correctly enabled for your install
-	set(HTTPLIB_IS_USING_OPENSSL TRUE)
-elseif(@HTTPLIB_USE_OPENSSL_IF_AVAILABLE@)
-	# Look quietly since it's optional
-	find_dependency(OpenSSL @_HTTPLIB_OPENSSL_MIN_VER@ QUIET)
-	# Lets you check if these options were correctly enabled for your install
-	set(HTTPLIB_IS_USING_OPENSSL @OPENSSL_FOUND@)
-else()
-	set(HTTPLIB_IS_USING_OPENSSL FALSE)
+if(@HTTPLIB_IS_USING_OPENSSL@)
+	# OpenSSL COMPONENTS were added in Cmake v3.11
+	if(CMAKE_VERSION VERSION_LESS "3.11")
+		find_dependency(OpenSSL @_HTTPLIB_OPENSSL_MIN_VER@ REQUIRED)
+	else()
+		# Once the COMPONENTS were added, they were made optional when not specified.
+		# Since we use both, we need to search for both.
+		find_dependency(OpenSSL @_HTTPLIB_OPENSSL_MIN_VER@ COMPONENTS Crypto SSL REQUIRED)
+	endif()
 endif()
 endif()
-if(@HTTPLIB_REQUIRE_ZLIB@)
+if(@HTTPLIB_IS_USING_ZLIB@)
 	find_dependency(ZLIB REQUIRED)
 	find_dependency(ZLIB REQUIRED)
-	# Lets you check if these options were correctly enabled for your install
-	set(HTTPLIB_IS_USING_ZLIB TRUE)
-elseif(@HTTPLIB_USE_ZLIB_IF_AVAILABLE@)
-	# Look quietly since it's optional
-	find_dependency(ZLIB QUIET)
-	# Lets you check if these options were correctly enabled for your install
-	set(HTTPLIB_IS_USING_ZLIB @ZLIB_FOUND@)
-else()
-	set(HTTPLIB_IS_USING_ZLIB FALSE)
 endif()
 endif()
 
 
 # Lets the end-user find the header path if needed
 # Lets the end-user find the header path if needed