114 lines
4.2 KiB
Bash
Executable File
114 lines
4.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
#
|
||
# Checks the layer dependency rule stated in README.md.
|
||
#
|
||
# The workspace is organized into dependency ordered layers:
|
||
#
|
||
# foundation -> physics -> orbit -> rf -> network -> ops -> sim
|
||
#
|
||
# A crate may depend on any crate in a layer to its left, but never on one to
|
||
# its right. This script reads every workspace manifest, works out which
|
||
# layer each dependency belongs to, and fails if any dependency points to the
|
||
# right.
|
||
#
|
||
# `apsis-core` and `apsis-math` are exempt as global prerequisites: they sit
|
||
# in `foundation` and every layer may use them, which the left-only rule
|
||
# already permits, so no special case is needed for them.
|
||
#
|
||
# Usage:
|
||
#
|
||
# scripts/check-layers.sh
|
||
#
|
||
# Exit status is zero when the rule holds and one otherwise.
|
||
#
|
||
# 检查 README.md 所述的分层依赖规则。
|
||
#
|
||
# 工作区组织为按依赖排序的各层:
|
||
#
|
||
# foundation -> physics -> orbit -> rf -> network -> ops -> sim
|
||
#
|
||
# 一个 crate 可以依赖其左侧层中的任何 crate,但绝不依赖其右侧的。本脚本
|
||
# 读取每个工作区清单,判定每个依赖属于哪一层,并在任何依赖指向右侧时
|
||
# 失败。
|
||
#
|
||
# `apsis-core` 与 `apsis-math` 作为全局前提被豁免:它们位于
|
||
# `foundation`,每一层都可使用它们,而仅向左的规则本已允许,故它们无需
|
||
# 特殊处理。
|
||
#
|
||
# 用法:
|
||
#
|
||
# scripts/check-layers.sh
|
||
#
|
||
# 规则成立时退出状态为零,否则为一。
|
||
|
||
set -euo pipefail
|
||
|
||
# The layer order, left to right. A dependency to a layer with a lower index
|
||
# is allowed; to a higher index is a reverse dependency and fails.
|
||
# 层的次序,从左到右。依赖到下标更低的层是允许的;到更高的下标即反向
|
||
# 依赖,会失败。
|
||
LAYERS=(foundation physics orbit rf network ops sim)
|
||
|
||
# The root of the workspace, taken from this script's location so that the
|
||
# check works from any working directory.
|
||
# 工作区的根,取自本脚本的位置,使检查可在任何工作目录下工作。
|
||
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||
cd "$root"
|
||
|
||
# Returns the index of a layer name, or an empty string when the name is not
|
||
# a layer. A crate outside these directories is not part of the layer scheme
|
||
# and is skipped rather than reported.
|
||
# 返回某层名的下标;该名不是层时返回空串。位于这些目录之外的 crate 不属于
|
||
# 分层方案,会被跳过而非报告。
|
||
layer_index() {
|
||
local name="$1"
|
||
local index=0
|
||
for layer in "${LAYERS[@]}"; do
|
||
if [ "$layer" = "$name" ]; then
|
||
echo "$index"
|
||
return 0
|
||
fi
|
||
index=$((index + 1))
|
||
done
|
||
echo ""
|
||
}
|
||
|
||
failures=0
|
||
checked=0
|
||
|
||
for manifest in $(find crates -name Cargo.toml | sort); do
|
||
directory="$(dirname "$manifest")"
|
||
# The layer directory is the second path component, as in
|
||
# `crates/<layer>/<crate>/Cargo.toml`.
|
||
# 层目录是路径的第二段,如 `crates/<layer>/<crate>/Cargo.toml`。
|
||
layer="$(echo "$directory" | cut -d/ -f2)"
|
||
source_index="$(layer_index "$layer")"
|
||
[ -z "$source_index" ] && continue
|
||
|
||
# Every `apsis-...` name in the manifest is a dependency or a package
|
||
# name. The package name is the crate itself, which resolves to the same
|
||
# layer and is skipped by the index comparison below.
|
||
# 清单中每个 `apsis-...` 名称要么是依赖,要么是包名。包名即该 crate
|
||
# 本身,它解析到同一层,会被下面的下标比较跳过。
|
||
for dependency in $(grep -oE 'apsis-[a-z-]+' "$manifest" | sort -u); do
|
||
dependency_directory="$(find crates -maxdepth 2 -type d -name "$dependency" 2>/dev/null | head -1)"
|
||
[ -z "$dependency_directory" ] && continue
|
||
dependency_layer="$(echo "$dependency_directory" | cut -d/ -f2)"
|
||
dependency_index="$(layer_index "$dependency_layer")"
|
||
[ -z "$dependency_index" ] && continue
|
||
|
||
if [ "$dependency_index" -gt "$source_index" ]; then
|
||
echo "reverse dependency: $layer -> $dependency_layer ($(basename "$directory") -> $dependency)" >&2
|
||
failures=$((failures + 1))
|
||
fi
|
||
checked=$((checked + 1))
|
||
done
|
||
done
|
||
|
||
if [ "$failures" -gt 0 ]; then
|
||
echo "layer check failed: $failures reverse dependency(ies)" >&2
|
||
exit 1
|
||
fi
|
||
|
||
echo "layer check passed: $checked dependency edge(s), none reversed"
|