Skip to content

There is no cross-platform way to use include_*! #75075

@Shnatsel

Description

@Shnatsel
Member

Build scripts place their output in a folder created by Cargo. The path to it is passed in the environment variable OUT_DIR both to the build script and the main code. Since the file is placed in the directory, we need to add the path separator and then the name of the file. There seems to be no way to call include_bytes! with a platform-agnostic path separator, inserting / or \ depending on the host OS.

This works but is not portable: include_bytes!(concat!(env!("OUT_DIR"), "/myfile"));

This doesn't work: include_bytes!(concat!(env!("OUT_DIR"), std::path::MAIN_SEPARATOR, "myfile")); because MAIN_SEPARATOR is not a literal, and concat! only eats literals.

I've tried to to assemble a String in const fn, but that doesn't work either because String::push() requires a mutable borrow which are unsable in const fn.

#[cfg(unix)] and #[cfg(windows)] sort of work, but break on cross-compilation because these cfg attributes look at the target OS, and we need to add a separator that works on the host OS.

It's weird that this is either impossible to accomplish or the way to do so is very obscure. I'd expect this to be a relatively common operation.

rustc --version --verbose:

rustc 1.46.0-beta.2 (6f959902b 2020-07-23)
binary: rustc
commit-hash: 6f959902b3103c49ca981fbc01871589c3498489
commit-date: 2020-07-23
host: x86_64-unknown-linux-gnu
release: 1.46.0-beta.2
LLVM version: 10.0

Activity

retep998

retep998 commented on Aug 2, 2020

@retep998
Member

Can you use a build script to use cargo:rustc-env to set your own environment variable that contains the full path using the host specific path separators and whatnot?

Shnatsel

Shnatsel commented on Aug 2, 2020

@Shnatsel
MemberAuthor

I'll try it. Thanks for pointing this out!

rodrimati1992

rodrimati1992 commented on Aug 2, 2020

@rodrimati1992
Contributor

You can use cfg-ed macros to pass conditional string literals to concat!
(I can't say if this code specifically is good enough for cross-platform support)

#[cfg(not(windows))]
macro_rules! main_separator{
    ()=>{"/"}
}

#[cfg(windows)]
macro_rules! main_separator{
    ()=>{r#"\"#}
}

fn main(){
    println!("{}", include_str!(concat!(".", main_separator!(), "main.rs" )));
}
mati865

mati865 commented on Aug 2, 2020

@mati865
Member

/ should work on Windows, if it doesn't then that is the issue.

Shnatsel

Shnatsel commented on Aug 2, 2020

@Shnatsel
MemberAuthor

The build.rs trick worked, thanks!

You can use cfg-ed macros to pass conditional string literals to concat!

I did that, but that only look at the target platform and should break when the host platform is different (i.e. when cross-compiling).

/ should work on Windows, if it doesn't then that is the issue.

include_bytes! is explicitly documented to use platform-dependent separators. The documentation doesn't say anything about / working on Windows. If include_bytes! allows / as the cross-platform separator, it would be great to document that.

changed the title [-]No way platform-independent way to `include_bytes!` data generated by a build script[/-] [+]The way to use `include_bytes!` in a cross-platform way is not discoverable[/+] on Aug 2, 2020
retep998

retep998 commented on Aug 2, 2020

@retep998
Member

/ should work on Windows, if it doesn't then that is the issue.

On Windows if OUT_DIR for whatever reason uses \\?\ paths then using / as the separator will break. While it is nice to support / in paths as much as we can, we should be pushing to ensure code always uses the correct separator for the platform.

mati865

mati865 commented on Aug 2, 2020

@mati865
Member

Oh that's a bummer, I thought the same parsing here would apply as for std::path::Path which allows to use both separators on Windows.

retep998

retep998 commented on Aug 2, 2020

@retep998
Member

Well it is the same parsing. Concatenating foo/bar onto a Path that starts with \\?\ on Windows also runs into the exact same issue.

CryZe

CryZe commented on Aug 2, 2020

@CryZe
Contributor

Would it maybe make sense for the include_*! macros to allow syntax like include_bytes!(env!("OUT_DIR") / "myfile")?

danielhenrymantilla

danielhenrymantilla commented on Aug 10, 2020

@danielhenrymantilla
Contributor

I did that, but that only look at the target platform and should break when the host platform is different (i.e. when cross-compiling).

//! `build.rs`

#[cfg(windows)]
const HOST_FAMILY: &str = "windows";

#[cfg(unix)]
const HOST_FAMILY: &str = "unix";

fn main ()
{
    #[cfg(any(windows, unix))] {
        println!("cargo:rust-cfg=host_family={}", HOST_FAMILY);
    }
}
  • (Hopefully this ends up being an official cfg)

and then:

#[cfg(host_family = "windows")]
macro_rules! PATH_SEPARATOR {() => (
    r"\"
)}
#[cfg(not(host_family = "windows"))]
macro_rules! PATH_SEPARATOR {() => (
    r"/"
)}

include_bytes!(concat!(
    env!("OUT_DIR"), PATH_SEPARATOR!(), "my_file"
));
changed the title [-]The way to use `include_bytes!` in a cross-platform way is not discoverable[/-] [+]There is no cross-platform way to use `include_bytes!`[/+] on Aug 11, 2020
changed the title [-]There is no cross-platform way to use `include_bytes!`[/-] [+]There is no cross-platform way to use `include_*!`[/+] on Oct 20, 2020

23 remaining items

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-crossArea: Cross compilationT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.T-langRelevant to the language team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @briansmith@ssokolow@Shnatsel@therealbnut@retep998

        Issue actions

          There is no cross-platform way to use `include_*!` · Issue #75075 · rust-lang/rust