我很难理解 SCons 的逻辑,即使在阅读了一些资料并花了相当多的时间编码之后也是如此。我还没有找到任何与我想要完成的任务相关的使用 SCons 的合理可理解的 Fortran 项目。我还找到了任何深入的教程或解释(官方手册并没有真正告诉后端如何工作)。
我的目标是自动编译一个Fortran项目,其源文件分散在从某个根目录开始的目录树中。
代码概要:
fortdepend
生成依赖关系并区分程序和模块源之间的依赖关系通过 fortdepend 解决。但我还是坚持告诉 SCons 依赖关系。由于 SCons 具有明确的 Fortran 支持,因此对于这样的通用用例,我的解决方案感觉很复杂。
这是我的
SConstruct
文件:
import os
import fortdepend as fd
from SCons.Environment import Environment
fexts = ['.f', '.for', '.f90', '.f95', '.f03', '.f08']
def find_fortran_files(root_dir, fortran_extensions, abspath=False):
"""Find all Fortran source files in src directory and its subdirectories"""
source_files = []
for dir, _, files in os.walk(root_dir):
for file in files:
if any(file.endswith(ext) for ext in fortran_extensions):
source_files.append( os.path.join(dir, file) if abspath else os.path.join(dir.replace(root_dir, '.'), file))
return source_files
def fortran_source_to_object(source, fortran_extensions):
"""replace the fortran ext with .o"""
for ext in fortran_extensions:
if source.endswith(ext):
return source.replace(ext, '.o')
s2o = lambda src: fortran_source_to_object(str(src), fexts)
ss2os = lambda srcs: [fortran_source_to_object(str(src), fexts) for src in srcs]
def generate_fortran_dependencies(source_files, fortran_extensions, **kwargs):
""" use fortdepend to get dependencies """
fproj = fd.FortranProject(files=source_files, **kwargs)
source_deps, prog_deps = {}, {}
for key, val in fproj.depends_by_module.items():
_key = key.source_file.filename
_val = [v.source_file.filename for v in val]
if key.unit_type=='module':
source_deps[_key] = _val
elif key.unit_type=='program':
prog_deps[_key] = _val
return source_deps, prog_deps
env = Environment(tools=['default', 'gfortran'], F90='gfortran', LINK='gfortran', LINKFLAGS='', F90FLAGS='')
all_files = find_fortran_files(os.getcwd(), fortran_extensions=fexts, abspath=False)
print('- all files found with fortran ext: ', all_files)
source_deps, prog_deps = generate_fortran_dependencies(all_files, fexts)
source_files = list(source_deps.keys())
prog_files = list(prog_deps.keys())
print('- all source deps (no progs) ', source_deps)
print('- all program deps found ', prog_deps)
print('- all source files (no progs) ', source_files)
print('- all prog files ', prog_files)
objects = []
all_deps = {**source_deps, **prog_deps}
for src in all_deps:
obj_path = os.path.splitext(src)[0] + '.o'
object = env.Object(obj_path, src)
print(f'-- tell SCons {obj_path} also depends on {all_deps[src]}')
env.Depends(target=object, dependency = all_deps[src])
objects.append(object)
print('- all SCons objects', [str(o) for o in objects])
for prog in prog_deps:
prog_name = os.path.splitext(os.path.basename(prog))[0]
print(f'-- tell SCons {prog_name} also depends on {ss2os(prog_deps[prog])}')
env.Depends(target=prog_name, dependency = ss2os(prog_deps[prog]))
# Filter out .mod files from the objects list
objects_for_linking = [str(o[0]) for o in objects] # get only the first entry which is the object file
print('- all SCons objects for linking', objects_for_linking)
prog_to_make = 'main'
env.Program(target=prog_to_make, source=objects_for_linking)
在下面的存储库中
https://github.com/alexksr/SconsMWE.git
有两个Python脚本,用于使用示例Fortran代码设置根目录。 init1.py
存在依赖关系,按字母顺序编译时可以满足这些依赖关系(简单,非用例)。第二个 init2.py
具有依赖性,必须明确考虑这些依赖性(module0
取决于 module1
和 module2
)。对于梯子,我的代码不起作用,但结果是:
scons: Reading SConscript files ...
- all files found with fortran ext: ['./src/module0.f90', './src/main.f90', './src/module1.f90', './src/module2.f90', './src/utils/util_module.f90']
- all source deps (no progs) {'./src/module0.f90': ['./src/module1.f90', './src/module2.f90'], './src/module1.f90': [], './src/module2.f90': [], './src/utils/util_module.f90': []}
- all program deps found {'./src/main.f90': ['./src/module0.f90', './src/module1.f90', './src/module2.f90', './src/utils/util_module.f90']}
- all source files (no progs) ['./src/module0.f90', './src/module1.f90', './src/module2.f90', './src/utils/util_module.f90']
- all prog files ['./src/main.f90']
-- tell SCons ./src/module0.o also depends on ['./src/module1.f90', './src/module2.f90']
-- tell SCons ./src/module1.o also depends on []
-- tell SCons ./src/module2.o also depends on []
-- tell SCons ./src/utils/util_module.o also depends on []
-- tell SCons ./src/main.o also depends on ['./src/module0.f90', './src/module1.f90', './src/module2.f90', './src/utils/util_module.f90']
- all SCons objects ["['src/module0.o', 'module0.mod']", "['src/module1.o', 'module1.mod']", "['src/module2.o', 'module2.mod']", "['src/utils/util_module.o', 'util_module.mod']", "['src/main.o']"]
-- tell SCons main also depends on ['./src/module0.o', './src/module1.o', './src/module2.o', './src/utils/util_module.o']
- all SCons objects for linking ['src/module0.o', 'src/module1.o', 'src/module2.o', 'src/utils/util_module.o', 'src/main.o']
scons: done reading SConscript files.
scons: Building targets ...
gfortran -o src/module0.o -c src/module0.f90
src/module0.f90:3:8:
use module1
1
Fatal Error: Can't open module file 'module1.mod' for reading at (1): No such file or directory
compilation terminated.
scons: *** [src/module0.o] Error 1
scons: building terminated because of errors.
在我放弃 SCons 并回去制作之前,感谢任何帮助。
试试这个,假设您只从所有生成的源中构建一个程序。无需显式使用 Depends(),因为 SCons 会自动在程序->对象->源之间创建依赖关系。
import os
fexts = ['.f', '.for', '.f90', '.f95', '.f03', '.f08']
def find_fortran_files(root_dir, fortran_extensions, abspath=False):
"""Find all Fortran source files in src directory and its subdirectories"""
source_files = []
for dir, _, files in os.walk(root_dir):
for file in files:
if any(file.endswith(ext) for ext in fortran_extensions):
source_files.append( os.path.join(dir, file) if abspath else os.path.join(dir.replace(root_dir, '.'), file))
return source_files
env = Environment(tools=['default', 'gfortran'], F90='gfortran', LINK='gfortran', LINKFLAGS='', F90FLAGS='')
source_files = find_fortran_files(os.getcwd(), fortran_extensions=fexts, abspath=False)
print('- all files found with fortran ext: ', all_files)
print('- all source files (no progs) ', source_files)
prog_to_make = 'main'
env.Program(target=prog_to_make, source=source_files)