Repository: cargo-lambda/cargo-lambda (language: python)

Issue:
Deploy doesn't work in workspace
Hey, it seems like `cargo-lambda` can't deploy things in a workspace?

tree:
```
├── Cargo.lock
├── Cargo.toml
├── crates
│   ├── app_1
│   │   ├── Cargo.toml
│   │   └── src
│   ├── app_2
│   │   ├── Cargo.toml
│   │   └── src
│   ├── app_3
│   │   ├── Cargo.toml
│   │   ├── migrations
│   │   └── src
├── src
│   ├── config.rs
│   ├── lib.rs
│   ├── notifier.rs
```

`app_2` is the only one that has a lambda and depends on `lambda_http` etc.

root Cargo.toml:
```toml
[package]
name = "project"
version = "0.1.0"
edition = "2021"

[profile.dev.package."*"]
opt-level = 2

[workspace]
members = ["crates/*"]
```

Running `cargo lambda deploy --iam-role arn:aws:iam::610581918146:role/AWS_LAMBDA_FULL_ACCESS app_2` gives me `Error: 
  × binary file for app_2 not found, use `cargo lambda build` to create it`

If I take the `app_2` and move it to another folder outside of this workspace and builds, then `cargo-lambda` is able to deploy it.


Discussion / hints:
oh, that's unfortunate. Where is the `target` directory for that function? by default, it looks into the same path where you run `cargo-lambda`, we might need to make some adjustments if the target is not there.
The `target` will be at the same level as the outer `src`.

```
├── target
│   ├── CACHEDIR.TAG
│   ├── release
│   │   ├── app_2
│   │   ├── app_2.d
│   │   ├── build
│   │   ├── deps
│   │   ├── examples
│   │   ├── incremental
│   │   ├── libapp_2.d
│   │   ├── libapp_2.rlib
│   └── x86_64-unknown-linux-gnu
│       ├── CACHEDIR.TAG
│       ├── debug
│       └── release
│           ├── app_2
│           ├── app_2.d
│           ├── build
│           ├── deps
│           ├── examples
│           ├── incremental
│           ├── libapp_2.d
│           ├── libapp_2.rlib
```
it doesn't look like that you ran `cargo lambda build`, is that correct? The `deploy` subcommand expects to find the binary under `target/lambda/BINARY_NAME/bootstrap`. That structure is generated when you run `cargo lambda build`. There are two options to solve this:

1. Move the file to that structure after running your build
2. Add a flag to `cargo lambda deploy` to pass the path to the binary to deploy

The second option will require some changes in the code, and it sounds like a useful option to add 😄 
I actally ran `cargo lambda build` and it does something but unclear what then :sweat_smile:  I have tried running it in the root as well as in `crates/app_2`. So I suppose `build` doesn't really support workspaces like this neither then?
`build` should support namespaces. At the end of the day, it just calls `cargo build` with some specific configuration options. Did you get any output from calling `build`?
This can be a bug caused by not being able to find the binary that you're compiling when we inspect the cargo metadata:

https://github.com/cargo-lambda/cargo-lambda/blob/main/crates/cargo-lambda-metadata/src/cargo/mod.rs#L33

I don't think we have a test case for a `Cargo.toml` file like yours. Can you paste the `Cargo.toml` file that you have for the `app_2` crate? I can try to reproduce it tonight.
I was able to reproduce your issue. I think there is a better default for cases like yours. However, I tested that if you run `cargo lambda build --package app_2` and then you run `cargo lambda deploy`, everything should work as expected. The key in your case is to tell cargo that you want to build the package `app_2` with `--package app_2`.
> I was able to reproduce your issue. I think there is a better default for cases like yours. However, I tested that if you run `cargo lambda build --package app_2` and then you run `cargo lambda deploy`, everything should work as expected. The key in your case is to tell cargo that you want to build the package `app_2` with `--package app_2`.

Ah ok, great! Thank you, will try with `--package` :)

Fix patch:
diff --git a/Cargo.lock b/Cargo.lock
index b4fe42df..4d803e66 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -643,6 +643,8 @@ dependencies = [
  "strum",
  "strum_macros",
  "tokio",
+ "tracing",
+ "tracing-subscriber",
  "zip",
 ]
 
