decompctx: Automatic includes from cflags, respect #pragma once

Resolves #23, #43
This commit is contained in:
Luke Street 2024-11-23 18:24:44 -07:00
parent 065fc7b715
commit d34e7c95db
2 changed files with 60 additions and 33 deletions

View File

@ -18,58 +18,63 @@ from typing import List
script_dir = os.path.dirname(os.path.realpath(__file__)) script_dir = os.path.dirname(os.path.realpath(__file__))
root_dir = os.path.abspath(os.path.join(script_dir, "..")) root_dir = os.path.abspath(os.path.join(script_dir, ".."))
src_dir = os.path.join(root_dir, "src") src_dir = os.path.join(root_dir, "src")
include_dirs = [ include_dirs: List[str] = [] # Set with -I flag
os.path.join(root_dir, "include"),
# Add additional include directories here
]
include_pattern = re.compile(r'^#\s*include\s*[<"](.+?)[>"]') include_pattern = re.compile(r'^#\s*include\s*[<"](.+?)[>"]')
guard_pattern = re.compile(r"^#\s*ifndef\s+(.*)$") guard_pattern = re.compile(r"^#\s*ifndef\s+(.*)$")
once_pattern = re.compile(r"^#\s*pragma\s+once$")
defines = set() defines = set()
deps = []
def import_h_file(in_file: str, r_path: str, deps: List[str]) -> str: def import_h_file(in_file: str, r_path: str) -> str:
rel_path = os.path.join(root_dir, r_path, in_file) rel_path = os.path.join(root_dir, r_path, in_file)
if os.path.exists(rel_path): if os.path.exists(rel_path):
return import_c_file(rel_path, deps) return import_c_file(rel_path)
for include_dir in include_dirs: for include_dir in include_dirs:
inc_path = os.path.join(include_dir, in_file) inc_path = os.path.join(include_dir, in_file)
if os.path.exists(inc_path): if os.path.exists(inc_path):
return import_c_file(inc_path, deps) return import_c_file(inc_path)
else: else:
print("Failed to locate", in_file) print("Failed to locate", in_file)
return "" return ""
def import_c_file(in_file: str, deps: List[str]) -> str: def import_c_file(in_file: str) -> str:
in_file = os.path.relpath(in_file, root_dir) in_file = os.path.relpath(in_file, root_dir)
deps.append(in_file) deps.append(in_file)
out_text = "" out_text = ""
try: try:
with open(in_file, encoding="utf-8") as file: with open(in_file, encoding="utf-8") as file:
out_text += process_file(in_file, list(file), deps) out_text += process_file(in_file, list(file))
except Exception: except Exception:
with open(in_file) as file: with open(in_file) as file:
out_text += process_file(in_file, list(file), deps) out_text += process_file(in_file, list(file))
return out_text return out_text
def process_file(in_file: str, lines: List[str], deps: List[str]) -> str: def process_file(in_file: str, lines: List[str]) -> str:
out_text = "" out_text = ""
for idx, line in enumerate(lines): for idx, line in enumerate(lines):
guard_match = guard_pattern.match(line.strip())
if idx == 0: if idx == 0:
guard_match = guard_pattern.match(line.strip())
if guard_match: if guard_match:
if guard_match[1] in defines: if guard_match[1] in defines:
break break
defines.add(guard_match[1]) defines.add(guard_match[1])
else:
once_match = once_pattern.match(line.strip())
if once_match:
if in_file in defines:
break
defines.add(in_file)
print("Processing file", in_file) print("Processing file", in_file)
include_match = include_pattern.match(line.strip()) include_match = include_pattern.match(line.strip())
if include_match and not include_match[1].endswith(".s"): if include_match and not include_match[1].endswith(".s"):
out_text += f'/* "{in_file}" line {idx} "{include_match[1]}" */\n' out_text += f'/* "{in_file}" line {idx} "{include_match[1]}" */\n'
out_text += import_h_file(include_match[1], os.path.dirname(in_file), deps) out_text += import_h_file(include_match[1], os.path.dirname(in_file))
out_text += f'/* end "{include_match[1]}" */\n' out_text += f'/* end "{include_match[1]}" */\n'
else: else:
out_text += line out_text += line
@ -100,10 +105,19 @@ def main():
"--depfile", "--depfile",
help="""Dependency file""", help="""Dependency file""",
) )
parser.add_argument(
"-I",
"--include",
help="""Include directory""",
action="append",
)
args = parser.parse_args() args = parser.parse_args()
deps = [] if args.include is None:
output = import_c_file(args.c_file, deps) exit("No include directories specified")
global include_dirs
include_dirs = args.include
output = import_c_file(args.c_file)
with open(os.path.join(root_dir, args.output), "w", encoding="utf-8") as f: with open(os.path.join(root_dir, args.output), "w", encoding="utf-8") as f:
f.write(output) f.write(output)

