Closed
Description
Motivation
This proposal is meant to start a discussion on a replacement for the current fpm
ci for stdlib. The idea would be to use a python script to preprocess stdlib before building with fpm or CMake. While CMake already has a customized fypp preprocessor, such script could serve as a replacement using a PRE_BUILD
action with add_custom_command
. Also, currently the fpm
branch lacks the means for a flexible way of adding dependencies in the toml file. This proposal would try to remedy these shortcomings.
Prior Art
No response
Additional Information
Proposal
Say a fypp_deployement.py
file is at the root of stdlib:
import os
import fypp
import argparse
from joblib import Parallel, delayed
def pre_process_toml(kargs):
"""
Pre-process the fpm.toml
"""
from tomlkit import table, dumps
data = table()
data.add("name", "stdlib")
data.add("version", str(kargs.vmajor)+
"."+str(kargs.vminor)+
"."+str(kargs.vpatch) )
data.add("license", "MIT")
data.add("author", "stdlib contributors")
data.add("maintainer", "@fortran-lang/stdlib")
data.add("copyright", "2019-2021 stdlib contributors")
if(kargs.with_blp):
build = table()
build.add("link", ["lapack", "blas"] )
data.add("build", build)
dev_dependencies = table()
dev_dependencies.add("test-drive", {"git" : "https://github.com/fortran-lang/test-drive",
"tag" : "v0.4.0"})
data.add("dev-dependencies", dev_dependencies)
preprocess = table()
preprocess.add("cpp", {} )
preprocess['cpp'].add("suffixes", [".F90", ".f90", ".fypp"] )
preprocess['cpp'].add("macros", ["MAXRANK="+str(kargs.maxrank),
"PROJECT_VERSION_MAJOR="+str(kargs.vmajor),
"PROJECT_VERSION_MINOR="+str(kargs.vminor),
"PROJECT_VERSION_PATCH="+str(kargs.vpatch)] )
data.add("preprocess", preprocess)
with open("fpm.toml", "w") as f:
f.write(dumps(data))
C_PREPROCESSED = (
"stdlib_linalg_constants" ,
"stdlib_linalg_blas" ,
"stdlib_linalg_blas_aux",
"stdlib_linalg_blas_s",
"stdlib_linalg_blas_d",
"stdlib_linalg_blas_q",
"stdlib_linalg_blas_c",
"stdlib_linalg_blas_z",
"stdlib_linalg_blas_w",
"stdlib_linalg_lapack",
"stdlib_linalg_lapack_aux",
"stdlib_linalg_lapack_s",
"stdlib_linalg_lapack_d",
"stdlib_linalg_lapack_q",
"stdlib_linalg_lapack_c",
"stdlib_linalg_lapack_z",
"stdlib_linalg_lapack_w"
)
def pre_process_fypp(kargs):
kwd = []
kwd.append("-DMAXRANK="+str(kargs.maxrank))
kwd.append("-DPROJECT_VERSION_MAJOR="+str(kargs.vmajor))
kwd.append("-DPROJECT_VERSION_MINOR="+str(kargs.vminor))
kwd.append("-DPROJECT_VERSION_PATCH="+str(kargs.vpatch))
if kargs.with_qp:
kwd.append("-DWITH_QP=True")
if kargs.with_xqp:
kwd.append("-DWITH_XQP=True")
optparser = fypp.get_option_parser()
options, leftover = optparser.parse_args(args=kwd)
options.includes = ['include']
# options.line_numbering = True
tool = fypp.Fypp(options)
# Define the folders to search for *.fypp files
folders = ['src', 'test', 'example']
# Process all folders
fypp_files = [os.path.join(root, file) for folder in folders
for root, _, files in os.walk(folder)
for file in files if file.endswith(".fypp")]
def process_f(file):
source_file = file
basename = os.path.splitext(source_file)[0]
sfx = 'f90' if os.path.basename(basename) not in C_PREPROCESSED else 'F90'
target_file = basename + '.' + sfx
tool.process_file(source_file, target_file)
Parallel(n_jobs=kargs.njob)(delayed(process_f)(f) for f in fypp_files)
return
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Preprocess stdlib source files.')
# fypp arguments
parser.add_argument("--vmajor", type=int, default=0, help="Project Version Major")
parser.add_argument("--vminor", type=int, default=4, help="Project Version Minor")
parser.add_argument("--vpatch", type=int, default=0, help="Project Version Patch")
parser.add_argument("--njob", type=int, default=4, help="Number of parallel jobs for preprocessing")
parser.add_argument("--maxrank",type=int, default=7, help="Set the maximum allowed rank for arrays")
parser.add_argument("--with_qp",type=bool, default=False, help="Include WITH_QP in the command")
parser.add_argument("--with_xqp",type=bool, default=False, help="Include WITH_XQP in the command")
# external libraries arguments
parser.add_argument("--with_blp",type=bool, default=False, help="Link against OpenBLAS")
args = parser.parse_args()
pre_process_toml(args)
pre_process_fypp(args)
Example:
python fypp_deployment.py --with_blp 1
Would produce a fpm.toml:
name = "stdlib"
version = "0.4.0"
license = "MIT"
author = "stdlib contributors"
maintainer = "@fortran-lang/stdlib"
copyright = "2019-2021 stdlib contributors"
[build]
link = ["lapack", "blas"]
[dev-dependencies]
[dev-dependencies.test-drive]
git = "https://github.com/fortran-lang/test-drive"
tag = "v0.4.0"
[preprocess]
[preprocess.cpp]
suffixes = [".F90", ".f90", ".fypp"]
macros = ["MAXRANK=7", "PROJECT_VERSION_MAJOR=0", "PROJECT_VERSION_MINOR=4", "PROJECT_VERSION_PATCH=0"]
Current limitations
- In this first example the fypp files are being preprocessed into .f90 files at the same location. Should they go somewhere else?