3 # Copyright The SCons Foundation
5 # Permission is hereby granted, free of charge, to any person obtaining
6 # a copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish,
9 # distribute, sublicense, and/or sell copies of the Software, and to
10 # permit persons to whom the Software is furnished to do so, subject to
11 # the following conditions:
13 # The above copyright notice and this permission notice shall be included
14 # in all copies or substantial portions of the Software.
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
17 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
18 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 """Routines for setting up Fortran, common to all dialects."""
26 from __future__
import annotations
31 import SCons
.Scanner
.Fortran
34 from SCons
.Action
import Action
, CommandAction
35 from SCons
.Defaults
import StaticObjectEmitter
, SharedObjectEmitter
38 def isfortran(env
, source
) -> bool:
39 """Returns True if *source* has any fortran files in it.
41 Only checks based on filename suffixes, does not examine code.
44 fsuffixes
= env
['FORTRANSUFFIXES']
46 # If no FORTRANSUFFIXES, no fortran tool, so there is no need to look
47 # for fortran sources.
51 # Source might be None for unusual cases like SConf.
55 ext
= os
.path
.splitext(str(s
.sources
[0]))[1]
61 def _fortranEmitter(target
, source
, env
) -> Tuple
:
62 """Common code for Fortran emitter.
64 Called by both the static and shared object emitters,
65 mainly to account for generated module files.
67 node
= source
[0].rfile()
68 if not node
.exists() and not node
.is_derived():
69 print("Could not locate " + str(node
.name
))
72 # This has to match the def_regex in the Fortran scanner
73 mod_regex
= r
"""(?i)^\s*MODULE\s+(?!PROCEDURE|SUBROUTINE|FUNCTION|PURE|ELEMENTAL)(\w+)"""
74 cre
= re
.compile(mod_regex
, re
.M
)
75 # Retrieve all USE'd module names
76 modules
= cre
.findall(node
.get_text_contents())
77 # Remove unique items from the list
78 modules
= SCons
.Util
.unique(modules
)
79 # Convert module name to a .mod filename
80 suffix
= env
.subst('$FORTRANMODSUFFIX', target
=target
, source
=source
)
81 moddir
= env
.subst('$FORTRANMODDIR', target
=target
, source
=source
)
82 modules
= [mod
.lower() + suffix
for mod
in modules
]
83 for module
in modules
:
84 target
.append(env
.fs
.File(module
, moddir
))
88 def FortranEmitter(target
, source
, env
) -> Tuple
:
89 """Create emitter for static objects."""
90 target
, source
= _fortranEmitter(target
, source
, env
)
91 return StaticObjectEmitter(target
, source
, env
)
94 def ShFortranEmitter(target
, source
, env
) -> Tuple
:
95 """Create emitter for shared objects."""
96 target
, source
= _fortranEmitter(target
, source
, env
)
97 return SharedObjectEmitter(target
, source
, env
)
100 def ComputeFortranSuffixes(suffixes
: list[str], ppsuffixes
: list[str]) -> None:
101 """Update the suffix lists to reflect the platform requirements.
103 If upper-cased suffixes can be distinguished from lower, those are
104 added to *ppsuffixes*. If not, they are added to *suffixes*.
107 suffixes: regular Fortran source files
108 ppsuffixes: Fortran source files that should be
109 be run through the pre-processor
111 assert len(suffixes
) > 0
114 upper_suffixes
= [suf
.upper() for suf
in suffixes
]
115 if SCons
.Util
.case_sensitive_suffixes(s
, sup
):
116 ppsuffixes
.extend(upper_suffixes
)
118 suffixes
.extend(upper_suffixes
)
121 def CreateDialectActions(
123 ) -> tuple[CommandAction
, CommandAction
, CommandAction
, CommandAction
]:
124 """Create dialect specific actions."""
125 CompAction
= Action(f
'${dialect}COM ', cmdstr
=f
'${dialect}COMSTR')
126 CompPPAction
= Action(f
'${dialect}PPCOM ', cmdstr
=f
'${dialect}PPCOMSTR')
127 ShCompAction
= Action(f
'$SH{dialect}COM ', cmdstr
=f
'$SH{dialect}COMSTR')
128 ShCompPPAction
= Action(f
'$SH{dialect}PPCOM ', cmdstr
=f
'$SH{dialect}PPCOMSTR')
129 return CompAction
, CompPPAction
, ShCompAction
, ShCompPPAction
136 ppsuffixes
: list[str],
137 support_mods
: bool = False,
139 """Add dialect specific construction variables.
142 dialect: dialect name
143 suffixes: suffixes associated with this dialect
144 ppsuffixes: suffixes using cpp associated with this dialect
145 support_mods: whether this dialect supports modules
147 ComputeFortranSuffixes(suffixes
, ppsuffixes
)
149 fscan
= SCons
.Scanner
.Fortran
.FortranScan(f
"{dialect}PATH")
150 for suffix
in suffixes
+ ppsuffixes
:
151 SCons
.Tool
.SourceFileScanner
.add_scanner(suffix
, fscan
)
153 env
.AppendUnique(FORTRANSUFFIXES
=suffixes
+ ppsuffixes
)
155 compaction
, compppaction
, shcompaction
, shcompppaction
= \
156 CreateDialectActions(dialect
)
157 static_obj
, shared_obj
= SCons
.Tool
.createObjBuilders(env
)
159 for suffix
in suffixes
:
160 static_obj
.add_action(suffix
, compaction
)
161 shared_obj
.add_action(suffix
, shcompaction
)
162 static_obj
.add_emitter(suffix
, FortranEmitter
)
163 shared_obj
.add_emitter(suffix
, ShFortranEmitter
)
165 for suffix
in ppsuffixes
:
166 static_obj
.add_action(suffix
, compppaction
)
167 shared_obj
.add_action(suffix
, shcompppaction
)
168 static_obj
.add_emitter(suffix
, FortranEmitter
)
169 shared_obj
.add_emitter(suffix
, ShFortranEmitter
)
171 if f
'{dialect}FLAGS' not in env
:
172 env
[f
'{dialect}FLAGS'] = SCons
.Util
.CLVar('')
173 if f
'SH{dialect}FLAGS' not in env
:
174 env
[f
'SH{dialect}FLAGS'] = SCons
.Util
.CLVar(f
'${dialect}FLAGS')
176 # If a tool does not define fortran prefix/suffix for include path, use C ones
177 if f
'INC{dialect}PREFIX' not in env
:
178 env
[f
'INC{dialect}PREFIX'] = '$INCPREFIX'
179 if f
'INC{dialect}SUFFIX' not in env
:
180 env
[f
'INC{dialect}SUFFIX'] = '$INCSUFFIX'
182 env
[f
'_{dialect}INCFLAGS'] = f
'${{_concat(INC{dialect}PREFIX, {dialect}PATH, INC{dialect}SUFFIX, __env__, RDirs, TARGET, SOURCE, affect_signature=False)}}'
185 env
[f
'{dialect}COM'] = f
'${dialect} -o $TARGET -c $FORTRANCOMMONFLAGS ${dialect}FLAGS $_{dialect}INCFLAGS $_FORTRANMODFLAG $SOURCES'
186 env
[f
'{dialect}PPCOM'] = f
'${dialect} -o $TARGET -c $FORTRANCOMMONFLAGS ${dialect}FLAGS $CPPFLAGS $_CPPDEFFLAGS $_{dialect}INCFLAGS $_FORTRANMODFLAG $SOURCES'
187 env
[f
'SH{dialect}COM'] = f
'$SH{dialect} -o $TARGET -c $FORTRANCOMMONFLAGS $SH{dialect}FLAGS $_{dialect}INCFLAGS $_FORTRANMODFLAG $SOURCES'
188 env
[f
'SH{dialect}PPCOM'] = f
'$SH{dialect} -o $TARGET -c $FORTRANCOMMONFLAGS $SH{dialect}FLAGS $CPPFLAGS $_CPPDEFFLAGS $_{dialect}INCFLAGS $_FORTRANMODFLAG $SOURCES'
190 env
[f
'{dialect}COM'] = f
'${dialect} -o $TARGET -c $FORTRANCOMMONFLAGS ${dialect}FLAGS $_{dialect}INCFLAGS $SOURCES'
191 env
[f
'{dialect}PPCOM'] = f
'${dialect} -o $TARGET -c $FORTRANCOMMONFLAGS ${dialect}FLAGS $CPPFLAGS $_CPPDEFFLAGS $_{dialect}INCFLAGS $SOURCES'
192 env
[f
'SH{dialect}COM'] = f
'$SH{dialect} -o $TARGET -c $FORTRANCOMMONFLAGS $SH{dialect}FLAGS $_{dialect}INCFLAGS $SOURCES'
193 env
[f
'SH{dialect}PPCOM'] = f
'$SH{dialect} -o $TARGET -c $FORTRANCOMMONFLAGS $SH{dialect}FLAGS $CPPFLAGS $_CPPDEFFLAGS $_{dialect}INCFLAGS $SOURCES'
196 def add_fortran_to_env(env
) -> None:
197 """Add Builders and construction variables for Fortran/generic."""
198 FortranSuffixes
= env
.get('FORTRANFILESUFFIXES', ['.f', '.for', '.ftn'])
199 FortranPPSuffixes
= env
.get('FORTRANPPFILESUFFIXES', ['.fpp', '.FPP'])
200 DialectAddToEnv(env
, "FORTRAN", FortranSuffixes
, FortranPPSuffixes
, support_mods
=True)
203 env
['FORTRANMODPREFIX'] = '' # like $LIBPREFIX
204 env
['FORTRANMODSUFFIX'] = '.mod' # like $LIBSUFFIX
205 env
['FORTRANMODDIR'] = '' # where the compiler should place .mod files
206 env
['FORTRANMODDIRPREFIX'] = '' # some prefix to $FORTRANMODDIR - similar to $INCPREFIX
207 env
['FORTRANMODDIRSUFFIX'] = '' # some suffix to $FORTRANMODDIR - similar to $INCSUFFIX
208 env
['_FORTRANMODFLAG'] = '$( ${_concat(FORTRANMODDIRPREFIX, FORTRANMODDIR, FORTRANMODDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)'
210 def add_f77_to_env(env
) -> None:
211 """Add Builders and construction variables for f77 dialect."""
212 F77Suffixes
= env
.get('F77FILESUFFIXES', ['.f77'])
213 F77PPSuffixes
= env
.get('F77PPFILESUFFIXES', [])
214 DialectAddToEnv(env
, "F77", F77Suffixes
, F77PPSuffixes
)
216 def add_f90_to_env(env
) -> None:
217 """Add Builders and construction variables for f90 dialect."""
218 F90Suffixes
= env
.get('F90FILESUFFIXES', ['.f90'])
219 F90PPSuffixes
= env
.get('F90PPFILESUFFIXES', [])
220 DialectAddToEnv(env
, "F90", F90Suffixes
, F90PPSuffixes
, support_mods
=True)
222 def add_f95_to_env(env
) -> None:
223 """Add Builders and construction variables for f95 dialect."""
224 F95Suffixes
= env
.get('F95FILESUFFIXES', ['.f95'])
225 F95PPSuffixes
= env
.get('F95PPFILESUFFIXES', [])
226 DialectAddToEnv(env
, "F95", F95Suffixes
, F95PPSuffixes
, support_mods
=True)
228 def add_f03_to_env(env
) -> None:
229 """Add Builders and construction variables for f03 dialect."""
230 F03Suffixes
= env
.get('F03FILESUFFIXES', ['.f03'])
231 F03PPSuffixes
= env
.get('F03PPFILESUFFIXES', [])
232 DialectAddToEnv(env
, "F03", F03Suffixes
, F03PPSuffixes
, support_mods
=True)
234 def add_f08_to_env(env
) -> None:
235 """Add Builders and construction variables for f08 dialect."""
236 F08Suffixes
= env
.get('F08FILESUFFIXES', ['.f08'])
237 F08PPSuffixes
= env
.get('F08PPFILESUFFIXES', [])
238 DialectAddToEnv(env
, "F08", F08Suffixes
, F08PPSuffixes
, support_mods
=True)
240 def add_all_to_env(env
) -> None:
241 """Add builders and construction variables for all supported dialects."""
242 add_fortran_to_env(env
)
251 # indent-tabs-mode:nil
253 # vim: set expandtab tabstop=4 shiftwidth=4: