#!/usr/bin/env python3

# This script must be run from the root of the Nix repository.
#
# For include path hygiene, we need to put headers in a separate
# directory than sources. But during development, it is nice to paths
# that are similar for headers and source files, e.g.
# `foo/bar/baz.{cc,hh}`, e.g. for less typing when opening one file, and
# then opening the other file.
#
# This script symlinks the headers next to the source files to
# facilitate such a development workflows. It also updates
# `.git/info/exclude` so that the symlinks are not accidentally committed
# by mistake.

from pathlib import Path
import subprocess
import os


def main() -> None:
    # Path to the source directory
    GIT_TOPLEVEL = Path(
        subprocess.run(
            ["git", "rev-parse", "--show-toplevel"],
            text=True,
            stdout=subprocess.PIPE,
            check=True,
        ).stdout.strip()
    )

    # Get header files from git
    result = subprocess.run(
        ["git", "-C", str(GIT_TOPLEVEL), "ls-files", "*/include/nix/**.hh"],
        text=True,
        stdout=subprocess.PIPE,
        check=True,
    )
    header_files = result.stdout.strip().split("\n")
    header_files.sort()

    links = []
    for file_str in header_files:
        project_str, header_str = file_str.split("/include/nix/", 1)
        project = Path(project_str)
        header = Path(header_str)

        # Reconstruct the full path (relative to SRC_DIR) to the header file.
        file = project / "include" / "nix" / header

        # The symlink should be created at "project/header", i.e. next to the project's sources.
        link = project / header

        # Compute a relative path from the symlink's parent directory to the actual header file.
        relative_source = os.path.relpath(
            GIT_TOPLEVEL / file, GIT_TOPLEVEL / link.parent
        )

        # Create the symbolic link.
        full_link_path = GIT_TOPLEVEL / link
        full_link_path.parent.mkdir(parents=True, exist_ok=True)
        if full_link_path.is_symlink():
            full_link_path.unlink()
        full_link_path.symlink_to(relative_source)
        links.append(link)

    # Generate .gitignore file
    gitignore_path = GIT_TOPLEVEL / ".git" / "info" / "exclude"
    gitignore_path.parent.mkdir(parents=True, exist_ok=True)
    with gitignore_path.open("w") as gitignore:
        gitignore.write("# DO NOT EDIT! Autogenerated\n")
        gitignore.write(
            "# Symlinks for headers to be next to sources for development\n"
        )
        gitignore.write('# Run "maintainers/link-headers" to regenerate\n\n')
        gitignore.write('# Run "maintainers/link-headers" to regenerate\n\n')

        for link in links:
            gitignore.write(f"/{link}\n")


if __name__ == "__main__":
    main()
