#!/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()