summaryrefslogtreecommitdiff
path: root/script/make_depend.py
diff options
context:
space:
mode:
authorCharles BIENVENUE <charles.bienvenue@polymtl.ca>2025-11-05 22:47:07 +0000
committerCharles BIENVENUE <charles.bienvenue@polymtl.ca>2025-11-05 22:47:07 +0000
commitf6804c8ce4114837ff6845ff11364cbfb057c918 (patch)
tree7e9ad87417236520023804b629e15e572bcb7c9d /script/make_depend.py
parent8e8dd16f67ed325e2bf7fca0a9d878a18e6911ae (diff)
parent2109f3ade9d6a202c413ed9ad3e3475caa944ae3 (diff)
Merge branch 'parallel_make' into 'main'
Parallel Build, OpenMP Support with Fortran 90, and Improved Module Dependencies See merge request dragon/5.1!19
Diffstat (limited to 'script/make_depend.py')
-rwxr-xr-xscript/make_depend.py279
1 files changed, 222 insertions, 57 deletions
diff --git a/script/make_depend.py b/script/make_depend.py
index 15c656e..ce8f688 100755
--- a/script/make_depend.py
+++ b/script/make_depend.py
@@ -1,57 +1,222 @@
-#!/bin/env python
-""" generation of module dependances for a xxx.f90 or xxx.F90 file """
-
-import string,sys
-from subprocess import Popen, PIPE
-
-def noComment(mot):
- """ remove comment characters in a word """
- ind= string.find(mot,'!')
- if ind != -1:
- mot= mot[:ind]
- return mot
-
-class md9:
- """ a file, its module and its dependances """
- def __init__(self,fic):
- self.name= fic
- self.used= []
- #find all module dependances
- listeLignes= open(self.name,'r').readlines()
- self.module= "dummy"
- for ligne in listeLignes:
- ligne= string.lower(ligne)
- if string.count(ligne,'use'):
- ligne= string.replace(ligne,',',' ')
- listeMots= string.split(ligne)
- if listeMots[0] == 'use':
- mod= noComment(listeMots[1])
- if mod not in self.used and mod != 'intrinsic':
- self.used.append(mod)
- elif string.count(ligne,'module'):
- listeMots= string.split(ligne)
- if listeMots[0] == 'module' and \
- noComment(listeMots[1]) != 'procedure':
- self.module= listeMots[1]
- def utilise(self,other):
- return (other.module in self.used)
-
-listeClasse= list()
-for x in sys.argv[1:]:
- if x.endswith('.f90') or x.endswith('.F90') : listeClasse.append(md9(x))
-#sort the file list
-listeRes= []
-while listeClasse:
- lgav= len(listeClasse)
- for fic in listeClasse:
- if len(filter(lambda x,y=fic:y.utilise(x),listeClasse)) == 0:
- listeRes.append(fic)
- listeClasse.remove(fic)
- lgap= len(listeClasse)
- if lgav == lgap and lgap != 0:
- raise RuntimeError("make_depend: cross-references found")
-#result output
-resstr=''
-for fic in listeRes:
- resstr=resstr+fic.name+' '
-print resstr
+#!/usr/bin/env python
+"""
+Generate Fortran dependencies from sources (*.f90, *.F90).
+
+Usage:
+ - Default: prints a dependency-ordered list of source files.
+ - --make-deps: prints Makefile-style dependency rules.
+"""
+
+from __future__ import print_function
+import sys
+import os
+import io
+import re
+import glob
+
+def strip_comment(line):
+ idx = line.find('!')
+ return line if idx == -1 else line[:idx]
+
+class FortranUnit(object):
+ USE_RE = re.compile(r"^\s*use\s+([a-z0-9_]+)", re.IGNORECASE)
+ MODULE_RE = re.compile(r"^\s*module\s+([a-z0-9_]+)", re.IGNORECASE)
+ MODULE_PROC_RE = re.compile(r"^\s*module\s+procedure\b", re.IGNORECASE)
+ END_MODULE_RE = re.compile(r"^\s*end\s*module\b", re.IGNORECASE)
+ CONTAINS_RE = re.compile(r"^\s*contains\b", re.IGNORECASE)
+ INCLUDE_RE = re.compile(r"^\s*include\s*[\'\"]([^\'\"]+)[\'\"]", re.IGNORECASE)
+
+ def __init__(self, path):
+ self.name = path
+ self.base = os.path.basename(path)
+ self.base_lc = self.base.lower()
+ self.module = None
+ self.uses = []
+ self.includes_in_contains = []
+ self._parse()
+
+ def _parse(self):
+ in_module = False
+ in_contains = False
+ try:
+ with io.open(self.name, 'r') as f:
+ lines = f.readlines()
+ except IOError:
+ return
+ for raw in lines:
+ line = strip_comment(raw).strip('\n')
+ if not line:
+ continue
+ low = line.lower()
+ if self.MODULE_RE.match(low) and not self.MODULE_PROC_RE.match(low):
+ m = self.MODULE_RE.match(low)
+ if m:
+ self.module = m.group(1)
+ in_module = True
+ in_contains = False
+ continue
+ if in_module and self.CONTAINS_RE.match(low):
+ in_contains = True
+ continue
+ if in_module and self.END_MODULE_RE.match(low):
+ in_module = False
+ in_contains = False
+ continue
+ m_use = self.USE_RE.match(low)
+ if m_use:
+ mod = m_use.group(1)
+ if mod != 'intrinsic' and mod not in self.uses:
+ self.uses.append(mod)
+ continue
+ if in_module and in_contains:
+ m_inc = self.INCLUDE_RE.match(low)
+ if m_inc:
+ inc = m_inc.group(1).strip()
+ self.includes_in_contains.append(os.path.basename(inc))
+
+def build_units(paths):
+ exts = (".f90", ".F90", ".f", ".F")
+ return [FortranUnit(p) for p in paths if p.lower().endswith(exts)]
+
+def build_dependency_order(paths):
+ units = build_units(paths)
+ by_base_lc = dict((u.base_lc, u) for u in units)
+ module_to_unit = dict((u.module, u) for u in units if u.module)
+ included_to_container = {}
+ for u in units:
+ if u.module and u.includes_in_contains:
+ for inc in u.includes_in_contains:
+ included_to_container.setdefault(inc.lower(), set()).add(u)
+
+ edges = dict((u, set()) for u in units)
+ indeg = dict((u, 0) for u in units)
+
+ def add_edge(src, dst):
+ if src is dst:
+ return
+ if dst not in edges[src]:
+ edges[src].add(dst)
+ indeg[dst] += 1
+
+ for u in units:
+ for mod in u.uses:
+ v = module_to_unit.get(mod)
+ if not v:
+ continue
+ included_containers = included_to_container.get(u.base_lc, set())
+ if v in included_containers:
+ continue
+ add_edge(v, u)
+
+ for container in units:
+ if container.module and container.includes_in_contains:
+ for inc in container.includes_in_contains:
+ inc_u = by_base_lc.get(inc.lower())
+ if inc_u:
+ add_edge(inc_u, container)
+
+ ordered = []
+ zero = [u for u in units if indeg[u] == 0]
+ zero.sort(key=lambda x: x.base_lc)
+ while zero:
+ n = zero.pop(0)
+ ordered.append(n)
+ for m in list(edges[n]):
+ edges[n].remove(m)
+ indeg[m] -= 1
+ if indeg[m] == 0:
+ zero.append(m)
+ zero.sort(key=lambda x: x.base_lc)
+
+ remaining = [u for u in units if u not in ordered]
+ if remaining:
+ remaining.sort(key=lambda x: (0 if not x.module else 1, x.base_lc))
+ ordered.extend(remaining)
+
+ excluded_bases = set()
+ for u in units:
+ if u.module and u.includes_in_contains:
+ for inc in u.includes_in_contains:
+ excluded_bases.add(inc.lower())
+
+ return [u.name for u in ordered if u.base_lc not in excluded_bases]
+
+def make_obj_name(path):
+ base, ext = os.path.splitext(path)
+ return base + ".o"
+
+def generate_make_deps(paths):
+ units = build_units(paths)
+ if not units:
+ return ""
+ by_base_lc = dict((u.base_lc, u) for u in units)
+ module_to_unit = dict((u.module, u) for u in units if u.module)
+ included_to_container = {}
+ for u in units:
+ if u.module and u.includes_in_contains:
+ for inc in u.includes_in_contains:
+ included_to_container.setdefault(inc.lower(), set()).add(u)
+
+ lines = []
+ for u in units:
+ deps_objs = set()
+ deps_files = set()
+
+ for mod in u.uses:
+ v = module_to_unit.get(mod)
+ if not v:
+ continue
+ included_containers = included_to_container.get(u.base_lc, set())
+ if v in included_containers:
+ continue
+ if v is not u:
+ deps_objs.add(make_obj_name(v.name))
+
+ if u.includes_in_contains:
+ for inc in u.includes_in_contains:
+ inc_u = by_base_lc.get(os.path.basename(inc).lower())
+ if inc_u:
+ deps_files.add(inc_u.name)
+ else:
+ deps_files.add(inc)
+
+ if deps_objs or deps_files:
+ target = make_obj_name(u.name)
+ deps = sorted(deps_objs) + sorted(deps_files)
+ lines.append("{}: {}".format(target, ' '.join(deps)))
+
+ return "\n".join(lines) + ("\n" if lines else "")
+
+def main():
+ argv = sys.argv[1:]
+ if not argv:
+ print("")
+ return
+
+ make_deps = False
+ if "--make-deps" in argv:
+ make_deps = True
+ argv = [a for a in argv if a != "--make-deps"]
+
+ paths = []
+ for a in argv:
+ if any(ch in a for ch in ['*', '?', '[']):
+ paths.extend(glob.glob(a))
+ else:
+ paths.append(a)
+
+ seen = set()
+ unique_paths = []
+ for p in paths:
+ if p not in seen:
+ seen.add(p)
+ unique_paths.append(p)
+
+ if make_deps:
+ sys.stdout.write(generate_make_deps(unique_paths))
+ else:
+ result = build_dependency_order(unique_paths)
+ sys.stdout.write("%s\n" % (" ".join(result)))
+
+if __name__ == '__main__':
+ main()