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

FoundCrate::Itself does not distinguish between crate and integration tests #10

Closed
chipsenkbeil opened this issue Apr 25, 2021 · 10 comments · Fixed by #13
Closed

FoundCrate::Itself does not distinguish between crate and integration tests #10

chipsenkbeil opened this issue Apr 25, 2021 · 10 comments · Fixed by #13

Comments

@chipsenkbeil
Copy link

chipsenkbeil commented Apr 25, 2021

I have code set up like this:

use proc_macro_crate::{crate_name, FoundCrate};
use proc_macro2::TokenStream;
use quote::quote;

pub fn root_crate() -> TokenStream {
    match crate_name("my-crate").expect("Find crate") {
        FoundCrate::Itself => quote!(crate),
        FoundCrate::Name(name) => quote!(::#name),
    }
}

This works great now to support crate when I'm within my crate and using ::my_crate for when I'm outside of my crate. The problem is that there is no way to distinguish being in the crate itself or being in the tests directory for integration tests. Because of this, the above function yields crate instead of ::my_crate for integration tests, which causes compilation errors because the integration test does not have access to the crate itself via crate but rather via ::my_crate.

Is there any plan to support distinguishing the integration tests? Really, just identifying that situation and returning FoundCrate::Name would be enough.


My first thought was to check for CARGO_BIN_EXE_<name>, which only exists for integration tests and benchmarks. If it exists and a Cargo.toml is found that matches FoundCrate::Itself, we could instead swap to the crate's actual name. Thoughts?

@bkchr
Copy link
Owner

bkchr commented Apr 25, 2021

Hmm.

I always use

extern crate self as my_crate_name;

In the lib.rs file

@chipsenkbeil
Copy link
Author

So you can refer to your crate by its name? I guess I could do that and then hardcode FoundCrate::Itself to be my crate's name, but that wouldn't line up with your README example.

e.g.

// my-crate/src/lib.rs

pub struct Something {}

extern crate self as my_crate;
// my-crate-macros/src/lib.rs

// Some macro code

fn root_crate() -> TokenStream {
    match crate_name("my-crate").expect("Find crate") {
        FoundCrate::Itself => quote!(::my_crate),
        FoundCrate::Name(name) => quote!(::#name),
    }
}
// my-crate/tests/my-integration-test.rs
use my_crate::Something;

#[test]
fn some_test() {
    // Some test code
}

@bkchr
Copy link
Owner

bkchr commented Apr 25, 2021

Yes you can refer a crate by its name.

If your proposed solution works flawlessly, I'm open to accept a pr. However, this should be testeted properly.

@chipsenkbeil
Copy link
Author

Yes you can refer a crate by its name.

If your proposed solution works flawlessly, I'm open to accept a pr. However, this should be testeted properly.

I tried it myself and the environment variable doesn't appear to be available, even when I use the compile-time macro; so, not sure if there's another way to determine.

@bkchr
Copy link
Owner

bkchr commented Apr 25, 2021

I'm not aware of any way.

@jplatte
Copy link
Contributor

jplatte commented Jun 6, 2021

I've also been using the extern crate self as original_name; trick, with

if let Ok(FoundCrate::Name(name)) = crate_name("original_name") {
    let import = format_ident!("{}", name);
    quote! { ::#import }
} else {
    quote! { ::original_name }
}

as a utility function. The only downside is that as you move things between crates in a workspace, you can accidentally have use original_name::Foo; instead of use crate::Foo; outside of macro code too.

@Shadow53
Copy link

Shadow53 commented Jun 14, 2021

Just did some testing, and it looks like CARGO_CRATE_NAME is set to the name of the integration test module/crate when compiling integration tests, and not the crate associated with the tests.

So if $CARGO_CRATE_NAME != $CARGO_PKG_NAME, could we then infer that the crate being compiled is not the primary one even when things are renamed?

@bkchr
Copy link
Owner

bkchr commented Jun 29, 2021

If that works, it sounds like a way to solve this problem.

@jplatte
Copy link
Contributor

jplatte commented Aug 12, 2021

Two observations (no time to work on this right now):

  • An integration name could have the same name as the crate, but there's also CARGO_BIN_NAME and CARGO_TARGET_TMPDIR which AFAICT would never be set when compiling a library so could be used as an additional filter that stops Itself being returned. These wouldn't even need to be read like CARGO_CRATE_NAME or CARGO_PKG_NAME, so wouldn't need extra error conditions (just env::var_os("foo").is_some()).

  • I think proc-macro-crate should also take

    [lib]
    name = "foo"

    into account if set.

@CobaltCause
Copy link
Contributor

CobaltCause commented Sep 8, 2021

As suggested, using CARGO_TARGET_TMPDIR to discern between library/binary builds vs integration test builds does indeed work. (Implementation here.)

However, it appears that this environment variable isn't set for doctests. Checking here doesn't say anything about environment variables set specifically for doctests either, so there doesn't appear to be something else we can check for that case. Is not setting CARGO_TARGET_TMPDIR for doctests a bug in cargo? Or do we need to ask them for a new environment variable?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants