llvm-project/flang/test/Semantics/test_modfile.py
Peter Klausler f7a15e0021
[flang] Use module file hashes for more checking and disambiguation (#80354)
f18's module files are Fortran with a leading header comment containing
the module file format version and a hash of the following contents.
This hash is currently used only to protect module files against
corruption and truncation.

Extend the use of these hashes to catch or avoid some error cases. When
one module file depends upon another, note its hash in additional module
file header comments. This allows the compiler to detect when the module
dependency is on a module file that has been updated. Further, it allows
the compiler to find the right module file dependency when the same
module file name appears in multiple directories on the module search
path.

The order in which module files are written, when multiple modules
appear in a source file, is such that every dependency is written before
the module(s) that depend upon it, so that their hashes are known.

A warning is emitted when a module file is not the first hit on the
module file search path.

Further work is needed to add a compiler option that emits (larger)
stand-alone module files that incorporate copies of their dependencies
rather than relying on search paths. This will be desirable for
application libraries that want to ship only "top-level" module files
without needing to include their dependencies.

Another future work item would be to admit multiple modules in the same
compilation with the same name if they have distinct hashes.
2024-03-01 13:58:36 -08:00

99 lines
3.0 KiB
Python
Executable File

#!/usr/bin/env python3
"""Compiles a source file and compares generated .mod files against expected.
Parameters:
sys.argv[1]: a source file with contains the input and expected output
sys.argv[2]: the Flang frontend driver
sys.argv[3:]: Optional arguments to the Flang frontend driver"""
import sys
import re
import os
import tempfile
import subprocess
import glob
import common as cm
from pathlib import Path
from difflib import unified_diff
cm.check_args_long(sys.argv)
srcdir = Path(sys.argv[1])
sources = list(glob.iglob(str(srcdir)))
sources = sorted(sources)
diffs = ""
flang_fc1 = cm.set_executable(sys.argv[2])
flang_fc_args = sys.argv[3:]
flang_fc1_options = "-fsyntax-only"
with tempfile.TemporaryDirectory() as tmpdir:
for src in sources:
src = Path(src).resolve()
actual = ""
expect = ""
expected_files = set()
actual_files = set()
if not src.is_file():
cm.die(src)
prev_files = set(os.listdir(tmpdir))
cmd = [flang_fc1, *flang_fc_args, flang_fc1_options, str(src)]
proc = subprocess.check_output(
cmd, stderr=subprocess.PIPE, universal_newlines=True, cwd=tmpdir
)
actual_files = set(os.listdir(tmpdir)).difference(prev_files)
# The first 3 bytes of the files are an UTF-8 BOM
with open(src, "r", encoding="utf-8", errors="strict") as f:
for line in f:
m = re.search(r"^!Expect: (.*)", line)
if m:
expected_files.add(m.group(1))
extra_files = actual_files.difference(expected_files)
if extra_files:
print(f"Unexpected .mod files produced: {extra_files}")
sys.exit(1)
for mod in expected_files:
mod = Path(tmpdir).joinpath(mod)
if not mod.is_file():
print(f"Compilation did not produce expected mod file: {mod}")
sys.exit(1)
with open(mod, "r", encoding="utf-8", errors="strict") as f:
for line in f:
if "!mod$" in line or "!need$" in line:
continue
actual += line
with open(src, "r", encoding="utf-8", errors="strict") as f:
for line in f:
if f"!Expect: {mod.name}" in line:
for line in f:
if re.match(r"^$", line):
break
m = re.sub(r"^!", "", line.lstrip())
expect += m
diffs = "\n".join(
unified_diff(
actual.replace(" ", "").split("\n"),
expect.replace(" ", "").split("\n"),
fromfile=mod.name,
tofile="Expect",
n=999999,
)
)
if diffs != "":
print(diffs)
print()
print("FAIL")
sys.exit(1)
print()
print("PASS")