1
+ import os
2
+ import shutil
3
+ import fypp
4
+ import argparse
5
+ from joblib import Parallel , delayed
6
+
7
+ def copy_folder_with_filter (source_folder , destination_folder , filter_list = None , filter_suffix = None ):
8
+ # Create destination folder if it doesn't exist
9
+ if not os .path .exists (destination_folder ):
10
+ os .makedirs (destination_folder )
11
+
12
+ # Iterate over the files and folders in the source folder
13
+ for item in os .listdir (source_folder ):
14
+ source_item = os .path .join (source_folder , item )
15
+ destination_item = os .path .join (destination_folder , item )
16
+
17
+ # If it's a folder, recursively copy it
18
+ if os .path .isdir (source_item ):
19
+ copy_folder_with_filter (source_item , destination_item , filter_list , filter_suffix )
20
+ else :
21
+ # If filter_list is provided, check if the filename is in the list to avoid it
22
+ should_copy = False if filter_list and item in filter_list else True
23
+ should_copy = False if filter_suffix and item .endswith (filter_suffix ) else should_copy
24
+ if should_copy :
25
+ shutil .copy2 (source_item , destination_item )
26
+
27
+ def pre_process_toml (args ):
28
+ """
29
+ Pre-process the fpm.toml
30
+ """
31
+ from tomlkit import table , dumps
32
+ data = table ()
33
+ data .add ("name" , "stdlib" )
34
+ data .add ("version" , str (args .vmajor )+
35
+ "." + str (args .vminor )+
36
+ "." + str (args .vpatch ) )
37
+ data .add ("license" , "MIT" )
38
+ data .add ("author" , "stdlib contributors" )
39
+ data .add ("maintainer" , "@fortran-lang/stdlib" )
40
+ data .add ("copyright" , "2019-2021 stdlib contributors" )
41
+
42
+ if (args .with_blp ):
43
+ build = table ()
44
+ build .add ("link" , ["lapack" , "blas" ] )
45
+ data .add ("build" , build )
46
+
47
+ dev_dependencies = table ()
48
+ dev_dependencies .add ("test-drive" , {"git" : "https://github.com/fortran-lang/test-drive" ,
49
+ "tag" : "v0.4.0" })
50
+ data .add ("dev-dependencies" , dev_dependencies )
51
+
52
+ preprocess = table ()
53
+ preprocess .add ("cpp" , {} )
54
+ preprocess ['cpp' ].add ("suffixes" , [".F90" , ".f90" ] )
55
+ preprocess ['cpp' ].add ("macros" , ["MAXRANK=" + str (args .maxrank )] )
56
+ data .add ("preprocess" , preprocess )
57
+
58
+ if args .destdir == 'stdlib-fpm' :
59
+ if not os .path .exists ('stdlib-fpm' ):
60
+ os .makedirs ('stdlib-fpm' )
61
+ name = 'stdlib-fpm' + os .sep + 'fpm.toml'
62
+ else :
63
+ name = "fpm.toml"
64
+ with open (name , "w" ) as f :
65
+ f .write (dumps (data ))
66
+ return
67
+
68
+ C_PREPROCESSED = (
69
+ "stdlib_linalg_constants" ,
70
+ "stdlib_linalg_blas" ,
71
+ "stdlib_linalg_blas_aux" ,
72
+ "stdlib_linalg_blas_s" ,
73
+ "stdlib_linalg_blas_d" ,
74
+ "stdlib_linalg_blas_q" ,
75
+ "stdlib_linalg_blas_c" ,
76
+ "stdlib_linalg_blas_z" ,
77
+ "stdlib_linalg_blas_w" ,
78
+ "stdlib_linalg_lapack" ,
79
+ "stdlib_linalg_lapack_aux" ,
80
+ "stdlib_linalg_lapack_s" ,
81
+ "stdlib_linalg_lapack_d" ,
82
+ "stdlib_linalg_lapack_q" ,
83
+ "stdlib_linalg_lapack_c" ,
84
+ "stdlib_linalg_lapack_z" ,
85
+ "stdlib_linalg_lapack_w"
86
+ )
87
+
88
+ def pre_process_fypp (args ):
89
+ kwd = []
90
+ kwd .append ("-DMAXRANK=" + str (args .maxrank ))
91
+ kwd .append ("-DPROJECT_VERSION_MAJOR=" + str (args .vmajor ))
92
+ kwd .append ("-DPROJECT_VERSION_MINOR=" + str (args .vminor ))
93
+ kwd .append ("-DPROJECT_VERSION_PATCH=" + str (args .vpatch ))
94
+ if args .with_qp :
95
+ kwd .append ("-DWITH_QP=True" )
96
+ if args .with_xqp :
97
+ kwd .append ("-DWITH_XDP=True" )
98
+
99
+ optparser = fypp .get_option_parser ()
100
+ options , leftover = optparser .parse_args (args = kwd )
101
+ options .includes = ['include' ]
102
+ # options.line_numbering = True
103
+ tool = fypp .Fypp (options )
104
+
105
+ # Check destination folder for preprocessing. if not 'stdlib-fpm', it is assumed to be the root folder.
106
+ in_place = False if args .destdir == 'stdlib-fpm' else True
107
+ src = 'src' if in_place else 'stdlib-fpm' + os .sep + 'src'
108
+ test = 'test' if in_place else 'stdlib-fpm' + os .sep + 'test'
109
+ example = 'example' if in_place else 'stdlib-fpm' + os .sep + 'example'
110
+ if not in_place :
111
+ copy_folder_with_filter ('src' , src ,
112
+ filter_list = ['CMakeLists.txt' ,"f18estop.f90" ])
113
+ copy_folder_with_filter ('test' , test ,
114
+ filter_list = ['CMakeLists.txt' ,"test_always_fail.f90" ,
115
+ "test_always_skip.f90" ,"test_hash_functions.f90" ],
116
+ filter_suffix = 'manual' )
117
+ copy_folder_with_filter ('example' ,example ,
118
+ filter_list = ['CMakeLists.txt' ])
119
+ shutil .copy2 ('ci' + os .sep + 'fpm.toml' , 'stdlib-fpm' + os .sep + 'fpm.toml' )
120
+ shutil .copy2 ('LICENSE' , 'stdlib-fpm' + os .sep + 'LICENSE' )
121
+
122
+ # Define the folders to search for *.fypp files
123
+ folders = [src ,test ]
124
+ # Process all folders
125
+ fypp_files = [os .path .join (root , file ) for folder in folders
126
+ for root , _ , files in os .walk (folder )
127
+ for file in files if file .endswith (".fypp" )]
128
+
129
+ def process_f (file ):
130
+ source_file = file
131
+ root = os .path .dirname (file )
132
+ basename = os .path .splitext (os .path .basename (source_file ))[0 ]
133
+ sfx = 'f90' if basename not in C_PREPROCESSED else 'F90'
134
+ target_file = root + os .sep + basename + '.' + sfx
135
+ tool .process_file (source_file , target_file )
136
+ # if folder different from root
137
+ if not in_place :
138
+ os .remove (source_file )
139
+
140
+ Parallel (n_jobs = args .njob )(delayed (process_f )(f ) for f in fypp_files )
141
+
142
+ return
143
+
144
+ def fpm_build (unknown ):
145
+ import subprocess
146
+ #==========================================
147
+ # check compilers
148
+ FPM_FC = os .environ ['FPM_FC' ] if "FPM_FC" in os .environ else "gfortran"
149
+ FPM_CC = os .environ ['FPM_CC' ] if "FPM_CC" in os .environ else "gcc"
150
+ FPM_CXX = os .environ ['FPM_CXX' ] if "FPM_CXX" in os .environ else "gcc"
151
+ #==========================================
152
+ # Filter out the macro definitions.
153
+ macros = [arg for arg in unknown if arg .startswith ("-D" )]
154
+ # Filter out the include paths with -I prefix.
155
+ include_paths = [arg for arg in unknown if arg .startswith ("-I" )]
156
+ # Filter out flags
157
+ flags = " "
158
+ for idx , arg in enumerate (unknown ):
159
+ if arg .startswith ("--flag" ):
160
+ flags = unknown [idx + 1 ]
161
+ #==========================================
162
+ # build with fpm
163
+ subprocess .run (["fpm build" ]+
164
+ [" --compiler " ]+ [FPM_FC ]+
165
+ [" --c-compiler " ]+ [FPM_CC ]+
166
+ [" --cxx-compiler " ]+ [FPM_CXX ]+
167
+ [" --flag " ]+ [flags ], shell = True , check = True )
168
+ return
169
+
170
+ if __name__ == "__main__" :
171
+ parser = argparse .ArgumentParser (description = 'Preprocess stdlib source files.' )
172
+ # fypp arguments
173
+ parser .add_argument ("--vmajor" , type = int , default = 0 , help = "Project Version Major" )
174
+ parser .add_argument ("--vminor" , type = int , default = 5 , help = "Project Version Minor" )
175
+ parser .add_argument ("--vpatch" , type = int , default = 0 , help = "Project Version Patch" )
176
+
177
+ parser .add_argument ("--njob" , type = int , default = 4 , help = "Number of parallel jobs for preprocessing" )
178
+ parser .add_argument ("--maxrank" ,type = int , default = 7 , help = "Set the maximum allowed rank for arrays" )
179
+ parser .add_argument ("--with_qp" ,type = bool , default = False , help = "Include WITH_QP in the command" )
180
+ parser .add_argument ("--with_xqp" ,type = bool , default = False , help = "Include WITH_XDP in the command" )
181
+
182
+ parser .add_argument ('--destdir' , action = 'store' , type = str , default = 'stdlib-fpm' , help = 'destination directory for the fypp preprocessing.' )
183
+ # external libraries arguments
184
+ parser .add_argument ("--with_blp" ,type = bool , default = False , help = "Link against OpenBLAS" )
185
+ parser .add_argument ("--build" ,type = bool , default = False , help = "Build the project" )
186
+
187
+ args , unknown = parser .parse_known_args ()
188
+ #==========================================
189
+ # read current manifest
190
+ import tomlkit
191
+ with open ('ci' + os .sep + 'fpm.toml' , 'r' ) as file :
192
+ manifest = tomlkit .parse (file .read ())
193
+ version = manifest ['version' ].split ("." )
194
+ if version != ['VERSION' ]:
195
+ vmajor , vminor , vpatch = [int (value ) for value in version ]
196
+ #==========================================
197
+ # pre process the fpm manifest
198
+ #pre_process_toml(args)
199
+ #==========================================
200
+ # pre process the meta programming fypp files
201
+ pre_process_fypp (args )
202
+ #==========================================
203
+ # build using fpm
204
+ if args .build :
205
+ fpm_build (unknown )
0 commit comments