--- /dev/null
+#!/usr/bin/env bash
+#
+# Print a -fplugin-library= line that lets a downstream package use the
+# classgraph plugin WITHOUT adding `classgraph` to its build-depends. This
+# avoids the plugin's own dependency bounds (aeson, ghc, file-embed, …)
+# leaking into the consumer's build plan — useful for projects with tight
+# bounds, like ouroboros-consensus or cardano-ledger.
+#
+# Usage:
+#
+# # Print the flag (default plugin output dir = .classgraph):
+# ./classgraph-plugin-flag.sh
+#
+# # With a custom output dir:
+# ./classgraph-plugin-flag.sh --dir my-output
+#
+# # As a snippet ready to drop into cabal.project.local:
+# ./classgraph-plugin-flag.sh --cabal >> cabal.project.local
+#
+# # Per-package form, for one package only:
+# ./classgraph-plugin-flag.sh --cabal --package ouroboros-consensus
+#
+# After pasting into the consumer's cabal.project.local, run
+#
+# cabal build <pkg>
+#
+# in the consumer; it will load classgraph from the prebuilt .so and emit
+# JSON dumps under the given directory. Re-run this script (and re-paste,
+# or just re-source the line) whenever you rebuild classgraph itself —
+# the inplace-suffix in the package id is regenerated on each build.
+
+set -euo pipefail
+
+cd "$(dirname "$(readlink -f "$0")")"
+
+DIR=.classgraph
+EMIT=raw # raw | cabal
+PACKAGE=
+while [ $# -gt 0 ]; do
+ case "$1" in
+ --dir) DIR=$2; shift 2 ;;
+ --cabal) EMIT=cabal; shift ;;
+ --package) PACKAGE=$2; shift 2 ;;
+ -h|--help)
+ sed -n '/^#$/q;/^# /{s/^# \?//;p;}' "$0"
+ exit 0 ;;
+ *) echo "unknown argument: $1" >&2; exit 2 ;;
+ esac
+done
+
+# Make sure the .so exists; build silently if not.
+if [ -z "$(find dist-newstyle -type f -name 'libHSclassgraph-*.so' 2>/dev/null | head -1)" ]; then
+ cabal build --silent lib:classgraph
+fi
+
+SO=$(find dist-newstyle -type f -name 'libHSclassgraph-*.so' | head -1)
+if [ -z "$SO" ]; then
+ echo "Could not find a libHSclassgraph-*.so under dist-newstyle/." >&2
+ echo "Run \`cabal build classgraph\` first." >&2
+ exit 1
+fi
+SO=$(readlink -f "$SO")
+
+# Derive the package-id from the .so file name:
+# libHS<package-id>-ghc<version>.so
+basename=${SO##*/libHS}
+basename=${basename%.so}
+PKG_ID=${basename%-ghc*}
+
+# GHC's parser (parseExternalPluginSpec) reads the args field with
+# `readMaybe :: String -> Maybe [String]`, so it must be a Haskell list
+# literal — `["dir=.classgraph"]`, not `dir=.classgraph` or
+# `[dir=.classgraph]`.
+#
+# In the cabal-config form we wrap the args portion in cabal's `"..."`
+# token grouping and escape the inner Haskell quotes with `\"`. The
+# bytes that end up in cabal.project.local look like:
+# ;"[\"dir=DIR\"]"
+# which cabal hands to GHC as `["dir=DIR"]`.
+#
+# In the raw form we leave the `"`s as plain quotes, since users
+# typically wrap the value in single quotes when pasting into a shell.
+RAW_FLAG='-fplugin-library='"${SO};${PKG_ID};Classgraph.Plugin;[\"dir=${DIR}\"]"
+CABAL_FLAG=$(printf '%s;%s;Classgraph.Plugin;"[\\"dir=%s\\"]"' \
+ "-fplugin-library=$SO" "$PKG_ID" "$DIR")
+
+case "$EMIT" in
+ raw)
+ echo "$RAW_FLAG"
+ ;;
+ cabal)
+ if [ -n "$PACKAGE" ]; then
+ printf 'package %s\n ghc-options:\n %s\n' "$PACKAGE" "$CABAL_FLAG"
+ else
+ printf 'program-options\n ghc-options:\n %s\n' "$CABAL_FLAG"
+ fi
+ ;;
+esac