Files
JuliaFEM.jl/scripts/check_layer_contract.jl
T
Jukka Aho d647a565f7 feat(scripts): add check_layer_contract.jl
scripts/check_layer_contract.jl | 127 ++++++++++++++++++++++++++++++++++++++++  1 file changed, 127 insertions(+)
2026-05-09 16:30:28 +03:00

128 lines
3.4 KiB
Julia
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env julia
#
# Static audit for docs/src/developer/architecture_layers.md (layers AC).
# Run from anywhere:
# julia scripts/check_layer_contract.jl [REPO_ROOT]
#
# Uses only Base — no --project required.
const REPO_ROOT = isempty(ARGS) ? dirname(dirname(@__FILE__)) : abspath(ARGS[1])
const INCLUDE_IO_REGEX = r"include\s*\(\s*\"[^\"]*\bio/"
function jl_files(dir::AbstractString)
list = String[]
isdir(dir) || return list
for (root, _, files) in walkdir(dir)
for f in files
endswith(f, ".jl") || continue
push!(list, joinpath(root, f))
end
end
sort!(list)
end
function code_fragment(line::AbstractString)::SubString{String}
# Ignore trailing `# …` so notes do not false-positive the auditors.
parts = split(line, '#'; limit = 2)
return rstrip(parts[1])
end
function skip_line(line::AbstractString)::Bool
s = strip(line)
isempty(s) && return true
startswith(s, '#') && return true
return false
end
const DOMAIN_FORBIDDEN_SUBSTRINGS = [
"GmshReader",
"read_gmsh_mesh",
"GmshMesh",
"JuliaFEM.Legacy",
"KernelAbstractions",
"CUDABackend",
"MetalBackend",
"AMDGPUBackend",
"oneAPIBackend",
]
const FOUNDATION_EXTRA_SUBSTRINGS = [
"DOFHandler",
"create_structured_box_mesh",
]
function violations_for_line(line::AbstractString, extra_substrings)::Vector{String}
hits = String[]
if occursin(INCLUDE_IO_REGEX, line)
push!(hits, "include(\"…io/…\")")
end
for tok in DOMAIN_FORBIDDEN_SUBSTRINGS
if occursin(tok, line)
push!(hits, tok)
end
end
for tok in extra_substrings
if occursin(tok, line)
push!(hits, tok)
end
end
return hits
end
function audit_tree(label::AbstractString, dir::AbstractString, extra_substrings)::Bool
ok = true
rel = relpath(dir, REPO_ROOT)
for path in jl_files(dir)
lineno = 0
for line in eachline(path)
lineno += 1
skip_line(line) && continue
frag = code_fragment(line)
isempty(strip(frag)) && continue
hits = violations_for_line(frag, extra_substrings)
isempty(hits) && continue
ok = false
relfile = relpath(path, REPO_ROOT)
println(stderr, "$relfile:$lineno: [layer contract] $(join(sort!(unique(hits)), ", "))")
end
end
ok || println(stderr, "($label) violations under $rel")
return ok
end
function main()::Int
src = joinpath(REPO_ROOT, "src")
isdir(src) || error("expected src/ under $REPO_ROOT")
println("Layer contract audit (see docs/src/developer/architecture_layers.md)")
println("Repository: ", REPO_ROOT)
all_ok = true
domains = joinpath(src, "domains")
all_ok &= audit_tree("Layer C (domains)", domains, String[])
foundation_dirs = [
joinpath(src, "topology"),
joinpath(src, "quadrature"),
joinpath(src, "geometry"),
joinpath(src, "basis"),
joinpath(src, "sparse"),
]
for d in foundation_dirs
all_ok &= audit_tree("Layer A (foundation)", d, FOUNDATION_EXTRA_SUBSTRINGS)
end
if all_ok
println("OK — no forbidden coupling patterns found.")
return 0
else
println(stderr, "")
println(stderr, "FAILED — fix imports/includes or move code to an outer layer.")
return 1
end
end
exit(main())