Following symlinks when computing default sysroot


#1

I had a problem with std crate not being found. I opened a request for help in the users list and headed over to IRC as well.

For context I installed rust 1.12 from the tarball (and tried using rustup as well) into /usr/local/ I just found with the help of some of the guys in the #rust-beginners channel that the reason std could not be found was because my sysroot as output by rustc --print sysroot was /usr/local/stow/yabar/. I also noticed that if I set the sysroot manually in the rustc command line all was fine and the std crate would have been found. With this I started scratching my head and went to dig for an answer in the rustc source code.

It turns out that if no sysroot is defined, rustc will try to find a default sysroot.

This is the function that does the job (in src/librustc/session/filesearch.rs):

pub fn get_or_default_sysroot() -> PathBuf {
    // Follow symlinks.  If the resolved path is relative, make it absolute.
    fn canonicalize(path: Option<PathBuf>) -> Option<PathBuf> {
        path.and_then(|path| {
            match fs::canonicalize(&path) {
                // See comments on this target function, but the gist is that
                // gcc chokes on verbatim paths which fs::canonicalize generates
                // so we try to avoid those kinds of paths.
                Ok(canon) => Some(rustcfs::fix_windows_verbatim_for_gcc(&canon)),
                Err(e) => bug!("failed to get realpath: {}", e),
            }
        })
    }

    match canonicalize(env::current_exe().ok()) {
        Some(mut p) => { p.pop(); p.pop(); p }
        None => bug!("can't determine value for sysroot")
    }
}

As soon as I saw the function it hit me what the problem was. I installed in my new machine firstly yabar using GNU stow. Since /usr/local/bin didn’t exist, stow installed yabar to /usr/local/stow/yabar and symlinked /usr/local/bin to /usr/local/stow/yabar/bin. When I installed rust, the executables were installed to /usr/local/bin. When this function computes sysroot, it follows the symlinks and determines that rustc resides in /usr/local/stow/yabar/bin/rustc. And then returns as sysroot /usr/local/stow/yabar. This is obviously wrong since sysroot should be /usr/local/. Now, if the function hadn’t followed symlinks I would have had no error at all.

So, my question is, what is the reason for following symlinks when determining the sysroot?


#2

I use stow as well, it’s awesome. I think rust was right here, when the executables were instaleld in /usr/local/bin, they effectively landed in /usr/local/stow/yabar/bin, so rust was right to determine that as the location of rustc, and therefore the sysroot. Following symlinks probably wasn’t involved…

What you should do is make stow not symlink /usr/local/bin to /usr/local/stow/yabar/bin, but have it make symlinks in /usr/local/bin that point to targets that live in /usr/local/stow/yabar/bin. That would result in rustc being installed in to /usr/local/bin, and therefore a correct path.


#3

While I understand with stow I still don’t agree it has nothing to do with symlinks. which rustc reported it at /usr/local/bin so env::current_exe() would have been /usr/local/bin/rustc however the symlink following would have then transformed this into /usr/local/stow/yabar/bin/rustc.

I am in no way saying this is incorrect. In the end, it’s my fault due to my usage of stow but I am still intrigued why we are following symlinks here. What’s the use case?


#4

Ok, yeah, I see. Your PATH says that the rustc to use is the one at /usr/local/bin/rustc, nevermind that it is a symlink to /usr/local/stow/yabar/bin/rustc, and the first env args passed to rustc should actually read /usr/local/bin/rustc, not that-other-thing. So I revise my opinion and think that in this case the sysroot should have been /usr/local/bin.


#5

Whatever resolution rule rustc uses needs to work for downloaded rust snapshot tarballs, too.