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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  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. source scripts/agave-build-lists.sh
  29. # convert to comma separeted (ref: https://stackoverflow.com/a/53839433)
  30. printf -v allowed '"%s",' "${DCOU_TAINTED_PACKAGES[@]}"
  31. allowed="${allowed%,}"
  32. mode=${1:-full}
  33. # consume the mode, so that other arguments are forwarded to cargo-hack
  34. shift
  35. case "$mode" in
  36. tree | check-bins-and-lib | check-all-targets | full)
  37. ;;
  38. *)
  39. echo "$0: unrecognized mode: $mode";
  40. exit 1
  41. ;;
  42. esac
  43. if [[ $mode = "tree" || $mode = "full" ]]; then
  44. query=$(cat <<EOF
  45. .packages
  46. | map(.name as \$crate
  47. | (.dependencies
  48. | map(select((.kind // "normal") == "normal"))
  49. | map({
  50. "crate" : \$crate,
  51. "dependency" : .name,
  52. "dependencyFeatures" : .features,
  53. })
  54. )
  55. )
  56. | flatten
  57. | map(select(
  58. (.dependencyFeatures
  59. | index("dev-context-only-utils")
  60. ) and (.crate as \$needle
  61. | ([$allowed] | index(\$needle))
  62. | not
  63. )
  64. ))
  65. | map([.crate, .dependency] | join(": "))
  66. | join("\n ")
  67. EOF
  68. )
  69. abusers="$(_ cargo "+${rust_nightly}" metadata --format-version=1 |
  70. jq -r "$query")"
  71. if [[ -n "$abusers" ]]; then
  72. cat <<EOF 1>&2
  73. \`dev-context-only-utils\` must not be used as normal dependencies, but is by \
  74. "([crate]: [dependency])":
  75. $abusers
  76. EOF
  77. exit 1
  78. fi
  79. # Sanity-check that tainted packages has undergone the proper tedious rituals
  80. # to be justified as such.
  81. query=$(cat <<EOF
  82. .packages
  83. | map([.name, (.features | keys)] as [\$this_crate, \$this_feature]
  84. | if .name as \$needle | ([$allowed] | index(\$needle))
  85. then
  86. {
  87. "crate": \$this_crate,
  88. "crateFeatures": \$this_feature,
  89. }
  90. elif .dependencies | any(
  91. .name as \$needle | ([$allowed] | index(\$needle))
  92. )
  93. then
  94. .dependencies
  95. | map({
  96. "crate": \$this_crate,
  97. "crateFeatures": \$this_feature,
  98. })
  99. else
  100. []
  101. end)
  102. | flatten
  103. | map(select(
  104. (.crateFeatures | index("dev-context-only-utils")) | not
  105. ))
  106. | map(.crate)
  107. | join("\n ")
  108. EOF
  109. )
  110. misconfigured_crates=$(
  111. _ cargo "+${rust_nightly}" metadata \
  112. --format-version=1 \
  113. | jq -r "$query"
  114. )
  115. if [[ -n "$misconfigured_crates" ]]; then
  116. cat <<EOF 1>&2
  117. All crates marked \`tainted\`, as well as their dependents, MUST declare the \
  118. \`dev-context-only-utils\`. The following crates are in violation:
  119. $misconfigured_crates
  120. EOF
  121. exit 1
  122. fi
  123. fi
  124. # Detect possible compilation errors of problematic usage of
  125. # `dev-context-only-utils`-gated code without being explicitly declared as such
  126. # in respective workspace member `Cargo.toml`s. This cannot be detected with
  127. # `--workspace --all-targets`, due to unintentional `dev-context-only-utils`
  128. # feature activation by cargo's feature unification mechanism. So, we use
  129. # `cargo hack` to exhaustively build each individual workspace members in
  130. # isolation to work around.
  131. #
  132. # 1. Check implicit usage of `dev-context-only-utils`-gated code in non-dev (=
  133. # production) code by building without dev dependencies (= tests/benches) for
  134. # each crate
  135. # 2. Check implicit usage of `dev-context-only-utils`-gated code in dev (=
  136. # test/benches) code by building in isolation from other crates, which might
  137. # happen to enable `dev-context-only-utils`
  138. # dcou tends to newly trigger `unused_imports` and `dead_code` lints.
  139. # We could selectively deny (= `-D`) them here, however, deny all warnings for
  140. # consistency with other CI steps and for the possibility of new similar lints.
  141. export RUSTFLAGS="-D warnings -Z threads=8 $RUSTFLAGS"
  142. # As this environment value is used by the rather deep crate of our dep graph
  143. # (solana-varsion), this could invalidate significant portion of caches when
  144. # this changes just with a new tiny commit. Technically, it's possible for
  145. # CI_COMMIT to affect the outcome of compilation via build.rs, but it's
  146. # extremely unrealistic for such diverting compilation behaviors to be desired
  147. # as a sane use-case. So, just unset CI_COMMIT unconditionally to increase
  148. # cache efficiency.
  149. unset CI_COMMIT
  150. if [[ $mode = "check-bins-and-lib" || $mode = "full" ]]; then
  151. _ cargo "+${rust_nightly}" hack "$@" check
  152. fi
  153. if [[ $mode = "check-all-targets" || $mode = "full" ]]; then
  154. _ cargo "+${rust_nightly}" hack "$@" check --all-targets
  155. fi