@@ -1880,9 +1882,9 @@ dependencies = [
 
 [[package]]
 name = "once_cell"
-version = "1.10.0"
+version = "1.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
+checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
 
 [[package]]
 name = "opaque-debug"
@@ -3028,9 +3030,9 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6"
 
 [[package]]
 name = "tracing"
-version = "0.1.34"
+version = "0.1.35"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09"
+checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160"
 dependencies = [
  "cfg-if",
  "log",
@@ -3052,11 +3054,11 @@ dependencies = [
 
 [[package]]
 name = "tracing-core"
-version = "0.1.26"
+version = "0.1.28"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f54c8ca710e81886d498c2fd3331b56c93aa248d49de2222ad2742247c60072f"
+checksum = "7b7358be39f2f274f322d2aaed611acc57f382e8eb1e5b48cb9ae30933495ce7"
 dependencies = [
- "lazy_static",
+ "once_cell",
  "valuable",
 ]
 
diff --git a/Cargo.toml b/Cargo.toml
index 711469f7..d20017f5 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,6 +13,7 @@ members = [
 
 exclude = [
     "test/fixtures/single-binary-package",
+    "test/fixtures/mixed-workspace-package",
     "test/fixtures/missing-binary-package",
     "test/fixtures/multi-binary-package",
     "test/fixtures/workspace-package"
diff --git a/crates/cargo-lambda-build/Cargo.toml b/crates/cargo-lambda-build/Cargo.toml
index 8f1dad33..2dc34eb2 100644
--- a/crates/cargo-lambda-build/Cargo.toml
+++ b/crates/cargo-lambda-build/Cargo.toml
@@ -27,6 +27,8 @@ rustc_version = "0.4.0"
 sha2 = "0.10.2"
 strum = "0.24.0"
 strum_macros = "0.24.0"
+tracing = { version = "0.1.35", features = ["log"] }
+tracing-subscriber = { version = "0.3.11", features = ["env-filter"] }
 zip = { version = "0.6.2", features = ["bzip2", "deflate", "time"] }
 
 [dev-dependencies]
diff --git a/crates/cargo-lambda-build/src/lib.rs b/crates/cargo-lambda-build/src/lib.rs
index a0b2780c..85b4ed93 100644
--- a/crates/cargo-lambda-build/src/lib.rs
+++ b/crates/cargo-lambda-build/src/lib.rs
@@ -13,6 +13,8 @@ use std::{
 };
 use strum_macros::EnumString;
 use target_arch::TargetArch;
+use tracing::{debug, warn};
+use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
 use zip::{write::FileOptions, ZipWriter};
 
 mod toolchain;
@@ -73,6 +75,8 @@ enum OutputFormat {
 
 impl Build {
     pub async fn run(&mut self) -> Result<()> {
+        init_tracing();
+
         let rustc_meta = rustc_version::version_meta().into_diagnostic()?;
         let host_target = &rustc_meta.host;
         let release_channel = &rustc_meta.channel;
@@ -136,6 +140,7 @@ impl Build {
             .as_deref()
             .unwrap_or_else(|| Path::new("Cargo.toml"));
         let binaries = binary_targets(manifest_path)?;
+        debug!(binaries = ?binaries, "found new target binaries to build");
 
         if !self.build.bin.is_empty() {
             for name in &self.build.bin {
@@ -173,6 +178,8 @@ impl Build {
                 rust_flags += "-C target-cpu=";
                 rust_flags += target_cpu.as_str();
             }
+
+            debug!(rust_flags = ?rust_flags, "release RUSTFLAGS");
             cmd.env("RUSTFLAGS", rust_flags);
         }
 
@@ -199,9 +206,14 @@ impl Build {
             .join(rustc_target_without_glibc_version)
             .join(profile);
 
+        let mut found_binaries = false;
         for name in &binaries {
             let binary = base.join(name);
+            debug!(binary = ?binary, exists = binary.exists(), "checking function binary");
+
             if binary.exists() {
+                found_binaries = true;
+
                 let bootstrap_dir = if self.extension {
                     lambda_dir.join("extensions")
                 } else {
@@ -233,6 +245,9 @@ impl Build {
                 }
             }
         }
+        if !found_binaries {
+            warn!("no binaries found in target after build, try using the --bin or --package options to build specific binaries");
+        }
 
         Ok(())
     }
@@ -344,3 +359,14 @@ fn binary_permissions(path: &Path) -> Result<u32> {
 fn binary_permissions(_path: &Path) -> Result<u32> {
     Ok(0o755)
 }
+
+pub fn init_tracing() {
+    let fmt = tracing_subscriber::fmt::layer().without_time();
+
+    tracing_subscriber::registry()
+        .with(tracing_subscriber::EnvFilter::new(
+            std::env::var("RUST_LOG").unwrap_or_else(|_| "cargo_lambda=info".into()),
+        ))
+        .with(fmt)
+        .init();
+}
diff --git a/crates/cargo-lambda-metadata/src/cargo/mod.rs b/crates/cargo-lambda-metadata/src/cargo/mod.rs
index 25591d28..c543334d 100644
--- a/crates/cargo-lambda-metadata/src/cargo/mod.rs
+++ b/crates/cargo-lambda-metadata/src/cargo/mod.rs
@@ -147,6 +147,13 @@ mod tests {
         assert!(bins.contains("basic-lambda-2"));
     }
 
+    #[test]
+    fn test_binary_packages_with_mixed_workspace() {
+        let bins = binary_targets(fixture("mixed-workspace-package")).unwrap();
+        assert_eq!(1, bins.len());
+        assert!(bins.contains("function-crate"), "{:?}", bins);
+    }
+
     #[test]
     fn test_binary_packages_with_missing_binary_info() {
         let err = binary_targets(fixture("missing-binary-package")).unwrap_err();
diff --git a/crates/cargo-lambda-watch/src/trace.rs b/crates/cargo-lambda-watch/src/trace.rs
index e03bdc52..060f1cf2 100644
--- a/crates/cargo-lambda-watch/src/trace.rs
+++ b/crates/cargo-lambda-watch/src/trace.rs
@@ -20,12 +20,13 @@ pub(crate) fn init_tracing(print_traces: bool) {
     };
     let telemetry = tracing_opentelemetry::layer().with_tracer(tracer);
 
+    let fmt = tracing_subscriber::fmt::layer().without_time();
+
     tracing_subscriber::registry()
         .with(telemetry)
         .with(tracing_subscriber::EnvFilter::new(
-            std::env::var("RUST_LOG")
-                .unwrap_or_else(|_| "cargo_lambda=info,tower_http=info".into()),
+            std::env::var("RUST_LOG").unwrap_or_else(|_| "cargo_lambda=info".into()),
         ))
-        .with(tracing_subscriber::fmt::layer())
+        .with(fmt)
         .init();
 }


Test patch:
diff --git a/test/fixtures/mixed-workspace-package/Cargo.toml b/test/fixtures/mixed-workspace-package/Cargo.toml
new file mode 100644
index 00000000..95e30f3d
--- /dev/null
+++ b/test/fixtures/mixed-workspace-package/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "project"
+version = "0.1.0"
+edition = "2021"
+
+[workspace]
+members = ["crates/*"]
\ No newline at end of file
diff --git a/test/fixtures/mixed-workspace-package/crates/crate-1/Cargo.toml b/test/fixtures/mixed-workspace-package/crates/crate-1/Cargo.toml
new file mode 100644
index 00000000..b6a2f856
--- /dev/null
+++ b/test/fixtures/mixed-workspace-package/crates/crate-1/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "function-crate"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+lambda_runtime = "0.5.1"
+serde = "1.0.136"
+tokio = { version = "1", features = ["macros"] }
+
+[package.metadata.lambda.env]
+FOO = "BAR"
diff --git a/test/fixtures/mixed-workspace-package/crates/crate-1/src/main.rs b/test/fixtures/mixed-workspace-package/crates/crate-1/src/main.rs
new file mode 100644
index 00000000..82532746
--- /dev/null
+++ b/test/fixtures/mixed-workspace-package/crates/crate-1/src/main.rs
@@ -0,0 +1,44 @@
+use lambda_runtime::{run, service_fn, Error, LambdaEvent};
+use serde::{Deserialize, Serialize};
+
+/// This is a made-up example. Requests come into the runtime as unicode
+/// strings in json format, which can map to any structure that implements `serde::Deserialize`
+/// The runtime pays no attention to the contents of the request payload.
+#[derive(Deserialize)]
+struct Request {
+    command: String,
+}
+
+/// This is a made-up example of what a response structure may look like.
+/// There is no restriction on what it can be. The runtime requires responses
+/// to be serialized into json. The runtime pays no attention
+/// to the contents of the response payload.
+#[derive(Serialize)]
+struct Response {
+    req_id: String,
+    msg: String,
+}
+
+/// This is the main body for the function.
+/// Write your code inside it.
+/// There are some code example in the following URLs:
+/// - https://github.com/awslabs/aws-lambda-rust-runtime/tree/main/lambda-runtime/examples
+/// - https://github.com/aws-samples/serverless-rust-demo/
+async fn function_handler(event: LambdaEvent<Request>) -> Result<Response, Error> {
+    // Extract some useful info from the request
+    let command = event.payload.command;
+
+    // Prepare the response
+    let resp = Response {
+        req_id: event.context.request_id,
+        msg: format!("Command {}.", command),
+    };
+
+    // Return `Response` (it will be serialized to JSON automatically by the runtime)
+    Ok(resp)
+}
+
+#[tokio::main]
+async fn main() -> Result<(), Error> {
+    run(service_fn(function_handler)).await
+}
diff --git a/test/fixtures/mixed-workspace-package/crates/crate-2/Cargo.toml b/test/fixtures/mixed-workspace-package/crates/crate-2/Cargo.toml
new file mode 100644
index 00000000..af6847d2
--- /dev/null
+++ b/test/fixtures/mixed-workspace-package/crates/crate-2/Cargo.toml
@@ -0,0 +1,4 @@
+[package]
+name = "lib-crate"
+version = "0.1.0"
+edition = "2021"
diff --git a/test/fixtures/mixed-workspace-package/crates/crate-2/src/lib.rs b/test/fixtures/mixed-workspace-package/crates/crate-2/src/lib.rs
new file mode 100644
index 00000000..e69de29b
diff --git a/test/fixtures/mixed-workspace-package/src/lib.rs b/test/fixtures/mixed-workspace-package/src/lib.rs
new file mode 100644
index 00000000..21b1f33d
--- /dev/null
+++ b/test/fixtures/mixed-workspace-package/src/lib.rs
@@ -0,0 +1,3 @@
+pub fn hi() {
+    println!("hi");
+}