-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathCMakeLists.txt
More file actions
207 lines (160 loc) · 7.32 KB
/
Copy pathCMakeLists.txt
File metadata and controls
207 lines (160 loc) · 7.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
cmake_minimum_required(VERSION 3.19)
option(BUILD_PYTHON "export python module" off)
set(CMAKE_CXX_STANDARD 17 CACHE STRING "C++ version selection")
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
include(DynamicVersion)
extract_version_string(
HEADER_FILE ${CMAKE_CURRENT_LIST_DIR}/include/urlparser.h
VERSION_PREFIX URLPARSER_
OUTPUT_VAR PROJECT_VERSION
)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
endif()
project(liburlparser LANGUAGES CXX VERSION ${PROJECT_VERSION})
message(STATUS "Project: ${PROJECT_NAME}@v${PROJECT_VERSION}")
# Kept for docs/cpp templates (Doxyfile.in, footer.html.in) that still
# reference @PyProject_VERSION@.
set(PyProject_VERSION ${PROJECT_VERSION})
if (MSVC)
add_compile_options(/EHsc)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
endif()
if (CYGWIN OR UNIX)
add_compile_definitions(_GNU_SOURCE)
endif()
find_package(Python3 REQUIRED COMPONENTS Interpreter)
# --- httpp local patch --------------------------------------------------
# Only treat this as "liburlparser is the scikit-build-core package being
# installed" when SKBUILD_PROJECT_NAME actually IS liburlparser. Without
# this guard, vendoring liburlparser as a subdirectory of another
# scikit-build-core project (e.g. httpp) makes it wrongly think it's the
# top-level package and try to build its own nanobind bindings, which
# fails when nanobind isn't installed (it isn't a dependency of the
# parent project at all). See src/third_party/README.md for details.
if(DEFINED SKBUILD AND SKBUILD_PROJECT_NAME STREQUAL PROJECT_NAME)
set(PYTHON_PROJECT_NAME "${SKBUILD_PROJECT_NAME}")
elseif(BUILD_PYTHON)
set(PYTHON_PROJECT_NAME "${CMAKE_BINARY_DIR}")
execute_process(
COMMAND "${Python3_EXECUTABLE}" -m nanobind --cmake_dir
OUTPUT_STRIP_TRAILING_WHITESPACE
OUTPUT_VARIABLE NB_DIR
ERROR_QUIET
RESULT_VARIABLE NANOBIND_CHECK_RESULT
)
if(NOT NANOBIND_CHECK_RESULT EQUAL 0 OR NOT NB_DIR)
message(WARNING "nanobind not found for ${Python3_EXECUTABLE}.")
message(WARNING "Please install build requirements with: pip install -r requirements-dev.txt")
else()
message(STATUS "Found NanoBind at ${NB_DIR}")
list(APPEND CMAKE_PREFIX_PATH "${NB_DIR}")
endif()
endif()
# Fetch the Public Suffix List: tries a fresh download first every
# configure (cached per build directory via a marker file - see
# cmake/PublicSuffixList.cmake for why), overwriting the one real,
# tracked copy at src/public_suffix_list.dat in place. On a failed
# download it just warns and keeps using that existing file. The URL
# itself lives in a single place: URLPARSER_PUBLIC_SUFFIX_LIST_URL in
# include/urlparser.h.
include(${PROJECT_SOURCE_DIR}/cmake/PublicSuffixList.cmake)
fetch_public_suffix_list(
HEADER_FILE ${PROJECT_SOURCE_DIR}/include/urlparser.h
VERSION_PREFIX URLPARSER_
TARGET_FILE ${PROJECT_SOURCE_DIR}/src/public_suffix_list.dat
OUTPUT_VAR PUBLIC_SUFFIX_LIST_DAT
URL_OUTPUT_VAR PUBLIC_SUFFIX_LIST_URL
)
file(GLOB SOURCES ${PROJECT_SOURCE_DIR}/src/*.cpp)
# Embed the PSL data file into a C++ header at CONFIGURE time (so it exists
# right after `cmake -B build`, not only after the first build). This
# removes the need to ship/locate a .dat file at runtime entirely.
include(${PROJECT_SOURCE_DIR}/cmake/EmbedFile.cmake)
set(GENERATED_HEADERS_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated")
set(PUBLIC_SUFFIX_LIST_DAT_H "${GENERATED_HEADERS_DIR}/public_suffix_list_dat.h")
embed_file_as_string(
"${PUBLIC_SUFFIX_LIST_DAT}"
"${PUBLIC_SUFFIX_LIST_DAT_H}"
"templates"
"public_suffix_list_dat"
)
if(DEFINED PYTHON_PROJECT_NAME)
# Try to import all Python components potentially needed by nanobind
#set(Python3_FIND_STRATEGY LOCATION)
find_package(Python 3.10
REQUIRED COMPONENTS Interpreter Development.Module
OPTIONAL_COMPONENTS Development.SABIModule)
# Import nanobind through CMake's find_package mechanism
find_package(nanobind CONFIG REQUIRED)
set(NB_MODULE _urlparser_py)
# Linked STABLE_ABI needs Python >= 3.12 (nanobind's floor for the
# in-process stable ABI). Below that, build a regular per-version
# extension instead of reaching for split-mode (STABLE_ABI +
# BACKEND_MODULE): split-mode ships no nanobind runtime in the wheel
# itself and instead depends on a separate 'nanobind-backend' package
# at import time - a package that, in practice, isn't reliably
# available for every interpreter/platform combination our CI builds
# (manylinux cp310 in particular), so installs can fail outright with
# "No matching distribution found for nanobind-backend". Linking the
# nanobind runtime straight into the extension avoids that dependency
# entirely, at the cost of one wheel per Python minor version below 3.12.
if(Python_VERSION VERSION_GREATER_EQUAL "3.12")
set(NB_ABI_ARGS STABLE_ABI)
else()
set(NB_ABI_ARGS)
endif()
# We are now ready to compile the actual extension module
nanobind_add_module(
# Name of the extension
${NB_MODULE}
${NB_ABI_ARGS}
# Source code goes here
${PROJECT_SOURCE_DIR}/src/bindings/python/binding_py.cpp ${SOURCES}
)
target_include_directories(${NB_MODULE} PRIVATE include src ${GENERATED_HEADERS_DIR})
if(DEFINED SKBUILD)
install(TARGETS ${NB_MODULE}
CONFIGURATIONS Release
LIBRARY DESTINATION
${PYTHON_PROJECT_NAME})
else()
file(COPY ${PROJECT_SOURCE_DIR}/src/bindings/python/${PROJECT_NAME} DESTINATION ${PYTHON_PROJECT_NAME})
set_target_properties(${NB_MODULE} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PYTHON_PROJECT_NAME}/${PROJECT_NAME})
endif()
endif()
if(DEFINED SKBUILD AND SKBUILD_PROJECT_NAME STREQUAL PROJECT_NAME)
# (see the httpp local-patch comment above) only early-return here when
# liburlparser itself is the package scikit-build-core is installing —
# not when it's vendored as a subdirectory of another project.
RETURN()
endif()
add_library(urlparser STATIC ${SOURCES})
add_library(url::base ALIAS urlparser)
target_include_directories(urlparser PRIVATE ${GENERATED_HEADERS_DIR})
target_include_directories(urlparser
PUBLIC
${PROJECT_SOURCE_DIR}/include
PRIVATE
${PROJECT_SOURCE_DIR}/src)
add_executable(example examples/main.cpp)
target_link_libraries(example PRIVATE url::base)
install(TARGETS urlparser
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
INCLUDES DESTINATION include
)
enable_testing()
find_package(Threads REQUIRED)
# Only build tests/docs when liburlparser is the top-level project. When
# it's pulled in as a dependency (e.g. FetchContent from benchmarks/ or a
# consumer's own build), building its entire test suite and documentation
# pipeline as a side effect is wasted work - and tests/docs assume they can
# write into this source tree (public_suffix_list.dat, docs/out, coverage),
# which isn't guaranteed when embedded read-only inside another build.
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/tests)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/docs)
endif()