11cmake_minimum_required (VERSION 3.15 )
2- project (docx_comment_parser VERSION 1.0.0 LANGUAGES CXX )
2+ project (docx_comment_parser VERSION 1.1.1 LANGUAGES CXX )
33
44# ─── Policy: normalise install() DESTINATION paths (fixes CMP0177 warning) ───
5- # Python3_SITEARCH on MinGW contains backslashes which CMake 3.26+ normalises
6- # automatically under the NEW policy. Setting it explicitly silences the warning
7- # on all CMake versions ≥ 3.15 that support CMP0177.
85if (POLICY CMP0177)
96 cmake_policy (SET CMP0177 NEW )
107endif ()
@@ -27,18 +24,114 @@ if(NOT MINGW)
2724 endif ()
2825endif ()
2926
30- # ─── Dependencies ─────────────────────────────────────────────────────────────
31- # No libxml2 required — self-contained XML parser.
32- #
33- # zlib strategy:
34- # MSVC — no system zlib exists. zip_reader.cpp compiles the vendored
35- # single-header inflate from vendor/zlib/zlib.h; no link step needed.
36- # MinGW — zlib1.dll + libz.a ship with every MinGW-w64 installation.
37- # Linux / macOS — system zlib (apt install zlib1g-dev / brew install zlib).
27+ # ─── zlib ─────────────────────────────────────────────────────────────────────
28+ # MSVC — vendored single-header inflate compiled into zip_reader.cpp; no link step.
29+ # MinGW — zlib1.dll + libz.a ship with every MinGW-w64 installation.
30+ # Linux / macOS — system zlib (apt install zlib1g-dev / brew install zlib).
3831if (NOT MSVC )
3932 find_package (ZLIB REQUIRED )
4033endif ()
4134
35+ # ─── Common compile options helper ────────────────────────────────────────────
36+ # Applied to every target that compiles the library sources.
37+ function (docx_apply_compile_options target )
38+ target_compile_options (${target} PRIVATE
39+ $<$<CXX_COMPILER_ID :GNU ,Clang >:-Wall -Wextra -Wpedantic >
40+ # -O3 / -DNDEBUG only for GCC/Clang; MSVC sets optimisation via build type.
41+ $<$<AND :$<CONFIG :Release >,$<CXX_COMPILER_ID :GNU ,Clang >>:-O3 -DNDEBUG >
42+ )
43+ endfunction ()
44+
45+ # ─── MinGW pthread detection ──────────────────────────────────────────────────
46+ # std::thread on MinGW with the POSIX threading model needs -lpthread.
47+ # With the Win32 model (default in most MSYS2 toolchains) no extra flag is needed.
48+ # -lws2_32 / -lmswsock are NOT needed (no socket code); -lmswsock in particular
49+ # is absent from some MinGW installations and was the previous cause of
50+ # "ld returned 5" (ERROR_ACCESS_DENIED / linker crash).
51+ if (MINGW)
52+ include (CheckCXXSourceCompiles )
53+ check_cxx_source_compiles ("
54+ #include <pthread.h>
55+ int main() { return 0; }
56+ " _has_pthread_h )
57+ endif ()
58+
59+ # ─── Helper: link zlib + pthreads for non-MSVC targets ───────────────────────
60+ function (docx_link_platform_libs target )
61+ target_link_libraries (${target} PRIVATE
62+ $<$<NOT :$<CXX_COMPILER_ID :MSVC >>:ZLIB ::ZLIB >
63+ )
64+ if (MINGW AND _has_pthread_h)
65+ target_link_libraries (${target} PRIVATE -lpthread )
66+ endif ()
67+ endfunction ()
68+
69+ # ═════════════════════════════════════════════════════════════════════════════
70+ # SCIKIT-BUILD-CORE (wheel) MODE
71+ # When scikit-build-core drives the build it defines SKBUILD.
72+ # In this mode we produce a single self-contained Python extension with all
73+ # C++ sources compiled in — no separate DLL. This mirrors what setup.py does
74+ # and avoids the MSVC LNK1149 "output filename matches input filename" error
75+ # that occurs when both docx_comment_parser.dll and docx_comment_parser.pyd
76+ # would generate the same docx_comment_parser.lib import library.
77+ # ═════════════════════════════════════════════════════════════════════════════
78+ if (DEFINED SKBUILD)
79+
80+ find_package (Python3 REQUIRED COMPONENTS Interpreter Development.Module )
81+ find_package (pybind11 CONFIG QUIET )
82+
83+ if (NOT pybind11_FOUND)
84+ execute_process (
85+ COMMAND ${Python3_EXECUTABLE} -c "import pybind11; print(pybind11.get_cmake_dir())"
86+ OUTPUT_VARIABLE _pybind11_cmake_dir
87+ ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE
88+ )
89+ if (_pybind11_cmake_dir)
90+ list (APPEND CMAKE_PREFIX_PATH "${_pybind11_cmake_dir} " )
91+ find_package (pybind11 CONFIG REQUIRED )
92+ endif ()
93+ endif ()
94+
95+ # Single module — all sources compiled in, no runtime DLL dependency.
96+ pybind11_add_module (docx_comment_parser MODULE
97+ python/python_bindings.cpp
98+ src/docx_parser.cpp
99+ src/batch_parser.cpp
100+ src/zip_reader.cpp
101+ src/xml_parser.cpp
102+ )
103+
104+ # DOCX_BUILDING_DLL is correct here: the extension IS building the symbols
105+ # (not importing them from a separate DLL).
106+ target_compile_definitions (docx_comment_parser PRIVATE DOCX_BUILDING_DLL )
107+
108+ target_include_directories (docx_comment_parser PRIVATE
109+ ${CMAKE_CURRENT_SOURCE_DIR} /include
110+ # Vendored zlib.h for MSVC (activated inside zip_reader.cpp)
111+ $<$<CXX_COMPILER_ID :MSVC >:${CMAKE_CURRENT_SOURCE_DIR} /vendor >
112+ )
113+
114+ docx_apply_compile_options (docx_comment_parser )
115+ docx_link_platform_libs (docx_comment_parser )
116+
117+ install (TARGETS docx_comment_parser
118+ DESTINATION "${SKBUILD_PLATLIB_DIR} "
119+ )
120+
121+ # Nothing else to build in wheel mode — skip the rest of this file.
122+ return ()
123+
124+ endif ()
125+
126+ # ═════════════════════════════════════════════════════════════════════════════
127+ # REGULAR CMAKE BUILD MODE
128+ # Builds the standalone shared library + optional Python extension that links
129+ # against it. The Python extension uses a distinct CMake target name
130+ # (docx_py_ext) to avoid colliding with the shared library target, but sets
131+ # OUTPUT_NAME so the .pyd/.so file is named "docx_comment_parser" to match
132+ # PYBIND11_MODULE(docx_comment_parser, m) in python_bindings.cpp.
133+ # ═════════════════════════════════════════════════════════════════════════════
134+
42135# ─── Core shared library ──────────────────────────────────────────────────────
43136add_library (docx_comment_parser SHARED
44137 src/docx_parser.cpp
@@ -47,59 +140,36 @@ add_library(docx_comment_parser SHARED
47140 src/xml_parser.cpp
48141)
49142
50- # Tell the compiler which TUs are *building* the DLL so DOCX_API expands to
51- # dllexport; consumers that only include the header get dllimport instead.
52143target_compile_definitions (docx_comment_parser PRIVATE DOCX_BUILDING_DLL )
53144
54145target_include_directories (docx_comment_parser
55146 PUBLIC
56147 $<BUILD_INTERFACE :${CMAKE_CURRENT_SOURCE_DIR} /include >
57148 $<INSTALL_INTERFACE :include >
58149 PRIVATE
59- # On MSVC the vendored zlib.h is included by zip_reader.cpp via a
60- # relative path "../vendor/zlib/zlib.h". Adding vendor/ here makes
61- # the path work regardless of which directory cl.exe is invoked from.
62150 $<$<CXX_COMPILER_ID :MSVC >:${CMAKE_CURRENT_SOURCE_DIR} /vendor >
63151)
64152
65- target_link_libraries (docx_comment_parser
66- PRIVATE
67- # Link system zlib on non-MSVC platforms only.
68- # On MSVC, inflate is compiled directly into zip_reader.cpp.
69- $<$<NOT :$<CXX_COMPILER_ID :MSVC >>:ZLIB ::ZLIB >
70- )
153+ docx_apply_compile_options (docx_comment_parser )
154+ docx_link_platform_libs (docx_comment_parser )
71155
72- # On MinGW, link the standard threading and networking libs that std::thread
73- # and socket code pull in indirectly.
74- if (MINGW)
75- target_link_libraries (docx_comment_parser PRIVATE -lws2_32 -lmswsock )
76- endif ()
77-
78- # Symbol visibility — ELF only. On Windows (PE/DLL) visibility is handled by
79- # __declspec(dllexport/dllimport) in the header; the GCC attribute is a no-op
80- # there but can confuse some linker versions, so guard it explicitly.
156+ # Symbol visibility — ELF only.
81157if (NOT WIN32 )
82158 set_target_properties (docx_comment_parser PROPERTIES
83- CXX_VISIBILITY_PRESET hidden
159+ CXX_VISIBILITY_PRESET hidden
84160 VISIBILITY_INLINES_HIDDEN ON
85161 )
86162endif ()
87163
88- # VERSION / SOVERSION are ELF-only concepts (produce .so.1 symlinks on Linux).
89- # MinGW/Windows uses a different DLL versioning mechanism; setting these
90- # properties on a PE target causes the ld "error: ld returned 5" link failure.
164+ # VERSION / SOVERSION are ELF-only (.so.1 symlinks on Linux).
165+ # Setting them on a PE/DLL target causes MinGW ld to fail with exit code 5.
91166if (NOT WIN32 )
92167 set_target_properties (docx_comment_parser PROPERTIES
93168 VERSION ${PROJECT_VERSION}
94169 SOVERSION 1
95170 )
96171endif ()
97172
98- target_compile_options (docx_comment_parser PRIVATE
99- $<$<CXX_COMPILER_ID :GNU ,Clang >:-Wall -Wextra -Wpedantic >
100- $<$<CONFIG :Release >:-O3 -DNDEBUG >
101- )
102-
103173# ─── Python extension (optional) ──────────────────────────────────────────────
104174option (BUILD_PYTHON_BINDINGS "Build Python bindings via pybind11" ON )
105175
@@ -108,7 +178,6 @@ if(BUILD_PYTHON_BINDINGS)
108178 find_package (pybind11 CONFIG QUIET )
109179
110180 if (NOT pybind11_FOUND)
111- # Locate pybind11 installed via pip
112181 execute_process (
113182 COMMAND ${Python3_EXECUTABLE} -c "import pybind11; print(pybind11.get_cmake_dir())"
114183 OUTPUT_VARIABLE _pybind11_cmake_dir
@@ -121,28 +190,37 @@ if(BUILD_PYTHON_BINDINGS)
121190 endif ()
122191
123192 if (pybind11_FOUND)
124- pybind11_add_module (_docx_comment_parser
193+ # Distinct CMake target name avoids collision with the SHARED library
194+ # target above. OUTPUT_NAME makes the file "docx_comment_parser.pyd"
195+ # so Python's import machinery finds PyInit_docx_comment_parser.
196+ # (The old name "_docx_comment_parser" caused an ImportError because
197+ # Python looked for PyInit__docx_comment_parser — note the double
198+ # underscore — which never existed.)
199+ pybind11_add_module (docx_py_ext MODULE
125200 python/python_bindings.cpp
126201 )
127202
128- # The extension builds *into* the DLL — it also needs DOCX_BUILDING_DLL
129- # so that DOCX_API expands to dllexport when compiling its TU on Windows.
130- target_compile_definitions ( _docx_comment_parser PRIVATE DOCX_BUILDING_DLL )
203+ set_target_properties ( docx_py_ext PROPERTIES
204+ OUTPUT_NAME "docx_comment_parser"
205+ )
131206
132- target_include_directories (_docx_comment_parser PRIVATE
207+ # Do NOT define DOCX_BUILDING_DLL: this extension links against the
208+ # shared library above, so DOCX_API must expand to __declspec(dllimport).
209+ target_include_directories (docx_py_ext PRIVATE
133210 ${CMAKE_CURRENT_SOURCE_DIR} /include
134- # Vendor dir for MSVC (same reason as the main library target)
135211 $<$<CXX_COMPILER_ID :MSVC >:${CMAKE_CURRENT_SOURCE_DIR} /vendor >
136212 )
137213
138- target_link_libraries (_docx_comment_parser PRIVATE
214+ target_link_libraries (docx_py_ext PRIVATE
139215 docx_comment_parser
140216 $<$<NOT :$<CXX_COMPILER_ID :MSVC >>:ZLIB ::ZLIB >
141217 )
142218
143- # CMP0177 NEW: CMake normalises the path before passing it to install(),
144- # so backslashes from Python3_SITEARCH on Windows are converted to slashes.
145- install (TARGETS _docx_comment_parser
219+ if (MINGW AND _has_pthread_h)
220+ target_link_libraries (docx_py_ext PRIVATE -lpthread )
221+ endif ()
222+
223+ install (TARGETS docx_py_ext
146224 LIBRARY DESTINATION "${Python3_SITEARCH} "
147225 RUNTIME DESTINATION "${Python3_SITEARCH} "
148226 )
0 commit comments