summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Gamari <ben@smart-cactus.org>2019-07-01 11:52:47 -0400
committerBen Gamari <ben@smart-cactus.org>2019-07-01 12:05:07 -0400
commit29d297b540486bb4ea0127e7f61c8faf76f60267 (patch)
tree7f6d7ec4966c53e334aa7bb67dab7099ff342975
parentcc2f96d263e4f35e7d9132db3e6ed9727fb2a45b (diff)
downloadhaskell-29d297b540486bb4ea0127e7f61c8faf76f60267.tar.gz
Initial commit of dump-interfaces
This is the initial commit of a utility that I have been using to compare the user-facing interfaces exposed by GHC across releases
-rw-r--r--utils/dump-interfaces.py68
1 files changed, 68 insertions, 0 deletions
diff --git a/utils/dump-interfaces.py b/utils/dump-interfaces.py
new file mode 100644
index 0000000000..c5888ea286
--- /dev/null
+++ b/utils/dump-interfaces.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python
+
+"""
+This is a handy utility for comparing the interfaces exposed by the core
+library included in GHC's global package database. Given a compiler and a set
+of packages it will produce a directory containing dumps of the :browse output
+from each of the packages' exposed modules. These directories can be compared
+with, e.g., meld with a reasonable number of false differences.
+"""
+
+from pathlib import Path
+import subprocess
+from typing import TextIO, Set
+import re
+
+CORE_PACKAGES = [
+ "base",
+ "ghc-prim",
+ "template-haskell",
+ "ghc-boot",
+ "ghc-boot-th"
+]
+
+exposed_modules_re = re.compile('exposed-modules:\s*((?:(?:[A-Z][A-Za-z0-9_]*\.)*(?:[A-Z][A-Za-z0-9_]*)\s*)*)')
+
+def dump_module(out: TextIO, ghc: Path, mod: str):
+ print(f' Dumping {mod}...')
+ subprocess.run([ghc, '--interactive', '-dppr-cols=9999', '-v0'],
+ input=f':bro {mod}',
+ stdout=out,
+ encoding='UTF-8',
+ check=True)
+
+def dump_package(out_dir: Path, ghc: Path, pkg: str):
+ pkg_out = out_dir / pkg
+ pkg_out.mkdir(exist_ok=True, parents=True)
+
+ modules = get_modules(ghc, pkg)
+ print(f'Dumping {len(modules)} exposed modules from {pkg}...')
+ for mod in modules:
+ mod_out = pkg_out / f"{mod}.txt"
+ dump_module(mod_out.open('w'), ghc, mod)
+
+def get_modules(ghc: Path, pkg: str) -> Set[str]:
+ ghc_pkg = ghc.parent / "ghc-pkg"
+ out = subprocess.check_output([ghc_pkg, 'describe', pkg], encoding='UTF-8')
+ m = exposed_modules_re.search(out)
+ return set(m.group(1).split())
+
+def main() -> None:
+ import argparse
+ parser = argparse.ArgumentParser()
+ parser.add_argument('-c', '--compiler', type=Path, required=True,
+ help='GHC executable')
+ parser.add_argument('-o', '--output', type=Path, default=Path('interfaces'),
+ help='Output directory')
+ parser.add_argument('package', nargs='*', help='Packages to dump')
+ args = parser.parse_args()
+
+ packages = args.package
+ if packages == []:
+ packages = CORE_PACKAGES
+
+ for pkg in packages:
+ dump_package(args.output, args.compiler, pkg)
+
+if __name__ == "__main__":
+ main()