1#!/bin/bash
2#
3# Copyright 2023 The Fuchsia Authors
4#
5# Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
6# <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
7# license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
8# This file may not be copied, modified, or distributed except according to
9# those terms.
10
11# This script is a thin wrapper around Cargo that provides human-friendly
12# toolchain names which are automatically translated to the toolchain versions
13# we have pinned in CI.
14#
15#   cargo.sh --version <toolchain-name> # looks up the version for the named toolchain
16#   cargo.sh +<toolchain-name> [...]    # runs cargo commands with the named toolchain
17#   cargo.sh +all [...]                 # runs cargo commands with each toolchain
18#
19# The meta-toolchain "all" instructs this script to run the provided command
20# once for each toolchain (msrv, stable, nightly).
21#
22# A common task that is especially annoying to perform by hand is to update
23# trybuild's stderr files. Using this script:
24#
25#   TRYBUILD=overwrite ./cargo.sh +all test --workspace
26
27set -eo pipefail
28
29function print-usage-and-exit {
30  echo "Usage:"                          >&2
31  echo "  $0 --version <toolchain-name>" >&2
32  echo "  $0 +<toolchain-name> [...]"    >&2
33  echo "  $0 +all [...]"    >&2
34  exit 1
35}
36
37[[ $# -gt 0 ]] || print-usage-and-exit
38
39function pkg-meta {
40  # NOTE(#547): We set `CARGO_TARGET_DIR` here because `cargo metadata`
41  # sometimes causes the `cargo-metadata` crate to be rebuilt from source using
42  # the default toolchain. This has the effect of clobbering any existing build
43  # artifacts from whatever toolchain the user has specified (e.g., `+nightly`),
44  # causing the subsequent `cargo` invocation to rebuild unnecessarily. By
45  # specifying a separate build directory here, we ensure that this never
46  # clobbers the build artifacts used by the later `cargo` invocation.
47  CARGO_TARGET_DIR=target/cargo-sh cargo metadata --format-version 1 | jq -r ".packages[] | select(.name == \"zerocopy\").$1"
48}
49
50function lookup-version {
51  VERSION="$1"
52  case "$VERSION" in
53    msrv)
54      pkg-meta rust_version
55      ;;
56    stable)
57      pkg-meta 'metadata.ci."pinned-stable"'
58      ;;
59    nightly)
60      pkg-meta 'metadata.ci."pinned-nightly"'
61      ;;
62    *)
63      echo "Unrecognized toolchain name: '$VERSION' (options are 'msrv', 'stable', 'nightly')" >&2
64      return 1
65      ;;
66  esac
67}
68
69function get-rustflags {
70  [ "$1" == nightly ] && echo "--cfg __INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS"
71}
72
73function prompt {
74  PROMPT="$1"
75  YES="$2"
76  while true; do
77    read -p "$PROMPT " yn
78    case "$yn" in
79      [Yy]) $YES; return $?; ;;
80      [Nn])       return 1;  ;;
81      *)          break;     ;;
82    esac
83  done
84}
85
86case "$1" in
87  # cargo.sh --version <toolchain-name>
88  --version)
89    [[ $# -eq 2 ]] || print-usage-and-exit
90    lookup-version "$2"
91    ;;
92  # cargo.sh +all [...]
93  +all)
94    echo "[cargo.sh] warning: running the same command for each toolchain (msrv, stable, nightly)" >&2
95    for toolchain in msrv stable nightly; do
96      echo "[cargo.sh] running with toolchain: $toolchain" >&2
97      $0 "+$toolchain" ${@:2}
98    done
99    exit 0
100    ;;
101  # cargo.sh +<toolchain-name> [...]
102  +*)
103    TOOLCHAIN="$(lookup-version ${1:1})"
104
105    cargo "+$TOOLCHAIN" version &>/dev/null && \
106    rustup "+$TOOLCHAIN" component list | grep '^rust-src (installed)$' >/dev/null || {
107      echo "[cargo.sh] missing either toolchain '$TOOLCHAIN' or component 'rust-src'" >&2
108      # If we're running in a GitHub action, then it's better to bail than to
109      # hang waiting for input we're never going to get.
110      [ -z ${GITHUB_RUN_ID+x} ] || exit 1
111      prompt "[cargo.sh] would you like to install toolchain '$TOOLCHAIN' and component 'rust-src' via 'rustup'?" \
112        "rustup toolchain install $TOOLCHAIN -c rust-src"
113    } || exit 1
114
115    RUSTFLAGS="$(get-rustflags ${1:1}) $RUSTFLAGS" cargo "+$TOOLCHAIN" ${@:2}
116    ;;
117  *)
118    print-usage-and-exit
119    ;;
120esac
121