1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143#!/bin/bash
# Find all the directories we might need (based on
# heroku-buildpack-nodejs code).
BUILD_DIR=${1:-}
CACHE_DIR=${2:-}
ENV_DIR=${3:-}
BP_DIR=$(cd $(dirname ${0:-}); cd ..; pwd)
# Export DATABASE_URL at build time, mostly because Diesel is the best way to
# do SQL in Rust right now, and Diesel will use it to generate code for the
# database schema.
if [ -e "$ENV_DIR/DATABASE_URL" ]; then
export DATABASE_URL="$(cat $ENV_DIR/DATABASE_URL)";
fi
# Set defaults for our configuration variables. Stable Rust is sufficiently
# stable at this point that I think we can just default it.
VERSION=stable
# Set this to "1" in `RustConfig` to just install a Rust toolchain and not
# build `Cargo.toml`. This is useful if you have a project written in Ruby
# or Node (for example) that needs to build extension modules using Rust.
RUST_SKIP_BUILD=0
# If your Rust code is not at the root directory of the repository, specify a
# `BUILD_PATH` to the correct directory in the `RustConfig`
BUILD_PATH=""
# Set this to "1" in `RustConfig` to install diesel at build time and copy it
# into the target directory, next to your app binary. This makes it easy to
# run migrations by adding a release step to your Procfile:
# `release: ./target/release/diesel migration run`
RUST_INSTALL_DIESEL=0
# These flags are passed to `cargo install diesel`, e.g. '--no-default-features --features postgres'
DIESEL_FLAGS=""
# Default build flags to pass to `cargo build`.
RUST_CARGO_BUILD_FLAGS="--release"
# Load our toolchain configuration, if any was specified.
if [ -f "$BUILD_DIR/rust-toolchain" ]; then
VERSION="$(cat "$BUILD_DIR/rust-toolchain")"
fi
# Load our configuration variables, if any were specified.
if [ -f "$BUILD_DIR/RustConfig" ]; then
. "$BUILD_DIR/RustConfig"
fi
# Standard paranoia.
set -eu
# Check our configuration options.
if [ -z ${VERSION+x} ]; then
>&2 echo "failed: must set VERSION with rust-toolchain or RustConfig to indicate the Rust version."
exit 1
fi
# Notify users running old, unstable versions of Rust about how to deploy
# successfully.
if [ $RUST_SKIP_BUILD -ne 1 ] && ( [ ! -z ${CARGO_URL+x} ] || [ ! -f "$BUILD_DIR/$BUILD_PATH/Cargo.toml" ] ); then
>&2 cat <<EOF
To deploy a modern Rust app, make sure you have a Cargo.toml file, and that
you do not define CARGO_URL or CARGO_VERSION in RustConfig. If you're
using an older version of Rust, and you need to re-deploy an existing
application, try setting your buildpack to:
https://github.com/emk/heroku-buildpack-rust.git#old-rust
EOF
exit 1
fi
# Record our Rust build environment configuration in an export file, in
# case another buildpack needs it to build Ruby gems that use Rust or
# something like that.
cat <<EOF > $BP_DIR/export
# Our rustup installation.
export RUSTUP_HOME="$CACHE_DIR/multirust"
# Our cargo installation. We implicitly trust Rustup and Cargo
# to do the right thing when new versions are released.
export CARGO_HOME="$CACHE_DIR/cargo"
# Include binaries installed by cargo and rustup in our path.
PATH="\$CARGO_HOME/bin:\$PATH"
EOF
# Read our build environment back in and evaluate it so that we can use it.
. $BP_DIR/export
# Switch to our cache directory.
mkdir -p "$CACHE_DIR"
cd "$CACHE_DIR"
# Make sure we have an appropriate Rust toolchain installed.
if [ -d "$CARGO_HOME" ]; then
echo "-----> Checking for new releases of Rust $VERSION channel"
# It's possible that $VERSION has changed, or the `stable` channel has updated.
rustup self update
rustup update "$VERSION"
rustup default "$VERSION"
else
echo "-----> Downloading rustup"
curl https://sh.rustup.rs -sSf > rustup.sh
chmod u+x rustup.sh
echo "-----> Using rustup to install Rust channel $VERSION"
./rustup.sh -y --default-toolchain "$VERSION"
rm rustup.sh
fi
if [ ! -x "$CARGO_HOME/bin/rustc" ]; then
echo "failed: Cannot find Rust binaries at $CARGO_HOME"
exit 1
fi
# This is where we will cache our Rust output. Note the suggestions at
# https://github.com/alexcrichton/cargo/commit/014765f788ca1c2476936835ca32cc2746f99b42
# which describe how this needs to be named.
export CARGO_TARGET_DIR="$CACHE_DIR/target"
if [ $RUST_SKIP_BUILD -ne 1 ]; then
# Build our project (into CARGO_TARGET_DIR so we have caching) and copy it
# back to the source tree. In theory, we could probably just copy the
# binary or do something clever with `cargo install`, but we haven't
# figured that out yet.
#
# To debug git issues:
#export RUST_LOG="cargo::sources::git=debug"
# To debug compiler and linking issues, add `--verbose`.
echo "-----> Building application using Cargo"
cd "$BUILD_DIR/$BUILD_PATH"
rm -rf target/
cargo build $RUST_CARGO_BUILD_FLAGS
mkdir -p target/release
find "$CARGO_TARGET_DIR/release" -maxdepth 1 -type f -executable -exec cp -a -t target/release {} \;
else
echo "-----> Skipping Cargo build"
fi
# Install diesel so we can use it for migrations
if [ $RUST_INSTALL_DIESEL -eq 1 ]; then
echo "-----> Installing diesel"
cargo install diesel_cli $DIESEL_FLAGS || echo "already installed"
cp $(which diesel) target/release/
fi