-
-
Notifications
You must be signed in to change notification settings - Fork 60
/
CMakeLists.txt
724 lines (609 loc) · 22.5 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
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
cmake_minimum_required(VERSION 3.21 FATAL_ERROR)
# We will use cmake_path for file-name manipulation
list(APPEND CMAKE_MESSAGE_CONTEXT "sisl")
message(STATUS "Building with CMake version: ${CMAKE_VERSION}")
if(SKBUILD)
message(STATUS "Building with scikit-build-core: ${SKBUILD_CORE_VERSION}")
endif()
project(sisl
DESCRIPTION "Cythonized or fortran codes"
HOMEPAGE_URL "https://github.com/zerothi/sisl"
LANGUAGES C
# cmake requires a version of X.Y.Z or some other form, it does not accept setuptools_scm dirty tags
# VERSION "${SKBUILD_PROJECT_VERSION}"
)
# Print out the discovered paths
include(CMakePrintHelpers)
# Enable python packages
find_package(Python COMPONENTS
Interpreter
Development.Module
NumPy # needed for f2py
REQUIRED)
# Retrive the cython executable
message(CHECK_START "Checking Cython")
list(APPEND CMAKE_MESSAGE_INDENT " ")
message(CHECK_START "Locating executable")
find_program(CYTHON_EXECUTABLE
NAMES cython cython3
NO_CACHE
REQUIRED)
message(CHECK_PASS "${CYTHON_EXECUTABLE}")
message(CHECK_START "Checking version")
execute_process(COMMAND
"${CYTHON_EXECUTABLE}" --version
OUTPUT_VARIABLE CYTHON_VERSION
RESULT_VARIABLE CYTHON_VERSION_ERR
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_STRIP_TRAILING_WHITESPACE)
# Print it out
if(CYTHON_VERSION_ERR)
message(CHECK_FAIL "${CYTHON_VERSION_ERR}")
else()
message(CHECK_PASS "${CYTHON_VERSION}")
endif()
list(POP_BACK CMAKE_MESSAGE_INDENT)
message(CHECK_PASS "Found Cython")
# Define global definitions
# We will never use the deprecated API any-more
add_compile_definitions(NPY_NO_DEPRECATED_API=NPY_1_20_API_VERSION)
add_compile_definitions(CYTHON_NO_PYINIT_EXPORT=1)
# All libraries in sisl will *not* be prefixed with
#: lib, perhaps we should change this
set(CMAKE_SHARED_MODULE_PREFIX "")
# Determine whether we are in CIBUILDWHEEL
# and whether we are building for the universal target
set(_def_fortran TRUE)
#if( APPLE AND
# "$ENV{CIBUILDWHEEL}" STREQUAL "1" AND
# "$ENV{CIBW_ARCHS}" MATCHES "(universal2|arm64)")
# # Disable fortran here, it shouldn't be needed
# set(_def_fortran FALSE)
#endif()
option(WITH_FORTRAN
"Whether to compile and ship fortran sources (default ${_def_fortran})" ${_def_fortran})
# Define all options for the user
if( WITH_FORTRAN )
enable_language(Fortran)
set(F2PY_REPORT_ON_ARRAY_COPY 10
CACHE STRING
"The minimum (element) size of arrays before warning about copies")
option(WITH_F2PY_REPORT_COPY
"Add instructions to track f2py-copies on routine calls use -DF2PY_REPORT_ON_ARRAY_COPY=<int> as well" FALSE)#
option(WITH_F2PY_REPORT_EXIT
"Report timings on routine exits" FALSE)
endif()
option(WITH_COVERAGE
"Add instructions for coverage in Cython files" FALSE)
option(WITH_LINE_DIRECTIVES
"Create line-directives in cythonized sources" FALSE)
option(WITH_ANNOTATE
"Create html file outputs that can be used for figuring out performance bottlenecks in cython sources" FALSE)
option(WITH_GDB
"Add GDB-enabled Cython sources" FALSE)
# Define which pure-python modules that should not be built
set(NO_COMPILATION ""
CACHE STRING
"A list of files that should not be compiled [_sparse_grid_ops|_sparse_grid_ops.py]")
message(STATUS "Compilation flags")
list(APPEND CMAKE_MESSAGE_INDENT " ")
set(CC $ENV{CC})
set(CFLAGS $ENV{CFLAGS})
cmake_print_variables(CC)
cmake_print_variables(CFLAGS)
cmake_print_variables(CMAKE_C_COMPILER)
cmake_print_variables(CMAKE_C_FLAGS)
set(CXX $ENV{CXX})
set(CXXFLAGS $ENV{CXXFLAGS})
cmake_print_variables(CXX)
cmake_print_variables(CXXFLAGS)
cmake_print_variables(CMAKE_CXX_COMPILER)
cmake_print_variables(CMAKE_CXX_FLAGS)
if(WITH_FORTRAN)
set(FC $ENV{FC})
set(FFLAGS $ENV{FFLAGS})
cmake_print_variables(FC)
cmake_print_variables(FFLAGS)
cmake_print_variables(CMAKE_Fortran_COMPILER)
cmake_print_variables(CMAKE_Fortran_FLAGS)
endif()
set(ARCHFLAGS $ENV{ARCHFLAGS})
cmake_print_variables(ARCHFLAGS)
if( APPLE )
cmake_print_variables(CMAKE_OSX_ARCHITECTURES)
cmake_print_variables(CMAKE_OSX_DEPLOYMENT_TARGET)
cmake_print_variables(CMAKE_APPLE_ARCH_SYSROOTS)
cmake_print_variables(CMAKE_OSX_SYSROOT_PATH)
elseif( WIN32 )
# When building using a Python not compiled with the same compiler,
# we then need to add this definition to pybass a Cython problem
# See https://github.com/cython/cython/issues/3405
add_compile_definitions(MS_WIN64)
endif()
list(POP_BACK CMAKE_MESSAGE_INDENT)
# Parse Cython global options
if(WITH_COVERAGE)
add_compile_definitions(CYTHON_TRACE_NOGIL=1)
endif()
if(WITH_ANNOTATE)
list(APPEND CYTHON_FLAGS --annotate)
endif()
if(WITH_LINE_DIRECTIVES)
list(APPEND CYTHON_FLAGS --line-directives)
endif()
if(WITH_GDB)
list(APPEND CYTHON_FLAGS --gdb)
endif()
# Decide for fortran stuff
if(WITH_FORTRAN)
# Enable Fortran language
enable_language(Fortran)
# Retrieve header information
# Define f2py executable
set(F2PY_EXECUTABLE "${PYTHON_EXECUTABLE}" -m numpy.f2py)
# Get numpy.f2py header
set(Python_NumPy_F2Py_INCLUDE_DIR)
foreach(dir IN LISTS Python_NumPy_INCLUDE_DIRS)
if(IS_DIRECTORY "${dir}/../../f2py/src")
set(Python_NumPy_F2Py_INCLUDE_DIR "${dir}/../../f2py/src")
break()
endif()
endforeach()
if(NOT Python_NumPy_F2Py_INCLUDE_DIR)
message(STATUS "Could not locate f2py sources in ${NumPy_NumPy_INCLUDE_DIRS}/../../f2py/src/ trying numpy.f2py.get_include()")
execute_process(COMMAND
"${PYTHON_EXECUTABLE}" -c "import numpy.f2py; print(numpy.f2py.get_include())"
OUTPUT_VARIABLE Python_NumPy_F2Py_INCLUDE_DIR
RESULT_VARIABLE NumPy_F2Py_ERR
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NumPy_F2Py_ERR)
message(SEND_ERROR "ERROR: NumPy.F2PY header not found (${err}), looked in: ${NumPy_NumPy_INCLUDE_DIRS}/../../f2py/src.")
endif()
endif()
# f2py requires a basic object
add_library(f2py_fortranobject OBJECT "${Python_NumPy_F2Py_INCLUDE_DIR}/fortranobject.c")
target_link_libraries(f2py_fortranobject PUBLIC Python::NumPy)
target_include_directories(f2py_fortranobject PUBLIC "${Python_NumPy_F2Py_INCLUDE_DIR}")
set_property(TARGET f2py_fortranobject PROPERTY POSITION_INDEPENDENT_CODE ON)
set(F2PY_LIBRARIES f2py_fortranobject)
if(WITH_F2PY_REPORT_EXIT)
target_compile_definitions(f2py_fortranobject PRIVATE F2PY_REPORT_ATEXIT)
endif()
if(WITH_F2PY_REPORT_COPY)
target_compile_definitions(f2py_fortranobject PRIVATE F2PY_REPORT_ON_ARRAY_COPY=${F2PY_REPORT_ON_ARRAY_COPY})
endif()
endif(WITH_FORTRAN)
message(STATUS "Python variables:")
list(APPEND CMAKE_MESSAGE_INDENT " ")
cmake_print_variables(Python_INCLUDE_DIRS)
cmake_print_variables(Python_NumPy_INCLUDE_DIRS)
if(WITH_FORTRAN)
cmake_print_variables(Python_NumPy_F2Py_INCLUDE_DIR)
endif()
list(POP_BACK CMAKE_MESSAGE_INDENT)
message(STATUS "sisl options")
list(APPEND CMAKE_MESSAGE_INDENT " ")
cmake_print_variables(WITH_COVERAGE)
cmake_print_variables(WITH_ANNOTATE)
cmake_print_variables(WITH_LINE_DIRECTIVES)
cmake_print_variables(WITH_GDB)
cmake_print_variables(NO_COMPILATION)
cmake_print_variables(WITH_FORTRAN)
if(WITH_FORTRAN)
cmake_print_variables(WITH_F2PY_REPORT_COPY)
if(WITH_F2PY_REPORT_COPY)
cmake_print_variables(F2PY_REPORT_ON_ARRAY_COPY)
endif()
cmake_print_variables(WITH_F2PY_REPORT_EXIT)
endif()
list(POP_BACK CMAKE_MESSAGE_INDENT)
# Return in _result whether the _file should be built, or not
# It checks whether the file is present in the NO_COMPILATION
function(sisl_compile_source _file _result)
# By default, we always compile everything
set(${_result} TRUE PARENT_SCOPE)
# Check whether the file is listed in the variable
# First get the stem of the file path (relative to the sisl source dir)
set(in "${CMAKE_CURRENT_SOURCE_DIR}/${_file}")
cmake_path(RELATIVE_PATH in
BASE_DIRECTORY "${PROJECT_SOURCE_DIR}/src"
OUTPUT_VARIABLE base
)
string(REPLACE "/" ";" base_list "${base}")
# Loop over the segments of the file
list(LENGTH base_list len)
math(EXPR len "${len} - 1")
foreach(i RANGE 0 ${len})
# Create the path from the list of base_list[i:]
list(SUBLIST base_list ${i} -1 path)
string(REPLACE ";" "/" path "${path}")
# Check if the file is present in the file naming list
list(FIND NO_COMPILATION "${path}" res1)
list(FIND NO_COMPILATION "${path}.py" res2)
if( res1 GREATER -1 OR res2 GREATER -1 )
set(${_result} FALSE PARENT_SCOPE)
break()
endif()
endforeach()
endfunction()
# Below we have the, a bit complicated property setup for the build-mechanism.
# It allows one to properly define how specific fortran files should be
# processed by f2py for correct signature creations.
# Document the properties that we are dealing with
# user-added properties on individual files
define_property(SOURCE
PROPERTY SISL_SIGNATURE_SKIP
BRIEF_DOCS "Pass which subroutines/functions to skip in the signature generation of a source file (in create_f2py_signature)"
FULL_DOCS "See BRIEF_DOCS"
)
define_property(SOURCE
PROPERTY SISL_SIGNATURE_ONLY
BRIEF_DOCS "Pass which subroutines/functions to retain (skip the rest) in the signature generation of a source file (in create_f2py_signature)"
FULL_DOCS "See BRIEF_DOCS"
)
define_property(SOURCE
PROPERTY SISL_GENERATE_TARGET
BRIEF_DOCS "The target that generates a specific file, be it Cython or f2py signature file. Can be used for dependency management"
FULL_DOCS "See BRIEF_DOCS"
)
define_property(SOURCE
PROPERTY SISL_MODULE_NAME
BRIEF_DOCS "The module name passed when the signature file was created (necessary to be equivalent in the library creation"
FULL_DOCS "See BRIEF_DOCS"
)
define_property(SOURCE
PROPERTY SISL_FROM_SOURCES
BRIEF_DOCS "The sources that generated the signature file in create_f2py_signature; automatic usage of these sources in add_f2py_library"
FULL_DOCS "See BRIEF_DOCS"
)
function(sisl_has_substring _bool _string _substring)
string(FIND "${_string}" "${_substring}" index)
if(${index} EQUAL -1)
set(${_bool} FALSE PARENT_SCOPE)
else()
set(${_bool} TRUE PARENT_SCOPE)
endif()
endfunction()
function(sisl_file_as_gentarget _name _output)
if(IS_ABSOLUTE "${_name}")
# shorten the generated target (easier on debugging)
# But we retain the tree-structure of the current build directory
cmake_path(RELATIVE_PATH _name
BASE_DIRECTORY "${PROJECT_BINARY_DIR}"
OUTPUT_VARIABLE _name_rel)
else()
set(_name_rel "${_name}")
endif()
string(REPLACE "/" "_" gen_signature "gen_${_name_rel}")
string(REPLACE " " "_" gen_signature "${gen_signature}")
set(${_output} ${gen_signature} PARENT_SCOPE)
endfunction()
# Define a function for cythonizing sources
# All source-files generated with cython are expected to depend
# on numpy, the include-folder will at least be forcefully
# added.
# We might consider adding an option to disable it, but for
# now this is fine.
function(add_cython_library)
set(options ANNOTATE GDB LINE_DIRECTIVES CXX)
set(oneValueArgs
SOURCE # in
LIBRARY # out
# these are optional
CYTHON_EXECUTABLE # in[opt]
OUTPUT # out[opt]
)
set(multiValueArgs
CYTHON_FLAGS # in[opt]
INCLUDE_DIRS # in[opt]
DEPENDS # in[opt]
)
# Parse arguments
cmake_parse_arguments(_c "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
# Figure out the source file
if(NOT DEFINED _c_SOURCE)
list(GET _c_UNPARSED_ARGUMENTS 0 _c_SOURCE)
endif()
# Parse simple flags
if(NOT DEFINED _c_CYTHON_EXECUTABLE)
set(_c_CYTHON_EXECUTABLE "${CYTHON_EXECUTABLE}")
endif()
if(NOT DEFINED _c_CYTHON_FLAGS)
set(_c_CYTHON_FLAGS "-3" ${CYTHON_FLAGS})
endif()
# Figure out the extension and convert to proper extension
cmake_path(GET _c_SOURCE STEM LAST_ONLY _c_SOURCE_stem)
# Define the C- source
if( _c_CXX )
set(_c_is_c FALSE)
set(_output_ext "cxx")
list(APPEND _c_CYTHON_FLAGS --cplus)
else()
set(_c_is_c TRUE)
set(_output_ext "c")
endif()
set(_c_output_base "${_c_SOURCE_stem}.${_output_ext}")
set(_c_output_full "${CMAKE_CURRENT_BINARY_DIR}/${_c_output_base}")
if(DEFINED _c_OUTPUT)
# return in output the source file
set(${_c_OUTPUT} "${_c_output_full}" PARENT_SCOPE)
endif()
# storage of comment
set(_comment "Cythonizing source ${_c_SOURCE} into ${_c_output_base}")
if( _c_ANNOTATE )
list(APPEND _c_CYTHON_FLAGS --annotate)
endif()
sisl_has_substring(_has "${_c_CYTHON_FLAGS}" "--annotate")
if(_has)
set(_comment "${_comment} | annotation")
endif()
if( _c_GDB )
list(APPEND _c_CYTHON_FLAGS --gdb)
endif()
sisl_has_substring(_has "${_c_CYTHON_FLAGS}" "--gdb")
if(_has)
set(_comment "${_comment} | GDB")
endif()
if( _c_LINE_DIRECTIVES )
list(APPEND _c_CYTHON_FLAGS --line-directives)
endif()
sisl_has_substring(_has "${_c_CYTHON_FLAGS}" "--line-directives")
if(_has)
set(_comment "${_comment} | line-directives")
endif()
# ensure no two flags will be present
list(REMOVE_DUPLICATES _c_CYTHON_FLAGS)
# Add include directories
foreach(dir IN LISTS _c_INCLUDE_DIRS)
if(IS_DIRECTORY "${dir}")
list(APPEND _c_CYTHON_FLAGS "-I ${dir}")
else()
message(WARNING "Trying to add include dir: ${dir} but the folder does not exist?")
endif()
endforeach()
# Now we have everything
sisl_file_as_gentarget(${_c_output_full} _gen_target)
add_custom_target(${_gen_target} DEPENDS "${_c_output_full}")
add_custom_command(
OUTPUT "${_c_output_full}"
DEPENDS ${_c_SOURCE} ${_c_DEPENDS}
COMMAND "${_c_CYTHON_EXECUTABLE}" "${_c_CYTHON_FLAGS}" --output-file "${_c_output_full}" "${_c_SOURCE}"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
VERBATIM
# this handles the spaces from the c_CYTHON_FLAGS list
COMMAND_EXPAND_LISTS
COMMENT "${_comment}"
)
# Define it has numpy
set_source_files_properties("${_c_output_full}"
PROPERTIES
SISL_GENERATE_TARGET "${_gen_target}"
INCLUDE_DIRECTORIES "${Python_NumPy_INCLUDE_DIRS}"
)
# now we have a source file, lets create a library if required
if(DEFINED _c_LIBRARY)
python_add_library(${_c_LIBRARY} WITH_SOABI
MODULE "${_c_output_full}")
# ensure direct dependency on the source generator (for parallel builds)
add_dependencies(${_c_LIBRARY} ${_gen_target})
endif()
endfunction(add_cython_library)
function(create_f2py_signature)
set(options)
set(oneValueArgs
SIGNATURE # in
MODULE # in
F2PY_EXECUTABLE # in[opt]
OUTPUT
)
set(multiValueArgs
SOURCES # in could be foo.f only:bar bar2:
DEPENDS # in[opt]
F2PY_FLAGS # in[opt]
)
cmake_parse_arguments(_f "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
# We do not check whether the required arguments are added
# Check arguments
if(NOT IS_ABSOLUTE "${_f_SIGNATURE}")
set(_f_SIGNATURE "${CMAKE_CURRENT_BINARY_DIR}/${_f_SIGNATURE}")
endif()
# get full relative path (from current source dir)
cmake_path(RELATIVE_PATH _f_SIGNATURE
OUTPUT_VARIABLE _f_signature_rel)
# add signature file output and the module name
list(PREPEND _f_F2PY_FLAGS "-h" "${_f_SIGNATURE}")
list(PREPEND _f_F2PY_FLAGS "-m" "${_f_MODULE}")
if(NOT DEFINED _f_F2PY_EXECUTABLE)
set(_f_F2PY_EXECUTABLE "${F2PY_EXECUTABLE}")
endif()
# Create the full-path sources
macro(config_prefix prefix var)
if(${var})
message(VERBOSE "${name}[${prefix}]: ${${var}}")
set(${var} ${prefix}: ${${var}} :)
else()
# clear
unset(${var})
endif()
endmacro()
macro(get_config source)
get_filename_component(name "${source}" NAME)
get_source_file_property(s_skip "${name}" SISL_SIGNATURE_SKIP)
config_prefix("skip" s_skip)
get_source_file_property(s_only "${name}" SISL_SIGNATURE_ONLY)
config_prefix("only" s_only)
endmacro()
set(_sources)
set(_sources_all)
foreach(s IN LISTS _f_SOURCES)
get_config("${s}")
if(IS_ABSOLUTE "${s}")
list(APPEND _sources "${s}")
list(APPEND _sources_all "${s}")
elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${s}")
list(APPEND _sources "${CMAKE_CURRENT_SOURCE_DIR}/${s}")
list(APPEND _sources_all "${CMAKE_CURRENT_SOURCE_DIR}/${s}")
else()
# this will add the only|skip: : segments, if present
# Since the skip:...: is generally quoted, we will ensure
# unpacking it.
separate_arguments(seg UNIX_COMMAND ${s})
list(APPEND _sources_all ${seg})
endif()
# Add the properties of the signature handling
list(APPEND _sources_all ${seg} ${s_skip} ${s_only})
endforeach()
sisl_file_as_gentarget(${_f_SIGNATURE} _gen_target)
add_custom_target(${_gen_target} DEPENDS "${_f_SIGNATURE}")
add_custom_command(
OUTPUT "${_f_SIGNATURE}"
COMMAND "${_f_F2PY_EXECUTABLE}" "${_f_F2PY_FLAGS}" "${_sources_all}"
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
DEPENDS ${_sources} ${_f_DEPENDS}
COMMENT "Generating signature file ${_f_signature_rel}"
VERBATIM
COMMAND_EXPAND_LISTS
)
# Set the module name property, so it is easier to deal with defaults
set_source_files_properties("${_f_SIGNATURE}"
PROPERTIES
SISL_GENERATE_TARGET ${_gen_target}
SISL_MODULE_NAME "${_f_MODULE}"
SISL_FROM_SOURCES "${_sources}"
GENERATED TRUE
)
if(DEFINED _f_OUTPUT)
set(${_f_OUTPUT} "${_f_SIGNATURE}" PARENT_SCOPE)
endif()
endfunction(create_f2py_signature)
# Create a library from a previously created f2py signature file.
# If this signature file has been created from `create_f2py_signature` the command is very
# simple as there are properties set on the signature file enabling full automatic discovery.
#
# Create a library from a f2py signature created file
# Note that.
# For fortran 77 files we have this:
# Input file blah.f
# Generates
# blahmodule.c
# blah-f2pywrappers.f
# When no COMMON blocks are present only a C wrapper file is generated. Wrappers are also generated to rewrite assumed shape arrays as automatic arrays.
# For fortran 90 files we have this:
# Input file blah.f90
# Generates:
# blahmodule.c
# blah-f2pywrappers.f
# blah-f2pywrappers2.f90
# The f90 wrapper is used to handle code which is subdivided into modules. The f wrapper makes subroutines for functions. It rewrites assumed shape arrays as automatic arrays.
# When WITH_COMMON is present, it means that some of the f files present will have common blocks
# When WITH_FUNCTIONS is present, it means that some of the f90 files present will have functions that will be wrapped in the .f file.
# When WITHOUT_MODULES is present the f90 file will be generated
# The code will adapt which wrappers are generated
function(add_f2py_library)
set(options WITH_F_WRAPPER WITH_F90_WRAPPER)
set(oneValueArgs
SIGNATURE # in
LIBRARY # in[opt] default to module name
MODULE # in[opt] default to get_property({SIGNATURE} SISL_MODULE_NAME)
F2PY_EXECUTABLE # in[opt]
F2PY_FLAGS # in[opt]
OUTPUTS # out
)
set(multiValueArgs
SOURCES # in[opt] addition to get_property({SIGNATURE} SISL_FROM_SOURCES)
DEPENDS # in[opt]
)
cmake_parse_arguments(_f "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
# Retrive module name
if(NOT DEFINED _f_MODULE)
get_source_file_property(_f_MODULE "${_f_SIGNATURE}" SISL_MODULE_NAME)
endif()
if(NOT _f_MODULE)
message(SEND_ERROR "add_f2py_library missing MODULE argument and/or the signature file misses the property SISL_MODULE_NAME")
endif()
if(NOT DEFINED _f_LIBRARY)
set(_f_LIBRARY "${_f_MODULE}")
endif()
get_source_file_property(_sig_SOURCES "${_f_SIGNATURE}" SISL_FROM_SOURCES)
# Add signature sources
list(PREPEND _f_SOURCES ${_sig_SOURCES})
# If the user has accidentially supplide duplicates, lets remove them
list(REMOVE_DUPLICATES _f_SOURCES)
# retrieve the signatures target creator
get_source_file_property(_gen_signature_target "${_f_SIGNATURE}" SISL_GENERATE_TARGET)
if(NOT DEFINED _f_F2PY_EXECUTABLE)
set(_f_F2PY_EXECUTABLE "${F2PY_EXECUTABLE}")
endif()
# get current path relative to the project top-directory
cmake_path(RELATIVE_PATH CMAKE_CURRENT_SOURCE_DIR
BASE_DIRECTORY "${CMAKE_PROJECT_DIR}"
OUTPUT_VARIABLE _current_rel_path)
# Search for source files whether there are f or f90 files
# and also which wrappers are generated based on input
set(_has_f FALSE)
set(_has_f90 FALSE)
set(_has_unknown)
foreach(f IN LISTS _sig_SOURCES)
cmake_path(GET f EXTENSION LAST_ONLY ext)
if(${ext} STREQUAL ".f")
set(_has_f TRUE)
elseif(${ext} STREQUAL ".f90")
set(_has_f90 TRUE)
else()
list(APPEND _has_unknown ${ext})
endif()
endforeach()
if(_has_unknown)
message(WARNING "Unknown extensions (${_has_unknown}) found for library ${_f_LIBRARY} (module: ${_f_MODULE})")
endif()
set(_wrappers)
if(_f_WITH_F_WRAPPER)
list(APPEND _wrappers "${CMAKE_CURRENT_BINARY_DIR}/${_f_MODULE}-f2pywrappers.f")
endif()
if(_f_WITH_F90_WRAPPER)
list(APPEND _wrappers "${CMAKE_CURRENT_BINARY_DIR}/${_f_MODULE}-f2pywrappers2.f90")
endif()
#set(_wrappers)
#if(_has_f)
# if(_f_WITH_COMMON)
# list(APPEND _wrappers "${CMAKE_CURRENT_BINARY_DIR}/${_f_MODULE}-f2pywrappers.f")
# endif()
#endif()
#if(_has_f90)
# if(NOT _f_WITHOUT_MODULES)
# list(APPEND _wrappers "${CMAKE_CURRENT_BINARY_DIR}/${_f_MODULE}-f2pywrappers2.f90")
# endif()
# if(_f_WITH_FUNCTIONS)
# list(APPEND _wrappers "${CMAKE_CURRENT_BINARY_DIR}/${_f_MODULE}-f2pywrappers.f")
# endif()
#endif()
list(REMOVE_DUPLICATES _wrappers)
# default to a C output, I don't think f2py can do anything differently
set(extension "c")
set(_module_file "${CMAKE_CURRENT_BINARY_DIR}/${_f_MODULE}module.${extension}")
# add custom target to allow parallel builds
sisl_file_as_gentarget("${_module_file}" _gen_target)
add_custom_target(${_gen_target} DEPENDS "${_module_file}")
add_custom_command(
OUTPUT "${_module_file}"
BYPRODUCTS "${_wrappers}"
DEPENDS ${_f_SOURCES} ${_f_DEPENDS} "${_f_SIGNATURE}" ${_gen_signature_target}
COMMAND "${_f_F2PY_EXECUTABLE}" "${_f_F2PY_FLAGS}" "${_f_SIGNATURE}" "--lower"
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
COMMAND_EXPAND_LISTS
VERBATIM
COMMENT "Generating module file from signature ${_f_SIGNATURE}"
)
python_add_library(${_f_LIBRARY} WITH_SOABI
MODULE "${_module_file}" ${_f_SOURCES} ${_wrappers}
)
target_link_libraries(${_f_LIBRARY} PRIVATE ${F2PY_LIBRARIES})
# ensure direct dependency on the source generator (for parallel builds)
add_dependencies(${_f_LIBRARY} ${_gen_target})
if(DEFINED _f_OUTPUTS)
set(${_f_OUTPUTS} "${_module_file}" ${_wrappers}
PARENT_SCOPE)
endif()
endfunction(add_f2py_library)
# Add the sisl directory (nested CMakeLists.txt files)
add_subdirectory("src")