]> Repositorios git - classgraph.git/commitdiff
Add classgraph-plugin-flag.sh for unintrusive plugin usage
authorJavier Sagredo <[email protected]>
Mon, 4 May 2026 00:26:18 +0000 (02:26 +0200)
committerJavier Sagredo <[email protected]>
Mon, 4 May 2026 00:26:18 +0000 (02:26 +0200)
classgraph-plugin-flag.sh [new file with mode: 0755]

diff --git a/classgraph-plugin-flag.sh b/classgraph-plugin-flag.sh
new file mode 100755 (executable)
index 0000000..bf63968
--- /dev/null
@@ -0,0 +1,98 @@
+#!/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