diff --git a/scripts/depsort b/scripts/depsort
index 953260238b8ecdbb4a7beb8bfa16b3c9d7c11786..2293d9aa443e4f82fa05fbdd6994839f5185745c 100755
--- a/scripts/depsort
+++ b/scripts/depsort
@@ -1,3 +1,117 @@
 #!/bin/sh -e
 
-awk '{ for (f=1;f<=NF;f++) { print $(f),$1 } }' | "$(dirname $(readlink -f ${0}))"/tsort
+HERE="$(dirname $(readlink -f ${0}))";
+LIST="${HERE}/../.exclude";
+
+#---------------------------------------------------------------
+# overview
+
+##
+# Usage:
+#
+#   $ ./scripts/deplist system | ./scripts/depsort
+#
+# This script reads a list of packages (stdin) of the form:
+#
+#     pkg1 dep1 dep2 ...
+#     pkg2 ...
+#
+# and computes a topological sort, then prints to stdout.
+#
+# If the file called '$LIST' exists, it will be used as a filter
+# to exclude those packages, their dependencies, and transitive
+# dependencies as well, from the build plan.
+#
+# Entries must be one per line of the form REPO/PACKAGE:
+#
+#     user/rust
+#
+# It is possible to end up with a surprisingly small build plan
+# if a more essential package is excluded, so this mechanism
+# should only be used to temporarily avoid problematic packages
+# or known package families e.g. "all java-related".
+
+
+#---------------------------------------------------------------
+# supporting routines
+
+##
+# Appends text to the end of each line of stdin, in this case it
+# is to assist 'grep' later on.
+#
+# We strictly search for ending in space or end of line instead
+# of with the flag '-F', which is string literal, to avoid any
+# partial and undesirable matches, e.g. 'foo' in 'foo3'.
+#
+# This is a function because it is used multiple times and may
+# need to be updated in the future.
+#
+suffix ()
+{
+    sed -e 's@$@\\( \\|$\\)@g';
+}
+
+
+#---------------------------------------------------------------
+# initialization
+
+##
+# We need to create a few temporary files in order to support
+# handling transitive dependencies because it is not possible to
+# reuse 'foo' as in: 'tee >( ... > foo) | bar -f foo'.
+#
+data=$(mktemp); # stdin
+user=$(mktemp); # processed '$LIST'
+real=$(mktemp); # processed derivation of dependencies
+
+##
+# Capture stdin because it needs to be split for filtering.
+# This could in theory be done later, without 'cat', but
+# it is much easier for others to follow when it's here.
+#
+cat > "${data}";
+
+
+#---------------------------------------------------------------
+# preprocessing
+
+##
+# If there is no exclusion file, passthru.
+#
+if test -f "${LIST}"; then
+
+    ##
+    # Preprocess user input '$LIST' to include a suffix that
+    # works better with 'grep'. Write to file for multi-line
+    # support. The '!' before ' grep' is to ignore a no-match
+    # error that will otherwise cause the script to exit early.
+    #
+    suffix < "${LIST}" > "${user}";
+    ! grep -f "${user}" "${data}" \
+        | awk '{print $1}' \
+        | suffix \
+        | grep / `# ensure all lines of form: REPO/PACKAGE` \
+        > "${real}" \
+        ;
+    exclude="grep -vf ${real} ${data}";
+else
+    exclude="cat ${data}";
+fi
+
+#---------------------------------------------------------------
+# filtering
+
+##
+# Enumerate digraph nodes based on (possibly) filtered input.
+#
+# Pipe to 'tsort' for the final build plan.
+#
+${exclude} \
+    | awk '{ for (f=1;f<=NF;f++) { print $(f),$1 } }' \
+    | "${HERE}"/tsort \
+    ;
+
+#---------------------------------------------------------------
+# cleanup
+
+rm -f "${data}" "${user}" "${real}";