Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(deployer): add support for building wasm projects #437

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions cargo-shuttle/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use crossterm::style::Stylize;
use factory::LocalFactory;
use futures::StreamExt;
use shuttle_common::models::secret;
use shuttle_service::loader::{build_crate, Loader};
use shuttle_service::loader::{build_crate, Loader, Runtime};
use shuttle_service::Logger;
use tokio::sync::mpsc;
use tracing::trace;
Expand Down Expand Up @@ -277,7 +277,10 @@ impl Shuttle {
"Building".bold().green(),
working_directory.display()
);
let so_path = build_crate(id, working_directory, false, tx).await?;
let so_path = match build_crate(id, working_directory, false, false, tx).await? {
Runtime::Legacy(path) => path,
Runtime::Next(_) => todo!(),
};

trace!("loading secrets");
let secrets_path = working_directory.join("Secrets.toml");
Expand Down
12 changes: 8 additions & 4 deletions deployer/src/deployment/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use cargo_metadata::Message;
use chrono::Utc;
use crossbeam_channel::Sender;
use serde_json::json;
use shuttle_service::loader::{build_crate, get_config};
use shuttle_service::loader::{build_crate, get_config, Runtime};
use tracing::{debug, error, info, instrument, trace};
use uuid::Uuid;

Expand Down Expand Up @@ -158,7 +158,7 @@ impl Queued {
});

let project_path = project_path.canonicalize()?;
let so_path = build_deployment(self.id, &project_path, tx.clone()).await?;
let so_path = build_deployment(self.id, &project_path, false, tx.clone()).await?;

