check-dev-context-only-utils.sh 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. #!/usr/bin/env bash
  2. set -eo pipefail
  3. cd "$(dirname "$0")/.."
  4. source ci/_
  5. # only nightly is used uniformly as we contain good amount of nightly-only code
  6. # (benches, frozen abi...)
  7. source ci/rust-version.sh nightly
  8. # There's a special common feature called `dev-context-only-utils` to
  9. # overcome cargo's issue: https://github.com/rust-lang/cargo/issues/8379
  10. # This feature is like `cfg(test)`, which works between crates.
  11. #
  12. # Unfortunately, this in turn needs some special checks to avoid common
  13. # pitfalls of `dev-context-only-utils` itself.
  14. #
  15. # Firstly, detect any misuse of dev-context-only-utils as normal/build
  16. # dependencies. Also, allow some exceptions for special purpose crates. This
  17. # white-listing mechanism can be used for core-development-oriented crates like
  18. # bench bins.
  19. #
  20. # Put differently, use of dev-context-only-utils is forbidden for non-dev
  21. # dependencies in general. However, allow its use for non-dev dependencies only
  22. # if its use is confined under a dep. subgraph with all nodes being marked as
  23. # dev-context-only-utils.
  24. # Add your troubled package which seems to want to use `dev-context-only-utils`
  25. # as normal (not dev) dependencies, only if you're sure that there's good
  26. # reason to bend dev-context-only-utils's original intention and that listed
  27. # package isn't part of released binaries.
  28. declare tainted_packages=(
  29. solana-accounts-bench
  30. solana-banking-bench
  31. agave-ledger-tool
  32. )
  33. # convert to comma separeted (ref: https://stackoverflow.com/a/53839433)
  34. printf -v allowed '"%s",' "${tainted_packages[@]}"
  35. allowed="${allowed%,}"
  36. mode=${1:-full}
  37. case "$mode" in
  38. tree | check-bins | check-all-targets | full)
  39. ;;
  40. *)
  41. echo "$0: unrecognized mode: $mode";
  42. exit 1
  43. ;;
  44. esac
  45. if [[ $mode = "tree" || $mode = "full" ]]; then
  46. query=$(cat <<EOF
  47. .packages
  48. | map(.name as \$crate
  49. | (.dependencies
  50. | map(select((.kind // "normal") == "normal"))
  51. | map({
  52. "crate" : \$crate,
  53. "dependency" : .name,
  54. "dependencyFeatures" : .features,
  55. })
  56. )
  57. )
  58. | flatten
  59. | map(select(
  60. (.dependencyFeatures
  61. | index("dev-context-only-utils")
  62. ) and (.crate as \$needle
  63. | ([$allowed] | index(\$needle))
  64. | not
  65. )
  66. ))
  67. | map([.crate, .dependency] | join(": "))
  68. | join("\n ")
  69. EOF
  70. )
  71. abusers="$(_ cargo "+${rust_nightly}" metadata --format-version=1 |
  72. jq -r "$query")"
  73. if [[ -n "$abusers" ]]; then
  74. cat <<EOF 1>&2
  75. \`dev-context-only-utils\` must not be used as normal dependencies, but is by \
  76. "([crate]: [dependency])":
  77. $abusers
  78. EOF
  79. exit 1
  80. fi
  81. # Sanity-check that tainted packages has undergone the proper tedious rituals
  82. # to be justified as such.
  83. query=$(cat <<EOF
  84. .packages
  85. | map([.name, (.features | keys)] as [\$this_crate, \$this_feature]
  86. | if .name as \$needle | ([$allowed] | index(\$needle))
  87. then
  88. {
  89. "crate": \$this_crate,
  90. "crateFeatures": \$this_feature,
  91. }
  92. elif .dependencies | any(
  93. .name as \$needle | ([$allowed] | index(\$needle))
  94. )
  95. then
  96. .dependencies
  97. | map({
  98. "crate": \$this_crate,
  99. "crateFeatures": \$this_feature,
  100. })
  101. else
  102. []
  103. end)
  104. | flatten
  105. | map(select(
  106. (.crateFeatures | index("dev-context-only-utils")) | not
  107. ))
  108. | map(.crate)
  109. | join("\n ")
  110. EOF
  111. )
  112. misconfigured_crates=$(
  113. _ cargo "+${rust_nightly}" metadata \
  114. --format-version=1 \
  115. | jq -r "$query"
  116. )
  117. if [[ -n "$misconfigured_crates" ]]; then
  118. cat <<EOF 1>&2
  119. All crates marked \`tainted\`, as well as their dependents, MUST declare the \
  120. \`dev-context-only-utils\`. The following crates are in violation:
  121. $misconfigured_crates
  122. EOF
  123. exit 1
  124. fi
  125. fi
  126. # Detect possible compilation errors of problematic usage of
  127. # `dev-context-only-utils`-gated code without being explicitly declared as such
  128. # in respective workspace member `Cargo.toml`s. This cannot be detected with
  129. # `--workspace --all-targets`, due to unintentional `dev-context-only-utils`
  130. # feature activation by cargo's feature unification mechanism. So, we use
  131. # `cargo hack` to exhaustively build each individual workspace members in
  132. # isolation to work around.
  133. #
  134. # 1. Check implicit usage of `dev-context-only-utils`-gated code in non-dev (=
  135. # production) code by building without dev dependencies (= tests/benches) for
  136. # each crate
  137. # 2. Check implicit usage of `dev-context-only-utils`-gated code in dev (=
  138. # test/benches) code by building in isolation from other crates, which might
  139. # happen to enable `dev-context-only-utils`
  140. export RUSTFLAGS="-Z threads=8 $RUSTFLAGS"
  141. if [[ $mode = "check-bins" || $mode = "full" ]]; then
  142. _ cargo "+${rust_nightly}" hack check --bins
  143. fi
  144. if [[ $mode = "check-all-targets" || $mode = "full" ]]; then
  145. _ cargo "+${rust_nightly}" hack check --all-targets
  146. fi