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