/ maint / cbindgen
cbindgen
  1  #!/usr/bin/env bash
  2  #
  3  # Run cbindgen in each appropriate crate (the ones with a ffi interface),
  4  # and inspect the output for changes.
  5  #
  6  # This script will, by default, replace the headers
  7  # if the warnings have not changed.
  8  # You can replace the old headers _and_ the old saved warnings
  9  # with the  "--force" option.
 10  # You can use "--check" to make any changes (to headers or warnings) a failure.
 11  #
 12  # Our cbindgen configurations require nightly rust—otherwise, we can't
 13  # expand macros.  If $RUSTC_NIGHTLY is set, or if $RUSTC is nightly,
 14  # or if "rustc" is nightly, we'll use that.  Otherwise, we'll try to
 15  # ask `rustup` if there is a nightly toolchain.
 16  
 17  # TODO: Use maint/common helpers once they are merged.
 18  
 19  set -eou pipefail
 20  
 21  TOPLEVEL=$(realpath "$(dirname "$0")"/..)
 22  
 23  : "${CBINDGEN_RUSTC:=}"
 24  : "${RUSTC:=rustc}"
 25  
 26  replace=1
 27  emit_all_warnings=0
 28  fail_on_change=0
 29  overwrite_warnings=0
 30  
 31  function usage()
 32  {
 33      cat <<EOF
 34  cbindgen: Run cbindgen in appropriate crates, and inspect outputs for changes.
 35  
 36  Usage:
 37    cbindgen [opts]
 38  
 39  Options:
 40    -h: Print this message.
 41    -v: Do not suppress expected warnings.
 42  
 43    -f: Overwrite files, even if there are new warnings.
 44    -c: Give a nonzero exit status if any files have changed; do not overwrite.
 45  EOF
 46  }
 47  
 48  if command -v gnu-getopt >/dev/null; then
 49      # If gnu-getopt is present, the platform getopt is unlikely to be good.
 50      GETOPT=gnu-getopt
 51  else
 52      GETOPT=getopt
 53  fi
 54  
 55  set +e
 56  options=$("$GETOPT" -ohfvc --long help,force,verbose,check -- "$@")
 57  status="$?"
 58  set -e
 59  if test "$status" != 0; then
 60      usage
 61      exit 1
 62  fi
 63  
 64  eval set -- "$options"
 65  
 66  while true; do
 67      case "$1" in
 68  	-h | --help)
 69  	    usage
 70  	    exit 1
 71  	    ;;
 72  	-f | --force)
 73  	    overwrite_warnings=1
 74  	    shift
 75  	    ;;
 76  	-v | --verbose)
 77  	    emit_all_warnings=1
 78  	    shift
 79  	    ;;
 80  	-c | --check)
 81  	    fail_on_change=1
 82  	    replace=0
 83  	    shift
 84  	    ;;
 85  	--)
 86  	    shift
 87  	    break
 88  	    ;;
 89      esac
 90  done
 91  
 92  # First, find a suitable rustc.
 93  if test "$CBINDGEN_RUSTC" != ""; then
 94      # Supposedly we have one. Use that.
 95      :
 96  elif $RUSTC --version |grep -q nightly; then
 97      # Our rustc is apparently nightly. That'll do.
 98      CBINDGEN_RUSTC="$RUSTC"
 99  else
100      # Maybe rustup can help us?
101      #
102      # TODO: If we start to see differences depending on the version
103      # of nightly, we may want to change this to instead look at a
104      # particular version of nightly.
105      : "${CBINDGEN_TOOLCHAIN:=nightly}"
106      CBINDGEN_RUSTC=$(rustup "+$CBINDGEN_TOOLCHAIN" which rustc)
107  fi
108  
109  ANY_DIFFERENCES=0
110  SUGGEST_FORCE=0
111  
112  for cratedir in $("$TOPLEVEL/maint/list_crates" --subdir); do
113      crate=$(basename "$cratedir")
114      if ! test -e "$cratedir/cbindgen.toml"; then
115  	# no cbindgen.toml here; we don't run in this crate.
116  	continue
117      fi
118  
119      echo "$crate ..."
120      pushd "$TOPLEVEL/$cratedir">/dev/null
121  
122      set +e
123      RUSTC="${CBINDGEN_RUSTC}" cbindgen --lockfile ../../Cargo.lock >"$crate.h.tmp" 2>cbindgen.warnings.tmp
124      status="$?"
125      set -e
126      if test "$emit_all_warnings" = 1 || test "$status" != 0; then
127  	cat cbindgen.warnings.tmp 1>&2
128  	if "$status" != 0; then
129  	    echo "cbindgen failed with status $status: See warnings above."
130  	    exit "$status"
131  	fi
132      fi
133      echo "/* Generated by $(cbindgen --version) */" >>"$crate.h.tmp"
134  
135      changed_this_time=0
136      new_warnings_this_time=0
137      if diff -uN "$crate.h" "$crate.h.tmp"; then
138  	echo "  (No changes in the header)"
139      else
140  	changed_this_time=1
141  	ANY_DIFFERENCES=1
142      fi
143      if diff -uN "cbindgen.warnings" "cbindgen.warnings.tmp"; then
144  	echo "  (No changes in the warnings)"
145      else
146  	new_warnings_this_time=1
147  	changed_this_time=1
148  	ANY_DIFFERENCES=1
149      fi
150  
151      if test "$changed_this_time" = 1 && test "$replace" = 1; then
152  	if test "$new_warnings_this_time" = 1 && test "$overwrite_warnings" = 0; then
153  	    # TODO: Possibly we should only refuse to overwrite when there
154  	    # are _new_ warnings; not when warnings have gone away or been
155  	    # re-ordered.
156  	    echo "   CHANGED WARNINGS; not overwriting."
157  	    SUGGEST_FORCE=1
158  	else
159  	    echo "   Overwriting warnings and header file."
160  	    mv -f cbindgen.warnings.tmp cbindgen.warnings
161  	    mv -f "$crate.h.tmp" "$crate.h"
162  	fi
163      fi
164  
165      popd>/dev/null
166  done
167  
168  if test "$ANY_DIFFERENCES" = 1; then
169      echo "  ------------------------------  "
170      echo "At least one file changed; see above for diffs."
171      if test "$replace" = 0; then
172      	echo "(Running in 'check' mode, so nothing was changed.)"
173      elif test "$SUGGEST_FORCE" = 1; then
174  	echo "(Did not overwrite files in which the warnings changed.)"
175  	echo "Make sure to look over the list of warnings, and then run with -f"
176      fi
177      if test "$fail_on_change" = 1; then
178  	exit 2
179      fi
180  fi