Skip to content

Commit bec25f7

Browse files
nicknick
authored andcommitted
updated cmake to solve compilation issues with msc and mingw64 generators
1 parent 5752afd commit bec25f7

4 files changed

Lines changed: 190 additions & 74 deletions

File tree

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@
77
BUILDING.md
88
/.vscode
99
/build
10-
/dist
10+
/dist
11+
.pypirc
12+
/build_mingw64

CMakeLists.txt

Lines changed: 130 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
cmake_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.
85
if(POLICY CMP0177)
96
cmake_policy(SET CMP0177 NEW)
107
endif()
@@ -27,18 +24,114 @@ if(NOT MINGW)
2724
endif()
2825
endif()
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).
3831
if(NOT MSVC)
3932
find_package(ZLIB REQUIRED)
4033
endif()
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 ──────────────────────────────────────────────────────
43136
add_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.
52143
target_compile_definitions(docx_comment_parser PRIVATE DOCX_BUILDING_DLL)
53144

54145
target_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.
81157
if(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
)
86162
endif()
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.
91166
if(NOT WIN32)
92167
set_target_properties(docx_comment_parser PROPERTIES
93168
VERSION ${PROJECT_VERSION}
94169
SOVERSION 1
95170
)
96171
endif()
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) ──────────────────────────────────────────────
104174
option(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
)

pyproject.toml

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
[build-system]
2-
requires = ["setuptools", "pybind11"]
3-
build-backend = "setuptools.build_meta"
2+
# scikit-build-core invokes CMake automatically, produces correctly tagged
3+
# platform wheels (e.g. cp311-cp311-win_amd64), and handles MSVC / MinGW /
4+
# GCC / Clang toolchains without any extra configuration.
5+
requires = ["scikit-build-core>=0.9", "pybind11>=2.12"]
6+
build-backend = "scikit_build_core.build"
47

58
[project]
69
name = "docx-comment-parser"
7-
version = "1.0.0"
10+
version = "1.1.1"
811
description = "Fast C++ library for extracting comment metadata from .docx files"
912
readme = "README.md"
1013
requires-python = ">=3.8"
@@ -16,3 +19,17 @@ classifiers = [
1619
"Operating System :: Microsoft :: Windows",
1720
"Operating System :: MacOS",
1821
]
22+
23+
[tool.scikit-build]
24+
# Always build in Release mode for distributed wheels.
25+
cmake.build-type = "Release"
26+
27+
# Pass extra CMake args:
28+
# - disable the test suite (not needed inside the wheel)
29+
# - disable the standalone shared library (the wheel only needs the Python
30+
# extension; the extension itself compiles all C++ sources in via setup.py)
31+
cmake.args = ["-DBUILD_TESTS=OFF"]
32+
33+
# The extension module is the only thing that goes into the wheel.
34+
# No Python source packages to collect.
35+
wheel.packages = []

0 commit comments

Comments
 (0)