rust-lang / cargo

The Rust package manager
https://doc.rust-lang.org/cargo
Apache License 2.0
12.87k stars 2.43k forks source link

Access Denied Error (os error 5) if testing while the main program is running. #12485

Open EMUNES opened 1 year ago

EMUNES commented 1 year ago

I tried these simple code. But if I run cargo run,and then run cargo test in another terminal, it always shows: error: failed to remove file 'C:\my\Tut\rust_nails\target\debug\rust_nails.exe' Caused by: Access Denied. (os error 5)

main.rs code:

use std::net::SocketAddr;

use axum::response::Html;
use axum::routing::get;
use axum::Router;

#[tokio::main]
async fn main() {
    // The hello world route service.
    let route_hello = Router::new().route(
        "/hello",
        get(|| async { Html("Hello <strong>World!!!</strong>") }),
    ).into_make_service();

    let address = SocketAddr::from(([127, 0, 0, 1], 8000));
    axum::Server::bind(&address)
        .serve(route_hello)
        .await
        .unwrap();
}

Test code:

use anyhow::{Result, Ok};

#[tokio::test]
async fn quick_dev() -> Result<()> {
    let hc = httpc_test::new_client("127.0.0.1:8000").unwrap();

    hc.do_get("/hello").await?.print().await?;

    Ok(())
}

I expected to see this happen: Test should be able to run while the program is running.

Instead, this happened: Testing code failed to execute with OS Error 5.

Meta

rustc --version --verbose:

rustc 1.71.0 (8ede3aae2 2023-07-12)
binary: rustc
commit-hash: 8ede3aae28fe6e4d52b38157d7bfe0d3bceef225
commit-date: 2023-07-12
host: x86_64-pc-windows-msvc
release: 1.71.0
LLVM version: 16.0.5
Backtrace

``` error: failed to remove file `C:\my\Tut\rust_nails\target\debug\rust_nails.exe` Caused by: Access Denied. (os error 5) ```

ChrisDenton commented 1 year ago

This issue is to do with cargo so should be moved to the cargo repo.

ehuss commented 1 year ago

Thanks for the report! I have moved this to the cargo repo.

Unfortunately I'm unable to reproduce with the given instructions. Can you please share a full project that exhibits the problem, including the Cargo.toml and any other parts that are necessary to reproduce it.

epage commented 1 year ago

I'm assuming that this is windows specific behavior where you can't overwrite a file that is in use. In this case, so long as the binary from cargo run is still running and cargo test is needing to overwrite that binary, this error will happen.

I'm assuming cargo will only overwrite the main binary if something has changed. Any other operations that are happening might be important when preparing more detailed reproduction steps.

EMUNES commented 1 year ago

Thanks for the report! I have moved this to the cargo repo.

Unfortunately I'm unable to reproduce with the given instructions. Can you please share a full project that exhibits the problem, including the Cargo.toml and any other parts that are necessary to reproduce it.

I can reproduce the problem on win11. The environment is the same as I mentioned in the issue, and here attaches the full code. demo.zip

weihanglo commented 1 year ago

I'm assuming that this is windows specific behavior where you can't overwrite a file that is in use.

If this is the case, here are two issues you may be interested in:

ehuss commented 1 year ago

Looking at the attached project, it looks like the dev-dependencies introduce new features in shared dependencies which cause the binary to be rebuilt. That is tracked in #11954. If you want to avoid those rebuilds, one of the only options is to manually specify the dependencies (with features =["…"] in the dependency) to make sure the normal builds have the same features as the dev builds. You can use cargo tree to determine which features are enabled in the two scenarios, and see what is different.

Another suggestion is to avoid the workflow of having a test expecting to have a server running via cargo run. Usually it is best to have tests launch whichever services they need themselves. That's part of the reason why cargo builds the binary for integration tests, so that they can launch them.

My only suggestion is for cargo to provide a better error message for "Access Denied" when trying to remove an executable, and provide a suggestion to check if the executable is currently running, and explain that it cannot be replaced in that scenario. I can't exactly reproduce the given error here, since on my system the linker fails first with a very different error.

EMUNES commented 1 year ago

Looking at the attached project, it looks like the dev-dependencies introduce new features in shared dependencies which cause the binary to be rebuilt. That is tracked in #11954. If you want to avoid those rebuilds, one of the only options is to manually specify the dependencies (with features =["…"] in the dependency) to make sure the normal builds have the same features as the dev builds. You can use cargo tree to determine which features are enabled in the two scenarios, and see what is different.

Another suggestion is to avoid the workflow of having a test expecting to have a server running via cargo run. Usually it is best to have tests launch whichever services they need themselves. That's part of the reason why cargo builds the binary for integration tests, so that they can launch them.

My only suggestion is for cargo to provide a better error message for "Access Denied" when trying to remove an executable, and provide a suggestion to check if the executable is currently running, and explain that it cannot be replaced in that scenario. I can't exactly reproduce the given error here, since on my system the linker fails first with a very different error.

Thanks! I will try those methods and waiting for better error messages from cargo.

yanlong-li commented 12 months ago

I encountered the same issue. A workaround is to specify a different directory from the default target using the --target-dir parameter, for example --target-dir="target/test", to prevent the conflict.

dsherret commented 1 month ago

Following up from https://github.com/rust-lang/cargo/issues/14757#issuecomment-2450438279 where I suggested killing the process.

Considering we don't know the users intent and people can hit this with "unrelated" commands like cargo run and cargo test, I would be disinclined for us to kill the process.

That makes sense. Maybe it would be nice to prompt for X seconds asking if the user wants to kill the process if it's executing in a tty? It would be a nice quality of life improvement. Running into this can sometimes cost me a few minutes of time if doing a release build.