View File

@ -176,7 +176,9 @@ class ProjectConfig:
True # Generate compile_commands.json for clangd True # Generate compile_commands.json for clangd
) )
self.extra_clang_flags: List[str] = [] # Extra flags for clangd self.extra_clang_flags: List[str] = [] # Extra flags for clangd
self.scratch_preset_id: Optional[int] = None # Default decomp.me preset ID for scratches self.scratch_preset_id: Optional[int] = (
None # Default decomp.me preset ID for scratches
)
# Progress output, progress.json and report.json config # Progress output, progress.json and report.json config
self.progress = True # Enable report.json generation and CLI progress output self.progress = True # Enable report.json generation and CLI progress output
@ -380,7 +382,7 @@ def generate_build_ninja(
decompctx = config.tools_dir / "decompctx.py" decompctx = config.tools_dir / "decompctx.py"
n.rule( n.rule(
name="decompctx", name="decompctx",
command=f"$python {decompctx} $in -o $out -d $out.d", command=f"$python {decompctx} $in -o $out -d $out.d $includes",
description="CTX $in", description="CTX $in",
depfile="$out.d", depfile="$out.d",
deps="gcc", deps="gcc",
@ -809,10 +811,8 @@ def generate_build_ninja(
else: else:
extra_cflags.insert(0, "-lang=c") extra_cflags.insert(0, "-lang=c")
cflags_str = make_flags_str(cflags) all_cflags = cflags + extra_cflags
if len(extra_cflags) > 0: cflags_str = make_flags_str(all_cflags)
extra_cflags_str = make_flags_str(extra_cflags)
cflags_str += " " + extra_cflags_str
used_compiler_versions.add(obj.options["mw_version"]) used_compiler_versions.add(obj.options["mw_version"])
# Add MWCC build rule # Add MWCC build rule
@ -836,11 +836,21 @@ def generate_build_ninja(
# Add ctx build rule # Add ctx build rule
if obj.ctx_path is not None: if obj.ctx_path is not None:
include_dirs = []
for flag in all_cflags:
if (
flag.startswith("-i ")
or flag.startswith("-I ")
or flag.startswith("-I+")
):
include_dirs.append(flag[3:])
includes = " ".join([f"-I {d}" for d in include_dirs])
n.build( n.build(
outputs=obj.ctx_path, outputs=obj.ctx_path,
rule="decompctx", rule="decompctx",
inputs=src_path, inputs=src_path,
implicit=decompctx, implicit=decompctx,
variables={"includes": includes},
) )
# Add host build rule # Add host build rule
@ -1358,9 +1368,21 @@ def generate_objdiff_config(
unit_config["base_path"] = obj.src_obj_path unit_config["base_path"] = obj.src_obj_path
unit_config["metadata"]["source_path"] = obj.src_path unit_config["metadata"]["source_path"] = obj.src_path
cflags = obj.options["cflags"] # Filter out include directories
def keep_flag(flag):
return (
not flag.startswith("-i ")
and not flag.startswith("-i-")
and not flag.startswith("-I ")
and not flag.startswith("-I+")
and not flag.startswith("-I-")
)
all_cflags = list(
filter(keep_flag, obj.options["cflags"] + obj.options["extra_cflags"])
)
reverse_fn_order = False reverse_fn_order = False
for flag in cflags: for flag in all_cflags:
if not flag.startswith("-inline "): if not flag.startswith("-inline "):
continue continue
for value in flag.split(" ")[1].split(","): for value in flag.split(" ")[1].split(","):
@ -1369,20 +1391,11 @@ def generate_objdiff_config(
elif value == "nodeferred": elif value == "nodeferred":
reverse_fn_order = False reverse_fn_order = False
# Filter out include directories
def keep_flag(flag):
return not flag.startswith("-i ") and not flag.startswith("-I ")
cflags = list(filter(keep_flag, cflags))
compiler_version = COMPILER_MAP.get(obj.options["mw_version"]) compiler_version = COMPILER_MAP.get(obj.options["mw_version"])
if compiler_version is None: if compiler_version is None:
print(f"Missing scratch compiler mapping for {obj.options['mw_version']}") print(f"Missing scratch compiler mapping for {obj.options['mw_version']}")
else: else:
cflags_str = make_flags_str(cflags) cflags_str = make_flags_str(all_cflags)
if len(obj.options["extra_cflags"]) > 0:
extra_cflags_str = make_flags_str(obj.options["extra_cflags"])
cflags_str += " " + extra_cflags_str
unit_config["scratch"] = { unit_config["scratch"] = {
"platform": "gc_wii", "platform": "gc_wii",
"compiler": compiler_version, "compiler": compiler_version,