-
Notifications
You must be signed in to change notification settings - Fork 46
/
CMakeLists.txt
621 lines (560 loc) · 24.7 KB
/
CMakeLists.txt
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
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
cmake_minimum_required(VERSION 3.15...3.22)
# get version string from version.hpp
file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/include/gemmi/version.hpp"
gemmi_version_line REGEX "#define GEMMI_VERSION \"")
string(REGEX REPLACE ".+\"([0-9]+\.[0-9]+\.[0-9]+)(-dev)?\"" "\\1"
gemmi_version_str ${gemmi_version_line})
project(gemmi LANGUAGES C CXX VERSION ${gemmi_version_str})
message(STATUS "Gemmi version ${PROJECT_VERSION}")
include(GNUInstallDirs) # for CMAKE_INSTALL_LIBDIR, etc
if (DEFINED SKBUILD) # building with scikit-build-core (pip install)
# we set wheel.install-dir="/data" in pyproject.toml,
# so here we need to only set paths to /platlib and /scripts
set(PYTHON_INSTALL_DIR "${SKBUILD_PLATLIB_DIR}")
#set(CMAKE_INSTALL_BINDIR "${SKBUILD_SCRIPTS_DIR}")
endif()
option(BUILD_SHARED_LIBS "Build shared library" ON)
option(BUILD_GEMMI_PROGRAM "Build gemmi command-line program" ON)
option(INSTALL_DEV_FILES "Install C++ headers, cmake configs, static lib" ON)
option(USE_FORTRAN "Build Fortran bindings" OFF)
option(USE_PYTHON "Build Python bindings" OFF)
option(USE_ZLIB_NG "Use zlib-ng instead of zlib" OFF)
option(FETCH_ZLIB_NG "Download, build (statically) and use zlib-ng" OFF)
option(INTERNAL_ZLIB "Use subset of zlib distributed with gemmi" OFF)
option(GENERATE_STUBS "Generate Python type stubs" ON)
option(EXTRA_WARNINGS "Set extra warning flags" OFF)
option(USE_WMAIN "(Windows only) take Unicode arguments in gemmi program" ON)
option(STANDALONE_PYTHON_MODULE "Avoid linking Python module to libgemmi_cpp DLL" ON)
if (WIN32)
set(GEMMI_INSTALL_CMAKEDIR "cmake" CACHE STRING
"Install path for gemmi CMake files")
else()
set(GEMMI_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/gemmi" CACHE STRING
"Install path for gemmi CMake files")
endif()
option(STRIP_BINARY "Strip symbols from program" OFF)
option(ADD_SOVERSION "For shared library (on Linux):\
set ABI version to 0, build versions to project version" OFF)
option(USE_NLOPT "Experimental, for testing only, don't use it" OFF)
# uncomment to show compilation times for each compilation unit
#set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "\"${CMAKE_COMMAND}\" -E time")
if (ONLY_PROGRAM)
set(USE_PYTHON OFF CACHE BOOL "" FORCE) # it's set to ON in pyproject.toml
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
set(INSTALL_DEV_FILES OFF CACHE BOOL "" FORCE)
else()
if ((DEFINED ENV{FC} AND NOT "$ENV{FC}" STREQUAL "") OR CMAKE_Fortran_COMPILER)
set(USE_FORTRAN ON CACHE BOOL "Build Fortran bindings" FORCE)
endif()
if (DEFINED Python_EXECUTABLE AND NOT DEFINED SKBUILD)
set(USE_PYTHON ON CACHE BOOL "Build Python bindings" FORCE)
endif()
endif()
if (NOT DEFINED CMAKE_VISIBILITY_INLINES_HIDDEN)
set(CMAKE_VISIBILITY_INLINES_HIDDEN ON)
endif()
if (NOT DEFINED CMAKE_CXX_VISIBILITY_PRESET)
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
endif()
# We don't need to set policy CMP0069 for this file, because the minimum
# version is 3.15 and for versions >= 3.9 it's automatically set to NEW.
# But a FindPython script in cmake 3.15 have "cmake_policy(VERSION 3.7)"
# so we need to set CMP0069 because of that.
set(CMAKE_POLICY_DEFAULT_CMP0069 NEW)
# Unless it's explicitly specifed, we want LTO for non-debug builds only.
# TODO: check that it's compatible with nanobind_add_module, which has
# its own logic of setting LTO options.
if (NOT DEFINED CMAKE_INTERPROCEDURAL_OPTIMIZATION)
include(CheckIPOSupported)
check_ipo_supported(RESULT is_ipo_supported OUTPUT ipo_error)
if(is_ipo_supported)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE ON)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_MINSIZEREL ON)
else()
message(STATUS "Interprocedural optimization not supported: ${error}")
endif()
endif()
if (INFO)
set(GEMMI_VERSION_INFO ${INFO} CACHE STRING "Extra text for gemmi -V" FORCE)
endif()
if (USE_FORTRAN)
message(STATUS "Currently, Fortran bindings are not built by cmake.")
message(STATUS "They can be built by running make in fortran/.")
#enable_language(Fortran)
else()
#message(STATUS "Skipping Fortran bindings. Add -D USE_FORTRAN=1 to build them.")
endif()
if (DEFINED ENV{EXTRA_WFLAGS})
set(EXTRA_WARNINGS ON CACHE BOOL "Set extra warning flags" FORCE)
endif()
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_BUILD_RPATH_USE_ORIGIN ON)
include(CheckIncludeFile) # for check_include_file
if (DEFINED ENV{CXXFLAGS})
set(USING_ENV_CXXFLAGS ON CACHE BOOL "" FORCE)
endif()
# Set default build mode (based on CMake FAQ)
if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES AND NOT USING_ENV_CXXFLAGS)
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
endif()
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
if (FETCH_ZLIB_NG)
message(STATUS " =============== fetching and building zlib-ng ===============")
include(FetchContent)
# FetchContent_Declare can take EXCLUDE_FROM_ALL only since CMake 3.28;
# for now use FetchContent_Populate instead of FetchContent_MakeAvailable.
if (POLICY CMP0169)
cmake_policy(SET CMP0169 OLD)
endif()
FetchContent_Declare(zlib-ng GIT_REPOSITORY https://github.com/zlib-ng/zlib-ng.git
GIT_TAG 2.2.x
GIT_SHALLOW TRUE)
FetchContent_GetProperties(zlib-ng)
if (NOT zlib-ng_POPULATED)
FetchContent_Populate(zlib-ng)
set(ZLIB_ENABLE_TESTS OFF CACHE INTERNAL "")
set(BUILD_SHARED_LIBS_here ${BUILD_SHARED_LIBS})
set(BUILD_SHARED_LIBS OFF CACHE INTERNAL "build static zlib-ng")
add_subdirectory(${zlib-ng_SOURCE_DIR} ${zlib-ng_BINARY_DIR} EXCLUDE_FROM_ALL)
set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_here} CACHE BOOL "Build shared library" FORCE)
set_property(TARGET zlib PROPERTY POSITION_INDEPENDENT_CODE ON)
# zlib-ng, unless built WITH_NATIVE_INSTRUCTIONS=ON (-march=native),
# builds multiple variants of some functions, using different CPU features.
# IPO (LTO) has been reported to mess it up leading to illegal-instruction
# crash. When zlib-ng is built as a standalone project, not subdirectory,
# these flags are set in zlib-ng/CMakeLists.txt.
if (NOT WITH_NATIVE_INSTRUCTIONS)
set_target_properties(zlib PROPERTIES INTERPROCEDURAL_OPTIMIZATION OFF
INTERPROCEDURAL_OPTIMIZATION_RELEASE OFF
INTERPROCEDURAL_OPTIMIZATION_MINSIZEREL OFF)
endif()
message(STATUS " =============== end of zlib-ng configuration ===============\n")
endif()
elseif (USE_ZLIB_NG)
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/tools")
find_package(ZLIB_NG REQUIRED)
else()
if (NOT INTERNAL_ZLIB)
# CMake >= 3.24 respects variable ZLIB_USE_STATIC_LIBS
find_package(ZLIB)
endif()
if (NOT ZLIB_FOUND)
message(STATUS "The build will use zlib code from third_party/zlib.")
endif()
endif()
if (NOT DEFINED SKBUILD AND CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
find_package(benchmark 1.3 QUIET)
endif()
if (benchmark_FOUND)
message(STATUS "Found benchmark: ${benchmark_DIR}")
else (NOT benchmark_FOUND)
message(STATUS "Benchmarks not configured.")
endif()
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU" AND EXTRA_WARNINGS)
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic -Wformat=2 -Wredundant-decls -Wfloat-conversion -Wdisabled-optimization -Wshadow $ENV{EXTRA_WFLAGS}")
string(TOUPPER "CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE}" cxx_flags_config)
message(STATUS "C++ flags set to: ${CMAKE_CXX_FLAGS} ${${cxx_flags_config}}")
endif()
if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Qvec-report:1")
endif()
if (USE_FORTRAN)
if (CMAKE_Fortran_COMPILER_ID MATCHES "GNU")
set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -std=f2003 -fimplicit-none -Wall -Wextra -pedantic")
message(STATUS "Fortran flags set to: ${CMAKE_Fortran_FLAGS}")
set(CMAKE_Fortran_FLAGS_DEBUG "${CMAKE_Fortran_FLAGS_DEBUG} -fbounds-check")
endif()
endif()
if (WIN32 AND USE_WMAIN)
add_definitions(-D_UNICODE=1)
endif()
#add_library(cgemmi STATIC fortran/grid.cpp fortran/symmetry.cpp)
#
#if (USE_FORTRAN)
# add_library(fgemmi STATIC fortran/gemmi.f90)
# target_link_libraries(fgemmi PRIVATE cgemmi)
#endif()
add_library(gemmi_headers INTERFACE)
add_library(gemmi::headers ALIAS gemmi_headers)
target_include_directories(gemmi_headers INTERFACE
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>")
target_compile_features(gemmi_headers INTERFACE cxx_std_14)
set_target_properties(gemmi_headers PROPERTIES EXPORT_NAME headers)
add_library(gemmi_cpp
src/align.cpp src/assembly.cpp src/calculate.cpp src/crd.cpp
src/ddl.cpp src/eig3.cpp src/fprime.cpp src/gz.cpp
src/intensit.cpp src/json.cpp src/mmcif.cpp src/mmread_gz.cpp
src/monlib.cpp src/mtz.cpp src/mtz2cif.cpp
src/pdb.cpp src/polyheur.cpp src/read_cif.cpp src/read_map.cpp
src/resinfo.cpp src/riding_h.cpp src/select.cpp src/sprintf.cpp
src/to_json.cpp src/to_mmcif.cpp src/to_pdb.cpp src/topo.cpp
src/xds_ascii.cpp)
add_library(gemmi::gemmi_cpp ALIAS gemmi_cpp)
set_property(TARGET gemmi_cpp PROPERTY POSITION_INDEPENDENT_CODE ON)
#set_property(TARGET gemmi_cpp PROPERTY CXX_VISIBILITY_PRESET hidden)
target_compile_definitions(gemmi_cpp PRIVATE GEMMI_BUILD)
target_include_directories(gemmi_cpp PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/third_party")
if (BUILD_SHARED_LIBS)
target_compile_definitions(gemmi_cpp PUBLIC GEMMI_SHARED)
if (ADD_SOVERSION)
set_target_properties(gemmi_cpp PROPERTIES VERSION ${PROJECT_VERSION}
SOVERSION "0")
endif()
endif()
target_link_libraries(gemmi_cpp PUBLIC gemmi_headers)
if (FETCH_ZLIB_NG)
target_compile_definitions(gemmi_cpp PRIVATE USE_ZLIB_NG=1)
target_link_libraries(gemmi_cpp PRIVATE zlib)
elseif (ZLIB_NG_FOUND)
target_compile_definitions(gemmi_cpp PRIVATE USE_ZLIB_NG=1)
target_link_libraries(gemmi_cpp PRIVATE ZLIB_NG::ZLIB_NG)
elseif (ZLIB_FOUND)
target_link_libraries(gemmi_cpp PRIVATE ZLIB::ZLIB)
else()
target_sources(gemmi_cpp PRIVATE
"third_party/zlib/adler32.c"
"third_party/zlib/crc32.c"
"third_party/zlib/gzlib.c"
"third_party/zlib/gzread.c"
"third_party/zlib/inflate.c"
"third_party/zlib/inftrees.c"
"third_party/zlib/inffast.c"
"third_party/zlib/zutil.c")
check_include_file(unistd.h has_unistd_h)
target_compile_definitions(gemmi_cpp PRIVATE NO_GZCOMPRESS=1 DYNAMIC_CRC_TABLE=1)
if (has_unistd_h)
target_compile_definitions(gemmi_cpp PRIVATE Z_HAVE_UNISTD_H=1)
endif()
target_include_directories(gemmi_cpp PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/third_party/zlib")
if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
# /wd4267 - conversion from 'size_t' to 'unsigned int', possible loss of data
target_compile_options(gemmi_cpp PRIVATE /wd4267)
endif()
endif()
### programs from prog/ ###
add_library(options OBJECT prog/options.cpp)
target_link_libraries(options PUBLIC gemmi_headers)
target_include_directories(options PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/third_party")
if (GEMMI_VERSION_INFO)
target_compile_definitions(options PRIVATE GEMMI_VERSION_INFO=${GEMMI_VERSION_INFO})
endif()
# Gemmi subcommands compiled as individual binaries.
# Not built by default and used mostly for testing.
function(add_single_prog target)
cmake_parse_arguments(arg "NOLIB" "ADD_SRC" "" ${ARGN})
add_executable(gemmi-${target} EXCLUDE_FROM_ALL prog/${target}.cpp ${arg_ADD_SRC}
$<TARGET_OBJECTS:options>)
if (arg_NOLIB)
target_link_libraries(gemmi-${target} PRIVATE gemmi_headers)
else()
target_link_libraries(gemmi-${target} PRIVATE gemmi_cpp)
endif()
target_include_directories(gemmi-${target} PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}/third_party")
endfunction()
add_single_prog(align)
add_single_prog(blobs ADD_SRC prog/mapcoef.cpp)
add_single_prog(cif2json)
add_single_prog(cif2mtz)
add_single_prog(cifdiff)
add_single_prog(contact)
add_single_prog(contents)
add_single_prog(convert)
add_single_prog(crd ADD_SRC prog/monlib_opt.cpp)
add_single_prog(ecalc)
add_single_prog(fprime)
add_single_prog(grep)
add_single_prog(h)
add_single_prog(json2cif)
add_single_prog(map)
add_single_prog(map2sf)
add_single_prog(mask)
add_single_prog(mixmtz)
add_single_prog(merge)
add_single_prog(mondiff)
add_single_prog(mtz)
add_single_prog(mtz2cif)
add_single_prog(reindex)
add_single_prog(residues)
add_single_prog(rmsz)
add_single_prog(sf2map ADD_SRC prog/mapcoef.cpp)
add_single_prog(sfcalc)
add_single_prog(sg NOLIB)
add_single_prog(tags)
add_single_prog(validate ADD_SRC prog/validate_mon.cpp)
add_single_prog(wcn)
add_single_prog(xds2mtz)
# gemmi program with a number of subcommands
add_executable(gemmi_prog
prog/align.cpp prog/blobs.cpp
prog/cif2mtz.cpp prog/cif2json.cpp prog/cifdiff.cpp
prog/contact.cpp prog/contents.cpp prog/convert.cpp prog/crd.cpp
prog/ecalc.cpp prog/fprime.cpp
prog/grep.cpp prog/h.cpp prog/json2cif.cpp
prog/main.cpp prog/map.cpp prog/map2sf.cpp
prog/mapcoef.cpp prog/mask.cpp prog/merge.cpp
prog/mondiff.cpp prog/monlib_opt.cpp prog/mtz.cpp prog/mtz2cif.cpp
prog/reindex.cpp prog/residues.cpp prog/rmsz.cpp prog/set.cpp
prog/sf2map.cpp prog/sfcalc.cpp prog/sg.cpp prog/tags.cpp
prog/validate.cpp prog/validate_mon.cpp prog/wcn.cpp
prog/xds2mtz.cpp
$<TARGET_OBJECTS:options>)
add_executable(gemmi::prog ALIAS gemmi_prog)
target_link_libraries(gemmi_prog PRIVATE gemmi_cpp)
target_include_directories(gemmi_prog PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/third_party")
target_compile_definitions(gemmi_prog PRIVATE GEMMI_ALL_IN_ONE=1)
set_target_properties(gemmi_prog PROPERTIES OUTPUT_NAME gemmi EXPORT_NAME prog)
if (BUILD_SHARED_LIBS)
if (APPLE)
set(origin "@loader_path")
else ()
set(origin "$ORIGIN")
endif ()
set_property(TARGET gemmi_prog
PROPERTY INSTALL_RPATH "${origin}/../${CMAKE_INSTALL_LIBDIR}")
endif()
if (WIN32 AND USE_WMAIN)
# _UNICODE=1 is now set globally
#target_compile_definitions(gemmi_prog PRIVATE _UNICODE=1)
if(MINGW)
target_link_options(gemmi_prog PRIVATE "-municode")
endif()
endif()
if (NOT BUILD_GEMMI_PROGRAM)
set_property(TARGET gemmi_prog PROPERTY EXCLUDE_FROM_ALL TRUE)
endif()
# I'm not sure if this option should be here.
# The alternative is to use install/strip target, or cmake --install --strip.
if (STRIP_BINARY AND CMAKE_STRIP)
add_custom_command(TARGET gemmi_prog POST_BUILD
COMMENT "Strip symbols from program with ${CMAKE_STRIP}"
COMMAND ${CMAKE_STRIP}
ARGS $<TARGET_FILE:gemmi_prog>)
endif()
### tests and examples ###
#add_executable(c_test EXCLUDE_FROM_ALL fortran/c_test.c)
#target_link_libraries(c_test PRIVATE cgemmi)
add_executable(cpptest EXCLUDE_FROM_ALL tests/main.cpp tests/cif.cpp tests/windowsh.cpp)
target_compile_definitions(cpptest PRIVATE USE_STD_SNPRINTF=1)
target_link_libraries(cpptest PRIVATE gemmi_headers)
target_include_directories(cpptest PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/third_party")
add_executable(hello EXCLUDE_FROM_ALL examples/hello.cpp)
target_link_libraries(hello PRIVATE gemmi_headers)
add_executable(doc_example EXCLUDE_FROM_ALL
docs/code/sym.cpp docs/code/elem.cpp docs/code/resinfo.cpp
docs/code/cell.cpp docs/code/mutate.cpp src/resinfo.cpp)
target_link_libraries(doc_example PRIVATE gemmi_headers)
add_executable(doc_example2 EXCLUDE_FROM_ALL docs/code/cif_cc.cpp)
target_link_libraries(doc_example2 PRIVATE gemmi_headers)
add_executable(doc_newmtz EXCLUDE_FROM_ALL docs/code/newmtz.cpp)
target_link_libraries(doc_newmtz PRIVATE gemmi_cpp)
# always compile these tests with assertions enabled
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU|Intel")
target_compile_options(doc_example PRIVATE -UNDEBUG)
endif()
add_executable(test_disulf EXCLUDE_FROM_ALL tests/disulf.cpp)
target_link_libraries(test_disulf PRIVATE gemmi_cpp)
# auth_label requires C++17 for <filesystem>
add_executable(auth_label EXCLUDE_FROM_ALL examples/auth_label.cpp)
target_link_libraries(auth_label PRIVATE gemmi_cpp)
add_executable(check_conn EXCLUDE_FROM_ALL examples/check_conn.cpp)
target_link_libraries(check_conn PRIVATE gemmi_cpp)
enable_testing()
add_custom_target(print-version
COMMAND gemmi_prog --version
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
COMMENT "gemmi --version"
)
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} -C \"$<CONFIG>\")
add_test(NAME cpptest COMMAND cpptest)
add_dependencies(check
cpptest hello doc_example doc_example2 doc_newmtz
test_disulf check_conn print-version)
if (USE_FORTRAN)
# add_executable(ftest EXCLUDE_FROM_ALL fortran/ftest.f90)
# target_link_libraries(ftest PRIVATE fgemmi)
# add_test(NAME ftest COMMAND ftest)
# add_executable(ftest_grid EXCLUDE_FROM_ALL fortran/ftest_grid.f90)
# target_link_libraries(ftest_grid PRIVATE fgemmi)
# if("${CMAKE_Fortran_COMPILER_ID}" STREQUAL "Intel")
# set_property(TARGET ftest ftest_grid PROPERTY LINKER_LANGUAGE Fortran)
# endif()
# add_test(NAME ftest_grid COMMAND ftest_grid)
# add_dependencies(check ftest ftest_grid)
endif()
### benchmarks ###
if (benchmark_FOUND)
foreach(b stoi elem mod niggli pdb resinfo round sym writecif)
add_executable(${b}-bm EXCLUDE_FROM_ALL benchmarks/${b}.cpp)
if (b MATCHES "elem|resinfo|pdb|writecif")
target_link_libraries(${b}-bm PRIVATE gemmi_cpp)
endif()
target_link_libraries(${b}-bm PRIVATE gemmi_headers benchmark::benchmark)
set_property(TARGET ${b}-bm PROPERTY RUNTIME_OUTPUT_DIRECTORY
"${CMAKE_BINARY_DIR}/benchmarks")
add_dependencies(check ${b}-bm)
endforeach()
endif()
### Python bindings ###
if (USE_PYTHON)
message(STATUS "The python module will be built.")
# CMake >=3.18 has subcomponent Development.Module, scikit-build-core also has it
if (${CMAKE_VERSION} VERSION_LESS 3.18 AND NOT SKBUILD)
find_package(Python ${PYTHON_VERSION} REQUIRED COMPONENTS Interpreter Development)
else()
find_package(Python ${PYTHON_VERSION} REQUIRED COMPONENTS Interpreter Development.Module)
endif()
if (EXISTS "${CMAKE_HOME_DIRECTORY}/nanobind")
message(STATUS "Using ${CMAKE_HOME_DIRECTORY}/nanobind (internal copy).")
add_subdirectory(nanobind)
else()
# Detect the installed nanobind package and import it into CMake
execute_process(
COMMAND "${Python_EXECUTABLE}" -m nanobind --cmake_dir
OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE nb_cmake_dir)
list(APPEND CMAKE_PREFIX_PATH "${nb_cmake_dir}")
find_package(nanobind 2.2.0 CONFIG REQUIRED)
message(STATUS "Found nanobind ${nanobind_VERSION}: ${NB_DIR}")
endif()
nanobind_add_module(gemmi_py NOMINSIZE
python/gemmi.cpp python/align.cpp
python/ccp4.cpp python/chemcomp.cpp python/cif.cpp python/custom.cpp
python/elem.cpp python/grid.cpp python/hkl.cpp
python/meta.cpp python/mol.cpp python/monlib.cpp
python/mtz.cpp python/read.cpp python/recgrid.cpp
python/scaling.cpp python/search.cpp
python/sf.cpp python/sym.cpp python/topo.cpp
python/unitcell.cpp python/write.cpp)
if (STANDALONE_PYTHON_MODULE)
target_sources(gemmi_py PRIVATE $<TARGET_OBJECTS:gemmi_cpp>)
get_target_property(_gemmi_cpp_libs gemmi_cpp LINK_LIBRARIES)
set_property(TARGET gemmi_py APPEND PROPERTY LINK_LIBRARIES ${_gemmi_cpp_libs})
else()
target_link_libraries(gemmi_py PRIVATE gemmi_cpp)
endif()
set_property(TARGET gemmi_py PROPERTY OUTPUT_NAME gemmi_ext)
if (CMAKE_CONFIGURATION_TYPES)
set(py_dir $<CONFIG>/py)
else()
set(py_dir py)
endif()
set_property(TARGET gemmi_py PROPERTY LIBRARY_OUTPUT_DIRECTORY ${py_dir}/gemmi)
add_custom_command(TARGET gemmi_py PRE_BUILD
COMMENT "Copying gemmi/__init__.py"
COMMAND ${CMAKE_COMMAND} -E copy
"${CMAKE_CURRENT_SOURCE_DIR}/python/gemmi/__init__.py"
${py_dir}/gemmi/__init__.py
BYPRODUCTS ${py_dir}/gemmi/__init__.py)
# nanobind gives warnings with -Wpedantic and -Wshadow
if(CMAKE_CXX_FLAGS MATCHES "-Wshadow")
if (TARGET nanobind-static)
target_compile_options(nanobind-static PRIVATE "-Wno-shadow")
endif()
target_compile_options(gemmi_py PRIVATE "-Wno-shadow")
endif()
if (CMAKE_CXX_FLAGS MATCHES "-Wpedantic")
if (TARGET nanobind-static)
target_compile_options(nanobind-static PRIVATE "-Wno-pedantic")
endif()
target_compile_options(gemmi_py PRIVATE "-Wno-pedantic")
endif()
if (CMAKE_CXX_FLAGS MATCHES "-Wredundant-decls")
if (TARGET nanobind-static)
target_compile_options(nanobind-static PRIVATE "-Wno-redundant-decls")
endif()
endif()
if (USE_NLOPT)
message(STATUS "Using NLopt.")
find_package(NLopt REQUIRED)
target_link_libraries(gemmi_py PRIVATE NLopt::nlopt)
target_compile_definitions(gemmi_py PRIVATE WITH_NLOPT=1)
endif()
if (GENERATE_STUBS AND CMAKE_CROSSCOMPILING)
message(WARNING "Stubs cannot be generated when cross-compiling - skipping.")
elseif (GENERATE_STUBS)
nanobind_add_stub(
gemmi_stub
MODULE gemmi
OUTPUT ${py_dir}/gemmi/__init__.pyi
PYTHON_PATH ${py_dir}
DEPENDS gemmi_py
PATTERN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/python/stubs.pat"
MARKER_FILE ${py_dir}/gemmi/py.typed
)
nanobind_add_stub(
gemmi_cif_stub
MODULE gemmi.cif
OUTPUT ${py_dir}/gemmi/cif.pyi
PYTHON_PATH ${py_dir}
DEPENDS gemmi_py
)
endif()
else()
message(STATUS "Skipping Python module. Add -D USE_PYTHON=1 to build it.")
endif()
# avoid GCC warning: the ABI of passing structure with 'complex float' member
# has changed in GCC 4.4
set_property(SOURCE python/recgrid.cpp python/hkl.cpp python/mtz.cpp prog/mtz.cpp
PROPERTY COMPILE_FLAGS $<$<CXX_COMPILER_ID:GNU>:-Wno-psabi>)
include(CMakePackageConfigHelpers)
configure_package_config_file(tools/gemmi-config.cmake.in gemmi-config.cmake
INSTALL_DESTINATION "${GEMMI_INSTALL_CMAKEDIR}")
write_basic_package_version_file(gemmi-config-version.cmake
VERSION ${PROJECT_VERSION}
COMPATIBILITY AnyNewerVersion)
if (BUILD_GEMMI_PROGRAM)
# Is it useful to have the program location in gemmi-targets.cmake?
install(TARGETS gemmi_prog EXPORT GemmiTargets)
endif()
if (INSTALL_DEV_FILES)
# In CMake 3.23 we'll be able to use FILE_SET to install the headers
install(DIRECTORY include/gemmi DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
install(TARGETS gemmi_headers gemmi_cpp EXPORT GemmiTargets)
install(EXPORT GemmiTargets FILE gemmi-targets.cmake NAMESPACE gemmi::
DESTINATION "${GEMMI_INSTALL_CMAKEDIR}")
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/gemmi-config.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/gemmi-config-version.cmake"
DESTINATION "${GEMMI_INSTALL_CMAKEDIR}")
elseif (BUILD_SHARED_LIBS AND BUILD_GEMMI_PROGRAM)
install(TARGETS gemmi_cpp)
endif()
if (USE_PYTHON)
if (DEFINED PYTHON_INSTALL_DIR)
message(STATUS "Install directory for Python module: ${PYTHON_INSTALL_DIR}")
set(Python_SITEARCH "${PYTHON_INSTALL_DIR}")
endif()
file(TO_CMAKE_PATH "${Python_SITEARCH}" Python_SITEARCH)
# Using Python_SITEARCH/SITELIB is not good, because they are absolute
# and don't respect CMAKE_INSTALL_PREFIX.
# https://discourse.cmake.org/t/findpython3-how-to-specify-local-installation-directory-for-python-module/3580/5
if (BUILD_SHARED_LIBS AND NOT STANDALONE_PYTHON_MODULE)
if (DEFINED SKBUILD)
# When building via pip, Python_SITEARCH is /tmp.../wheel/platlib,
# different from the actual install path. Best guess:
set(rpath_rel "../../../../${CMAKE_INSTALL_LIBDIR}")
else()
file(RELATIVE_PATH rpath_rel
"${Python_SITEARCH}/gemmi" "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
endif()
set_target_properties(gemmi_py PROPERTIES INSTALL_RPATH "${origin}/${rpath_rel}")
endif()
install(TARGETS gemmi_py DESTINATION "${Python_SITEARCH}/gemmi" COMPONENT py)
install(DIRECTORY examples DESTINATION "${Python_SITEARCH}/gemmi" COMPONENT py
FILES_MATCHING PATTERN "*.py"
PATTERN "[._]*" EXCLUDE)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${py_dir}/gemmi/__init__.py"
DESTINATION "${Python_SITEARCH}/gemmi" COMPONENT py)
if (GENERATE_STUBS AND NOT CMAKE_CROSSCOMPILING)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/${py_dir}/gemmi/py.typed"
"${CMAKE_CURRENT_BINARY_DIR}/${py_dir}/gemmi/__init__.pyi"
"${CMAKE_CURRENT_BINARY_DIR}/${py_dir}/gemmi/cif.pyi"
DESTINATION "${Python_SITEARCH}/gemmi" COMPONENT py)
endif()
endif()