if self.will_run_tests {
info!(
Expand Down Expand Up @@ -265,13 +265,17 @@ fn extract_tar_gz_data(data: impl Read, dest: impl AsRef<Path>) -> Result<()> {
async fn build_deployment(
deployment_id: Uuid,
project_path: &Path,
wasm: bool,
tx: crossbeam_channel::Sender<Message>,
) -> Result<PathBuf> {
let so_path = build_crate(deployment_id, project_path, true, tx)
let runtime_path = build_crate(deployment_id, project_path, true, wasm, tx)
.await
.map_err(|e| Error::Build(e.into()))?;

Ok(so_path)
match runtime_path {
Runtime::Legacy(so_path) => Ok(so_path),
Runtime::Next(_) => todo!(),
}
}

#[instrument(skip(project_path, tx))]
Expand Down
32 changes: 27 additions & 5 deletions service/src/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::panic::AssertUnwindSafe;
use std::path::{Path, PathBuf};

use anyhow::{anyhow, Context};
use cargo::core::compiler::{CompileMode, MessageFormat};
use cargo::core::compiler::{CompileKind, CompileMode, CompileTarget, MessageFormat};
use cargo::core::{Manifest, PackageId, Shell, Summary, Verbosity, Workspace};
use cargo::ops::{compile, CompileOptions};
use cargo::util::interning::InternedString;
Expand Down Expand Up @@ -100,13 +100,20 @@ impl Loader {
}
}

/// How to run/build the project
pub enum Runtime {
Next(PathBuf),
Legacy(PathBuf),
}

/// Given a project directory path, builds the crate
pub async fn build_crate(
deployment_id: Uuid,
project_path: &Path,
release_mode: bool,
wasm: bool,
tx: Sender<Message>,
) -> anyhow::Result<PathBuf> {
) -> anyhow::Result<Runtime> {
let (read, write) = pipe::pipe();
let project_path = project_path.to_owned();

Expand All @@ -125,10 +132,15 @@ pub async fn build_crate(
check_version(summary)?;
check_no_panic(&ws)?;

let opts = get_compile_options(&config, release_mode)?;
let opts = get_compile_options(&config, release_mode, wasm)?;
let compilation = compile(&ws, &opts);

Ok(compilation?.cdylibs[0].path.clone())
let path = compilation?.cdylibs[0].path.clone();
Ok(if wasm {
Runtime::Next(path)
} else {
Runtime::Legacy(path)
})
});

// This needs to be on a separate thread, else deployer will block (reason currently unknown :D)
Expand Down Expand Up @@ -169,7 +181,11 @@ pub fn get_config(writer: PipeWriter) -> anyhow::Result<Config> {
}

/// Get options to compile in build mode
fn get_compile_options(config: &Config, release_mode: bool) -> anyhow::Result<CompileOptions> {
fn get_compile_options(
config: &Config,
release_mode: bool,
wasm: bool,
) -> anyhow::Result<CompileOptions> {
let mut opts = CompileOptions::new(config, CompileMode::Build)?;
opts.build_config.message_format = MessageFormat::Json {
render_diagnostics: false,
Expand All @@ -183,6 +199,12 @@ fn get_compile_options(config: &Config, release_mode: bool) -> anyhow::Result<Co
InternedString::new("dev")
};

opts.build_config.requested_kinds = vec![if wasm {
CompileKind::Target(CompileTarget::new("wasm32-unknown-unknown")?)
} else {
CompileKind::Host
}];

Ok(opts)
}

Expand Down
74 changes: 54 additions & 20 deletions service/tests/integration/build_crate.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
use std::path::{Path, PathBuf};

use shuttle_service::loader::build_crate;
use shuttle_service::loader::{build_crate, Runtime};

#[tokio::test(flavor = "multi_thread")]
async fn not_shuttle() {
let (tx, _) = crossbeam_channel::unbounded();
let project_path = format!("{}/tests/resources/not-shuttle", env!("CARGO_MANIFEST_DIR"));
let so_path = build_crate(Default::default(), Path::new(&project_path), false, tx)
.await
.unwrap();
let so_path = match build_crate(
Default::default(),
Path::new(&project_path),
false,
false,
tx,
)
.await
.unwrap()
{
Runtime::Legacy(path) => path,
_ => unreachable!(),
};

assert!(
so_path
Expand All @@ -27,20 +37,32 @@ async fn not_shuttle() {
async fn not_lib() {
let (tx, _) = crossbeam_channel::unbounded();
let project_path = format!("{}/tests/resources/not-lib", env!("CARGO_MANIFEST_DIR"));
build_crate(Default::default(), Path::new(&project_path), false, tx)
.await
.unwrap();
build_crate(
Default::default(),
Path::new(&project_path),
false,
false,
tx,
)
.await
.unwrap();
}

#[tokio::test(flavor = "multi_thread")]
async fn not_cdylib() {
let (tx, _) = crossbeam_channel::unbounded();
let project_path = format!("{}/tests/resources/not-cdylib", env!("CARGO_MANIFEST_DIR"));
assert!(
build_crate(Default::default(), Path::new(&project_path), false, tx)
.await
.is_ok()
);
assert!(matches!(
build_crate(
Default::default(),
Path::new(&project_path),
false,
false,
tx
)
.await,
Ok(Runtime::Legacy(_))
));
assert!(PathBuf::from(project_path)
.join("target/debug/libnot_cdylib.so")
.exists());
Expand All @@ -50,11 +72,17 @@ async fn not_cdylib() {
async fn is_cdylib() {
let (tx, _) = crossbeam_channel::unbounded();
let project_path = format!("{}/tests/resources/is-cdylib", env!("CARGO_MANIFEST_DIR"));
assert!(
build_crate(Default::default(), Path::new(&project_path), false, tx)
.await
.is_ok()
);
assert!(matches!(
build_crate(
Default::default(),
Path::new(&project_path),
false,
false,
tx
)
.await,
Ok(Runtime::Legacy(_))
));
assert!(PathBuf::from(project_path)
.join("target/debug/libis_cdylib.so")
.exists());
Expand All @@ -68,7 +96,13 @@ async fn not_found() {
"{}/tests/resources/non-existing",
env!("CARGO_MANIFEST_DIR")
);
build_crate(Default::default(), Path::new(&project_path), false, tx)
.await
.unwrap();
build_crate(
Default::default(),
Path::new(&project_path),
false,
false,
tx,
)
.await
.unwrap();
}