-
Notifications
You must be signed in to change notification settings - Fork 4.6k
Add special feature for inter-crate safe dev-related code reuse #32169
Changes from 14 commits
41cca66
2b9940e
6be1693
0f8436c
f8dd093
94741cd
ff796e4
9bef113
b5ed325
72967f8
822114a
e86986e
4a97ca1
652df19
58013c2
fd2d6cf
6e6d189
be5a110
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
#!/usr/bin/env bash | ||
|
||
set -eo pipefail | ||
|
||
scripts/check-dev-utils.sh "$@" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
#!/usr/bin/env bash | ||
|
||
set -eo pipefail | ||
cd "$(dirname "$0")/.." | ||
source ci/_ | ||
# only nightly is used uniformly as we contain good amount of nightly-only code | ||
# (benches, frozen abi...) | ||
source ci/rust-version.sh nightly | ||
|
||
# There's a special common feature called `dev-utils` to | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we call the feature There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. actually maybe a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
well i avoided
hmm, i don't feel strong for prefixing as other existing features aren't prefixed. also, features are usually namespaced to workspace. so, even if we use
i think features themselves imply using / exporting as cargo's features are additive by design. this new feature ( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh that i think we need another word in the middle to disambiguate. will think on it some more |
||
# overcome cargo's issue: https://github.com/rust-lang/cargo/issues/8379 | ||
# This feature is like `cfg(test)`, which works between crates. | ||
# | ||
# Unfortunately, this in turn needs some special checks to avoid common | ||
# pitfalls of `dev-utils` itself. | ||
# | ||
# Firstly, detect any misuse of dev-utils as normal/build dependencies. | ||
# Also, allow some exceptions for special purpose crates. This white-listing | ||
# mechanism can be used for core-development-oriented crates like bench bins. | ||
# | ||
# Put differently, use of dev-utils is forbidden for non-dev dependencies in | ||
# general. However, allow its use for non-dev dependencies only if its use | ||
# is confined under a dep. subgraph with all nodes being marked as dev-utils. | ||
|
||
# Add your troubled package which seems to want to use `dev-utils` as normal (not | ||
# dev) dependencies, only if you're sure that there's good reason to bend | ||
# dev-util's original intention and that listed package isn't part of released | ||
# binaries. | ||
declare -r dev_utils_feature="dev-utils" | ||
declare dev_util_tainted_packages=( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. currently empty; i'll add some with a follow up pr. |
||
) | ||
|
||
# convert to comma separeted (ref: https://stackoverflow.com/a/53839433) | ||
printf -v allowed '"%s",' "${dev_util_tainted_packages[@]}" | ||
ryoqun marked this conversation as resolved.
Show resolved
Hide resolved
|
||
allowed="${allowed%,}" | ||
|
||
mode=${1:-full} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the default
t-nelson marked this conversation as resolved.
Show resolved
Hide resolved
|
||
case "$mode" in | ||
tree | check-bins | check-all-targets | full) | ||
;; | ||
*) | ||
echo "$0: unrecognized mode: $mode"; | ||
exit 1 | ||
;; | ||
esac | ||
|
||
# cargo metadata uses null for normal dep. while "dev", "build" are natually | ||
# used by other dep. kinds. | ||
declare -r normal_dependency="null"; | ||
|
||
if [[ $mode = "tree" || $mode = "full" ]]; then | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hehe, I added this set of new checks after i drafted this pr. |
||
query=$(cat <<EOF | ||
.packages | ||
| map(.name as \$crate | ||
| (.dependencies | ||
| map(select(.kind == ${normal_dependency})) | ||
t-nelson marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| map({ | ||
"crate" : \$crate, | ||
"dependency" : .name, | ||
"dependencyFeatures" : .features, | ||
}) | ||
) | ||
) | ||
| flatten | ||
| map(select( | ||
(.dependencyFeatures | ||
| index("${dev_utils_feature}") | ||
) and (.crate as \$needle | ||
| ([$allowed] | index(\$needle)) | ||
| not | ||
) | ||
)) | ||
| map([.crate, .dependency] | join(": ")) | ||
| join("\n ") | ||
EOF | ||
) | ||
|
||
abusers="$(_ cargo "+${rust_nightly}" metadata --format-version=1 | jq -r "$query")" | ||
if [[ -n "$abusers" ]]; then | ||
# Fold message for heredoc while stripping white-spaces by echo | ||
# shellcheck disable=SC2116 | ||
error="$(echo "${dev_utils_feature}" must not be used as normal dependencies, \ | ||
but is by "([crate]: [dependency])")" | ||
cat <<EOF 1>&2 | ||
$error: | ||
$abusers | ||
EOF | ||
t-nelson marked this conversation as resolved.
Show resolved
Hide resolved
|
||
exit 1 | ||
fi | ||
|
||
# Sanity-check that tainted packages has undergone the proper tedious rituals | ||
# to be justified as such. | ||
query=$(cat <<EOF | ||
.packages | ||
| map([.name, (.features | keys)] as [\$this_crate, \$this_feature] | ||
| if .name as \$needle | ([$allowed] | index(\$needle)) | ||
t-nelson marked this conversation as resolved.
Show resolved
Hide resolved
|
||
then | ||
{ | ||
"crate": \$this_crate, | ||
"crateFeatures": \$this_feature, | ||
} | ||
elif .dependencies | any( | ||
.name as \$needle | ([$allowed] | index(\$needle)) | ||
) | ||
then | ||
.dependencies | ||
| map({ | ||
"crate": \$this_crate, | ||
"crateFeatures": \$this_feature, | ||
}) | ||
else | ||
[] | ||
end) | ||
| flatten | ||
| map(select( | ||
(.crateFeatures | index("${dev_utils_feature}")) | not | ||
)) | ||
| map(.crate) | ||
| join("\n ") | ||
EOF | ||
) | ||
|
||
misconfigured_crates=$( | ||
_ cargo "+${rust_nightly}" metadata \ | ||
--format-version=1 \ | ||
| jq -r "$query" | ||
) | ||
if [[ -n "$misconfigured_crates" ]]; then | ||
# Fold message for heredoc while stripping white-spaces by echo | ||
# shellcheck disable=SC2116 | ||
error="$(echo All crates marked \`tainted\', as well as their \ | ||
dependents, MUST declare the \`${dev_utils_feature}\`. The \ | ||
following crates are in violation)" | ||
cat <<EOF 1>&2 | ||
$error: | ||
$misconfigured_crates | ||
t-nelson marked this conversation as resolved.
Show resolved
Hide resolved
|
||
EOF | ||
exit 1 | ||
fi | ||
fi | ||
|
||
# Detect possible compilation errors of problematic usage of `dev-utils`-gated code | ||
# without being explicitly declared as such in respective workspace member | ||
# `Cargo.toml`s. This cannot be detected with `--workspace --all-targets`, due | ||
# to unintentional `dev-utils` feature activation by cargo's feature | ||
# unification mechanism. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. here |
||
# So, we use `cargo hack` to exhaustively build each individual workspace | ||
# members in isolation to work around. | ||
# | ||
# 1. Check implicit usage of `dev-utils`-gated code in non-dev (= production) code by | ||
# building without dev dependencies (= tests/benches) for each crate | ||
# 2. Check implicit usage of `dev-utils`-gated code in dev (= test/benches) code by | ||
# building in isolation from other crates, which might happen to enable `dev-utils` | ||
if [[ $mode = "check-bins" || $mode = "full" ]]; then | ||
_ cargo "+${rust_nightly}" hack check --bins | ||
fi | ||
if [[ $mode = "check-all-targets" || $mode = "full" ]]; then | ||
_ cargo "+${rust_nightly}" hack check --all-targets | ||
fi |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unexpected from me? lol I've done some dense commenting work. :)