|
1 | 1 | cmake_minimum_required(VERSION 3.15...3.26) |
2 | | - |
3 | 2 | project(compas_libigl LANGUAGES CXX) |
4 | 3 |
|
5 | 4 | set(CMAKE_CXX_STANDARD 20) |
6 | | -set(CMAKE_CXX_STANDARD_REQUIRED ON) |
7 | 5 | set(CMAKE_CXX_EXTENSIONS OFF) |
8 | | -set(CMAKE_BUILD_TYPE Release) |
9 | | - |
10 | | -# ===================================================================== |
11 | | -# Set this flag to ON for developing to reduce build time. |
12 | | -# Set this flag to OFF for publishing for file size reduction. |
13 | | -# ===================================================================== |
14 | | -option(ENABLE_PRECOMPILED_HEADERS "Enable precompiled headers for the build" ON) |
15 | 6 |
|
16 | | -# ===================================================================== |
17 | | -# Set maximum heap size for MSVC |
18 | | -# ===================================================================== |
| 7 | +option(ENABLE_PRECOMPILED_HEADERS "Enable precompiled headers" OFF) |
| 8 | +option(MULTITHREADED_COMPILATION "Enable multi-threaded compilation (Ninja only)" ON) |
19 | 9 |
|
20 | | -if(MSVC) |
21 | | - set(CMAKE_GENERATOR_PLATFORM x64) |
22 | | - add_compile_options(/Zm1200) |
| 10 | +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) |
| 11 | + message(STATUS "Setting build type to 'Release' as none was specified.") |
| 12 | + set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build." FORCE) |
| 13 | + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") |
23 | 14 | endif() |
24 | 15 |
|
25 | | -# ===================================================================== |
26 | | -# Build size reduction. |
27 | | -# ===================================================================== |
28 | | - |
29 | | -if (NOT ENABLE_PRECOMPILED_HEADERS) |
30 | | - set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) |
31 | | - if(MSVC) |
32 | | - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /O1") # Optimize for size on MSVC |
33 | | - else() |
34 | | - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Os") # Optimize for size on GCC/Clang |
| 16 | +if(MULTITHREADED_COMPILATION) |
| 17 | + include(ProcessorCount) |
| 18 | + ProcessorCount(N) |
| 19 | + if(NOT N EQUAL 0) |
| 20 | + message(STATUS "Using ${N} build jobs.") |
| 21 | + set(CMAKE_PARALLEL_LEVEL ${N}) |
| 22 | + if(CMAKE_GENERATOR MATCHES "^Ninja") |
| 23 | + set(CMAKE_JOB_POOL_COMPILE compile) |
| 24 | + set(CMAKE_JOB_POOL_LINK link) |
| 25 | + set(CMAKE_JOB_POOLS "compile=${N}" "link=2") |
35 | 26 | endif() |
| 27 | + endif() |
36 | 28 | endif() |
37 | 29 |
|
38 | | -# ===================================================================== |
39 | | -# Dependencies |
40 | | -# ===================================================================== |
41 | | -include(ExternalProject) |
42 | | - |
43 | | -# Define source directories for external dependencies |
44 | | -set(EXTERNAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external") |
45 | | -set(EIGEN_SOURCE_DIR "${EXTERNAL_DIR}/eigen") |
46 | | -set(LIBIGL_SOURCE_DIR "${EXTERNAL_DIR}/libigl") |
47 | | - |
48 | | -# Create directories if they don't exist |
49 | | -file(MAKE_DIRECTORY ${EXTERNAL_DIR}) |
50 | | -file(MAKE_DIRECTORY ${EIGEN_SOURCE_DIR}) |
51 | | -file(MAKE_DIRECTORY ${LIBIGL_SOURCE_DIR}) |
52 | | - |
53 | | -# Download Eigen first |
54 | | -if(NOT EXISTS "${EIGEN_SOURCE_DIR}/Eigen") |
55 | | - message(STATUS "Downloading Eigen...") |
56 | | - ExternalProject_Add( |
57 | | - eigen_download |
58 | | - PREFIX ${EXTERNAL_DIR} |
59 | | - URL https://gitlab.com/libeigen/eigen/-/archive/3.4.0/eigen-3.4.0.tar.gz |
60 | | - SOURCE_DIR "${EIGEN_SOURCE_DIR}" |
61 | | - CONFIGURE_COMMAND "" |
62 | | - BUILD_COMMAND "" |
63 | | - INSTALL_COMMAND "" |
64 | | - LOG_DOWNLOAD ON |
65 | | - UPDATE_COMMAND "" |
66 | | - PATCH_COMMAND "" |
67 | | - ) |
| 30 | +if(UNIX AND NOT APPLE) |
| 31 | + if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) |
| 32 | + set(CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}/share/${PROJECT_NAME}" CACHE PATH "Install prefix" FORCE) |
| 33 | + endif() |
68 | 34 | endif() |
69 | 35 |
|
70 | | -# Download libigl after Eigen |
71 | | -if(NOT EXISTS "${LIBIGL_SOURCE_DIR}/include/igl") |
72 | | - message(STATUS "Downloading libigl...") |
73 | | - ExternalProject_Add( |
74 | | - libigl_download |
75 | | - DEPENDS eigen_download |
76 | | - PREFIX ${EXTERNAL_DIR} |
77 | | - URL https://github.com/libigl/libigl/archive/refs/heads/main.zip |
78 | | - SOURCE_DIR "${LIBIGL_SOURCE_DIR}" |
79 | | - CONFIGURE_COMMAND "" |
80 | | - BUILD_COMMAND "" |
81 | | - INSTALL_COMMAND "" |
82 | | - LOG_DOWNLOAD ON |
83 | | - UPDATE_COMMAND "" |
84 | | - PATCH_COMMAND "" |
85 | | - DOWNLOAD_EXTRACT_TIMESTAMP TRUE |
86 | | - ) |
| 36 | +# Use custom directory for 3rd-party dependencies (optional but persistent) |
| 37 | +set(FETCHCONTENT_BASE_DIR "${CMAKE_SOURCE_DIR}/.third_party") |
| 38 | +set(FETCHCONTENT_QUIET OFF) |
| 39 | +set(FETCHCONTENT_UPDATES_DISCONNECTED ON) |
| 40 | + |
| 41 | +include(FetchContent) |
| 42 | + |
| 43 | +# === Eigen === |
| 44 | +# Check if Eigen already exists |
| 45 | +set(EIGEN_DIR "${FETCHCONTENT_BASE_DIR}/eigen-src") |
| 46 | +if(EXISTS "${EIGEN_DIR}") |
| 47 | + message(STATUS "Using existing Eigen from ${EIGEN_DIR}") |
| 48 | + set(eigen_SOURCE_DIR "${EIGEN_DIR}") |
| 49 | + set(eigen_BINARY_DIR "${FETCHCONTENT_BASE_DIR}/eigen-build") |
87 | 50 | endif() |
88 | 51 |
|
89 | | -# Create a custom target for all external dependencies |
90 | | -add_custom_target(external_downloads ALL) |
91 | | -if(TARGET eigen_download) |
92 | | - add_dependencies(external_downloads eigen_download) |
| 52 | +FetchContent_Declare( |
| 53 | + eigen |
| 54 | + URL https://gitlab.com/libeigen/eigen/-/archive/3.4.0/eigen-3.4.0.tar.gz |
| 55 | + SOURCE_DIR "${eigen_SOURCE_DIR}" |
| 56 | + BINARY_DIR "${eigen_BINARY_DIR}" |
| 57 | + DOWNLOAD_EXTRACT_TIMESTAMP TRUE |
| 58 | +) |
| 59 | +FetchContent_MakeAvailable(eigen) |
| 60 | + |
| 61 | +# === libigl === |
| 62 | +# Check if libigl already exists |
| 63 | +set(LIBIGL_DIR "${FETCHCONTENT_BASE_DIR}/libigl-src") |
| 64 | +if(EXISTS "${LIBIGL_DIR}") |
| 65 | + message(STATUS "Using existing libigl from ${LIBIGL_DIR}") |
| 66 | + set(libigl_SOURCE_DIR "${LIBIGL_DIR}") |
| 67 | + set(libigl_BINARY_DIR "${FETCHCONTENT_BASE_DIR}/libigl-build") |
93 | 68 | endif() |
94 | | -if(TARGET libigl_download) |
95 | | - add_dependencies(external_downloads libigl_download) |
| 69 | + |
| 70 | +FetchContent_Declare( |
| 71 | + libigl |
| 72 | + GIT_REPOSITORY https://github.com/libigl/libigl.git |
| 73 | + GIT_TAG main |
| 74 | + SOURCE_DIR "${libigl_SOURCE_DIR}" |
| 75 | + BINARY_DIR "${libigl_BINARY_DIR}" |
| 76 | +) |
| 77 | +FetchContent_MakeAvailable(libigl) |
| 78 | + |
| 79 | +# === Clipper2 === |
| 80 | +# Check if Clipper2 already exists |
| 81 | +set(CLIPPER2_DIR "${FETCHCONTENT_BASE_DIR}/clipper2-src") |
| 82 | +if(EXISTS "${CLIPPER2_DIR}") |
| 83 | + message(STATUS "Using existing Clipper2 from ${CLIPPER2_DIR}") |
| 84 | + set(clipper2_SOURCE_DIR "${CLIPPER2_DIR}") |
| 85 | + set(clipper2_BINARY_DIR "${FETCHCONTENT_BASE_DIR}/clipper2-build") |
96 | 86 | endif() |
97 | 87 |
|
98 | | -# Add include directories for external dependencies |
99 | | -set(EIGEN_INCLUDE_DIR "${EIGEN_SOURCE_DIR}") |
100 | | -set(LIBIGL_INCLUDE_DIR "${LIBIGL_SOURCE_DIR}/include") |
101 | | - |
102 | | -if (NOT SKBUILD) |
103 | | - message(WARNING "\ |
104 | | - This CMake file is meant to be executed using 'scikit-build'. Running |
105 | | - it directly will almost certainly not produce the desired result. If |
106 | | - you are a user trying to install this package, please use the command |
107 | | - below, which will install all necessary build dependencies, compile |
108 | | - the package in an isolated environment, and then install it. |
109 | | - ===================================================================== |
110 | | - $ pip install . |
111 | | - ===================================================================== |
112 | | - If you are a software developer, and this is your own package, then |
113 | | - it is usually much more efficient to install the build dependencies |
114 | | - in your environment once and use the following command that avoids |
115 | | - a costly creation of a new virtual environment at every compilation: |
116 | | - ===================================================================== |
117 | | - $ pip install nanobind scikit-build-core[pyproject] |
118 | | - $ pip install --no-build-isolation -ve . |
119 | | - ===================================================================== |
120 | | - You may optionally add -Ceditable.rebuild=true to auto-rebuild when |
121 | | - the package is imported. Otherwise, you need to re-run the above |
122 | | - after editing C++ files.") |
| 88 | +FetchContent_Declare( |
| 89 | + clipper2 |
| 90 | + URL https://github.com/AngusJohnson/Clipper2/releases/download/Clipper2_1.5.3/Clipper2_1.5.3.zip |
| 91 | + SOURCE_DIR "${clipper2_SOURCE_DIR}" |
| 92 | + BINARY_DIR "${clipper2_BINARY_DIR}" |
| 93 | + DOWNLOAD_EXTRACT_TIMESTAMP TRUE |
| 94 | +) |
| 95 | +FetchContent_MakeAvailable(clipper2) |
| 96 | + |
| 97 | +# Build Clipper2 as a static library |
| 98 | +file(GLOB CLIPPER2_SRC "${clipper2_SOURCE_DIR}/CPP/Clipper2Lib/src/*.cpp") |
| 99 | + |
| 100 | +add_library(clipper2_static STATIC ${CLIPPER2_SRC}) |
| 101 | +target_include_directories(clipper2_static PUBLIC "${clipper2_SOURCE_DIR}/CPP/Clipper2Lib/include") |
| 102 | +set_target_properties(clipper2_static PROPERTIES POSITION_INDEPENDENT_CODE ON) |
| 103 | + |
| 104 | +# Install clipper2 static lib and headers if not already installed |
| 105 | +set(CLIPPER2_HEADERS_DEST "${CMAKE_INSTALL_PREFIX}/include/clipper2") |
| 106 | +if(NOT EXISTS "${CLIPPER2_HEADERS_DEST}") |
| 107 | + message(STATUS "Installing Clipper2 headers and library") |
| 108 | + install(TARGETS clipper2_static ARCHIVE DESTINATION lib) |
| 109 | + install(DIRECTORY "${clipper2_SOURCE_DIR}/CPP/Clipper2Lib/include/clipper2" DESTINATION include) |
123 | 110 | endif() |
124 | 111 |
|
125 | | -# Find Python and nanobind |
126 | | -find_package(Python 3.8 |
127 | | - REQUIRED COMPONENTS Interpreter Development.Module |
128 | | - OPTIONAL_COMPONENTS Development.SABIModule) |
| 112 | +# === Include paths === |
| 113 | +set(EIGEN_INCLUDE_DIR ${eigen_SOURCE_DIR}) |
| 114 | +set(LIBIGL_INCLUDE_DIR ${libigl_SOURCE_DIR}/include) |
| 115 | +set(CLIPPER2_INCLUDE_DIR ${clipper2_SOURCE_DIR}/CPP/Clipper2Lib/include) |
| 116 | + |
| 117 | +# === Python & nanobind === |
| 118 | +find_package(Python 3.8 REQUIRED COMPONENTS Interpreter Development.Module OPTIONAL_COMPONENTS Development.SABIModule) |
129 | 119 | find_package(nanobind CONFIG REQUIRED) |
130 | 120 | find_package(Threads REQUIRED) |
131 | 121 |
|
132 | | -# Create a shared precompiled header library |
| 122 | +# === Precompiled headers === |
133 | 123 | if (ENABLE_PRECOMPILED_HEADERS) |
134 | 124 | add_library(compas_pch INTERFACE) |
135 | 125 | target_precompile_headers(compas_pch INTERFACE src/compas.hpp) |
136 | 126 | target_include_directories(compas_pch INTERFACE |
137 | 127 | ${EIGEN_INCLUDE_DIR} |
138 | 128 | ${LIBIGL_INCLUDE_DIR} |
| 129 | + ${CLIPPER2_INCLUDE_DIR} |
139 | 130 | ) |
140 | 131 | endif() |
141 | 132 |
|
142 | | -# Function to add a nanobind module with include directories |
| 133 | +# === Function to add nanobind modules === |
143 | 134 | function(add_nanobind_module module_name source_file) |
144 | 135 | nanobind_add_module(${module_name} STABLE_ABI NB_STATIC ${source_file}) |
| 136 | + add_dependencies(${module_name} clipper2_static) |
145 | 137 |
|
146 | | - # Ensure external dependencies are downloaded first |
147 | | - add_dependencies(${module_name} external_downloads) |
148 | | - |
149 | | - # Add include directories and link PCH if enabled |
150 | 138 | if (ENABLE_PRECOMPILED_HEADERS) |
151 | 139 | target_link_libraries(${module_name} PRIVATE compas_pch) |
152 | 140 | else() |
153 | 141 | target_include_directories(${module_name} SYSTEM PRIVATE |
154 | 142 | ${EIGEN_INCLUDE_DIR} |
155 | 143 | ${LIBIGL_INCLUDE_DIR} |
| 144 | + ${CLIPPER2_INCLUDE_DIR} |
156 | 145 | ) |
157 | 146 | endif() |
158 | 147 |
|
159 | | - target_link_libraries(${module_name} PRIVATE Threads::Threads) |
| 148 | + target_link_libraries(${module_name} PRIVATE Threads::Threads clipper2_static) |
160 | 149 | install(TARGETS ${module_name} LIBRARY DESTINATION compas_libigl) |
161 | 150 | endfunction() |
162 | 151 |
|
163 | | -# Add modules |
| 152 | +# === Add your modules === |
164 | 153 | add_nanobind_module(_nanobind src/nanobind.cpp) |
165 | 154 | add_nanobind_module(_types_std src/types_std.cpp) |
166 | 155 | add_nanobind_module(_boundaries src/boundaries.cpp) |
|
0 commit comments