diff --git a/src/librustc_trans/back/msvc/mod.rs b/src/librustc_trans/back/msvc/mod.rs index 87d3fd1c0e827..6f0baa86579e0 100644 --- a/src/librustc_trans/back/msvc/mod.rs +++ b/src/librustc_trans/back/msvc/mod.rs @@ -28,7 +28,7 @@ //! shell or MSYS shells. //! //! As a high-level note, all logic in this module for looking up various -//! paths/files is copied over from Clang in its MSVCToolChain.cpp file, but +//! paths/files is based on Microsoft's logic in their vcvars bat files, but //! comments can also be found below leading through the various code paths. use std::process::Command; @@ -42,290 +42,225 @@ pub fn link_exe_cmd(sess: &Session) -> Command { use std::env; use std::ffi::OsString; use std::fs; - use std::io; use std::path::{Path, PathBuf}; - use self::registry::{RegistryKey, LOCAL_MACHINE}; + use self::registry::{LOCAL_MACHINE}; - // When finding the link.exe binary the 32-bit version is at the top level - // but the versions to cross to other architectures are stored in - // sub-folders. Unknown architectures also just bail out early to return the - // standard `link.exe` command. - let extra = match &sess.target.target.arch[..] { - "x86" => "", - "x86_64" => "amd64", - "arm" => "arm", + let arch = &sess.target.target.arch; + let (binsub, libsub, vclibsub) = + match (bin_subdir(arch), lib_subdir(arch), vc_lib_subdir(arch)) { + (Some(x), Some(y), Some(z)) => (x, y, z), _ => return Command::new("link.exe"), }; - let vs_install_dir = get_vs_install_dir(); - - // First up, we need to find the `link.exe` binary itself, and there's a few - // locations that we can look. First up is the standard VCINSTALLDIR - // environment variable which is normally set by the vcvarsall.bat file. If - // an environment is set up manually by whomever's driving the compiler then - // we shouldn't muck with that decision and should instead respect that. + // First we need to figure out whether the environment is already correctly + // configured by vcvars. We do this by looking at the environment variable + // `VCINSTALLDIR` which is always set by vcvars, and unlikely to be set + // otherwise. If it is defined, then we derive the path to `link.exe` from + // that and trust that everything else is configured correctly. + // + // If `VCINSTALLDIR` wasn't defined (or we couldn't find the linker where it + // claimed it should be), then we resort to finding everything ourselves. + // First we find where the latest version of MSVC is installed and what + // version it is. Then based on the version we find the appropriate SDKs. + // + // For MSVC 14 (VS 2015) we look for the Win10 SDK and failing that we look + // for the Win8.1 SDK. We also look for the Universal CRT. // - // Next up is looking in PATH itself. Here we look for `cl.exe` and then - // assume that `link.exe` is next to it if we find it. Note that we look for - // `cl.exe` because MinGW ships /usr/bin/link.exe which is normally found in - // PATH but we're not interested in finding that. + // For MSVC 12 (VS 2013) we look for the Win8.1 SDK. // - // Finally we read the Windows registry to discover the VS install root. - // From here we probe for `link.exe` just to make sure that it exists. - let mut cmd = env::var_os("VCINSTALLDIR").and_then(|dir| { + // For MSVC 11 (VS 2012) we look for the Win8 SDK. + // + // For all other versions the user has to execute the appropriate vcvars bat + // file themselves to configure the environment. + // + // If despite our best efforts we are still unable to find MSVC then we just + // blindly call `link.exe` and hope for the best. + return env::var_os("VCINSTALLDIR").and_then(|dir| { + debug!("Environment already configured by user. Assuming it works."); let mut p = PathBuf::from(dir); p.push("bin"); - p.push(extra); + p.push(binsub); p.push("link.exe"); - if fs::metadata(&p).is_ok() {Some(p)} else {None} - }).or_else(|| { - env::var_os("PATH").and_then(|path| { - env::split_paths(&path).find(|path| { - fs::metadata(&path.join("cl.exe")).is_ok() - }).map(|p| { - p.join("link.exe") - }) - }) + if !p.is_file() { return None } + Some(Command::new(p)) }).or_else(|| { - vs_install_dir.as_ref().and_then(|p| { - let mut p = p.join("VC/bin"); - p.push(extra); - p.push("link.exe"); - if fs::metadata(&p).is_ok() {Some(p)} else {None} + get_vc_dir().and_then(|(ver, vcdir)| { + debug!("Found VC installation directory {:?}", vcdir); + let mut linker = vcdir.clone(); + linker.push("bin"); + linker.push(binsub); + linker.push("link.exe"); + if !linker.is_file() { return None } + let mut cmd = Command::new(linker); + add_lib(&mut cmd, &vcdir.join("lib").join(vclibsub)); + if ver == "14.0" { + if let Some(dir) = get_ucrt_dir() { + debug!("Found Universal CRT {:?}", dir); + add_lib(&mut cmd, &dir.join("ucrt").join(libsub)); + } + if let Some(dir) = get_sdk10_dir() { + debug!("Found Win10 SDK {:?}", dir); + add_lib(&mut cmd, &dir.join("um").join(libsub)); + } else if let Some(dir) = get_sdk81_dir() { + debug!("Found Win8.1 SDK {:?}", dir); + add_lib(&mut cmd, &dir.join("um").join(libsub)); + } + } else if ver == "12.0" { + if let Some(dir) = get_sdk81_dir() { + debug!("Found Win8.1 SDK {:?}", dir); + add_lib(&mut cmd, &dir.join("um").join(libsub)); + } + } else { // ver == "11.0" + if let Some(dir) = get_sdk8_dir() { + debug!("Found Win8 SDK {:?}", dir); + add_lib(&mut cmd, &dir.join("um").join(libsub)); + } + } + Some(cmd) }) - }).map(|linker| { - Command::new(linker) }).unwrap_or_else(|| { + debug!("Failed to locate linker."); Command::new("link.exe") }); - // The MSVC linker uses the LIB environment variable as the default lookup - // path for libraries. This environment variable is normally set up by the - // VS shells, so we only want to start adding our own pieces if it's not - // set. - // - // If we're adding our own pieces, then we need to add a few primary - // directories to the default search path for the linker. The first is in - // the VS install direcotry, the next is the Windows SDK directory, and the - // last is the possible UCRT installation directory. - // - // The UCRT is a recent addition to Visual Studio installs (2015 at the time - // of this writing), and it's in the normal windows SDK folder, but there - // apparently aren't registry keys pointing to it. As a result we detect the - // installation and then add it manually. This logic will probably need to - // be tweaked over time... - if env::var_os("LIB").is_none() { - if let Some(mut vs_install_dir) = vs_install_dir { - vs_install_dir.push("VC/lib"); - vs_install_dir.push(extra); - let mut arg = OsString::from("/LIBPATH:"); - arg.push(&vs_install_dir); - cmd.arg(arg); + // A convenience function to make the above code simpler + fn add_lib(cmd: &mut Command, lib: &Path) { + let mut arg: OsString = "/LIBPATH:".into(); + arg.push(lib); + cmd.arg(arg); + } - if let Some((ucrt_root, vers)) = ucrt_install_dir(&vs_install_dir) { - if let Some(arch) = windows_sdk_v8_subdir(sess) { - let mut arg = OsString::from("/LIBPATH:"); - arg.push(ucrt_root.join("Lib").join(vers) - .join("ucrt").join(arch)); - cmd.arg(arg); - } - } - } - if let Some(path) = get_windows_sdk_lib_path(sess) { - let mut arg = OsString::from("/LIBPATH:"); - arg.push(&path); - cmd.arg(arg); - } + // To find MSVC we look in a specific registry key for the newest of the + // three versions that we support. + fn get_vc_dir() -> Option<(&'static str, PathBuf)> { + LOCAL_MACHINE.open(r"SOFTWARE\Microsoft\VisualStudio\SxS\VC7".as_ref()) + .ok().and_then(|key| { + ["14.0", "12.0", "11.0"].iter().filter_map(|ver| { + key.query_str(ver).ok().map(|p| (*ver, p.into())) + }).next() + }) } - return cmd; + // To find the Universal CRT we look in a specific registry key for where + // all the Universal CRTs are located and then sort them asciibetically to + // find the newest version. While this sort of sorting isn't ideal, it is + // what vcvars does so that's good enough for us. + fn get_ucrt_dir() -> Option<PathBuf> { + LOCAL_MACHINE.open(r"SOFTWARE\Microsoft\Windows Kits\Installed Roots".as_ref()) + .ok().and_then(|key| { + key.query_str("KitsRoot10").ok() + }).and_then(|root| { + fs::read_dir(Path::new(&root).join("Lib")).ok() + }).and_then(|readdir| { + let mut dirs: Vec<_> = readdir.filter_map(|dir| { + dir.ok() + }).map(|dir| { + dir.path() + }).filter(|dir| { + dir.components().last().and_then(|c| { + c.as_os_str().to_str() + }).map(|c| c.starts_with("10.")).unwrap_or(false) + }).collect(); + dirs.sort(); + dirs.pop() + }) + } - // When looking for the Visual Studio installation directory we look in a - // number of locations in varying degrees of precedence: - // - // 1. The Visual Studio registry keys - // 2. The Visual Studio Express registry keys - // 3. A number of somewhat standard environment variables - // - // If we find a hit from any of these keys then we strip off the IDE/Tools - // folders which are typically found at the end. - // - // As a final note, when we take a look at the registry keys they're - // typically found underneath the version of what's installed, but we don't - // quite know what's installed. As a result we probe all sub-keys of the two - // keys we're looking at to find out the maximum version of what's installed - // and we use that root directory. - fn get_vs_install_dir() -> Option<PathBuf> { - LOCAL_MACHINE.open(r"SOFTWARE\Microsoft\VisualStudio".as_ref()).or_else(|_| { - LOCAL_MACHINE.open(r"SOFTWARE\Microsoft\VCExpress".as_ref()) - }).ok().and_then(|key| { - max_version(&key).and_then(|(_vers, key)| { - key.query_str("InstallDir").ok() - }) - }).or_else(|| { - env::var_os("VS120COMNTOOLS") - }).or_else(|| { - env::var_os("VS100COMNTOOLS") - }).or_else(|| { - env::var_os("VS90COMNTOOLS") - }).or_else(|| { - env::var_os("VS80COMNTOOLS") - }).map(PathBuf::from).and_then(|mut dir| { - if dir.ends_with("Common7/IDE") || dir.ends_with("Common7/Tools") { - dir.pop(); - dir.pop(); - Some(dir) - } else { - None - } + // Vcvars finds the correct version of the Windows 10 SDK by looking + // for the include um/Windows.h because sometimes a given version will + // only have UCRT bits without the rest of the SDK. Since we only care about + // libraries and not includes, we just look for the folder `um` in the lib + // section. Like we do for the Universal CRT, we sort the possibilities + // asciibetically to find the newest one as that is what vcvars does. + fn get_sdk10_dir() -> Option<PathBuf> { + LOCAL_MACHINE.open(r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0".as_ref()) + .ok().and_then(|key| { + key.query_str("InstallationFolder").ok() + }).and_then(|root| { + fs::read_dir(Path::new(&root).join("lib")).ok() + }).and_then(|readdir| { + let mut dirs: Vec<_> = readdir.filter_map(|dir| dir.ok()) + .map(|dir| dir.path()).collect(); + dirs.sort(); + dirs.into_iter().rev().filter(|dir| { + dir.join("um").is_dir() + }).next() }) } - // Given a registry key, look at all the sub keys and find the one which has - // the maximal numeric value. - // - // Returns the name of the maximal key as well as the opened maximal key. - fn max_version(key: &RegistryKey) -> Option<(OsString, RegistryKey)> { - let mut max_vers = 0; - let mut max_key = None; - for subkey in key.iter().filter_map(|k| k.ok()) { - let val = subkey.to_str().and_then(|s| { - s.trim_left_matches("v").replace(".", "").parse().ok() - }); - let val = match val { - Some(s) => s, - None => continue, - }; - if val > max_vers { - if let Ok(k) = key.open(&subkey) { - max_vers = val; - max_key = Some((subkey, k)); - } - } - } - return max_key + // Interestingly there are several subdirectories, `win7` `win8` and + // `winv6.3`. Vcvars seems to only care about `winv6.3` though, so the same + // applies to us. Note that if we were targetting kernel mode drivers + // instead of user mode applications, we would care. + fn get_sdk81_dir() -> Option<PathBuf> { + LOCAL_MACHINE.open(r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v8.1".as_ref()) + .ok().and_then(|key| { + key.query_str("InstallationFolder").ok() + }).map(|root| { + Path::new(&root).join("lib").join("winv6.3") + }) } - fn get_windows_sdk_path() -> Option<(PathBuf, usize, Option<OsString>)> { - let key = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows"; - let key = LOCAL_MACHINE.open(key.as_ref()); - let (n, k) = match key.ok().as_ref().and_then(max_version) { - Some(p) => p, - None => return None, - }; - let mut parts = n.to_str().unwrap().trim_left_matches("v").splitn(2, "."); - let major = parts.next().unwrap().parse::<usize>().unwrap(); - let _minor = parts.next().unwrap().parse::<usize>().unwrap(); - k.query_str("InstallationFolder").ok().map(|folder| { - let ver = k.query_str("ProductVersion"); - (PathBuf::from(folder), major, ver.ok()) + fn get_sdk8_dir() -> Option<PathBuf> { + LOCAL_MACHINE.open(r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v8.0".as_ref()) + .ok().and_then(|key| { + key.query_str("InstallationFolder").ok() + }).map(|root| { + Path::new(&root).join("lib").join("win8") }) } - fn get_windows_sdk_lib_path(sess: &Session) -> Option<PathBuf> { - let (mut path, major, ver) = match get_windows_sdk_path() { - Some(p) => p, - None => return None, - }; - path.push("Lib"); - if major <= 7 { - // In Windows SDK 7.x, x86 libraries are directly in the Lib folder, - // x64 libraries are inside, and it's not necessary to link against - // the SDK 7.x when targeting ARM or other architectures. - let x86 = match &sess.target.target.arch[..] { - "x86" => true, - "x86_64" => false, - _ => return None, - }; - Some(if x86 {path} else {path.join("x64")}) - } else if major <= 8 { - // Windows SDK 8.x installs libraries in a folder whose names - // depend on the version of the OS you're targeting. By default - // choose the newest, which usually corresponds to the version of - // the OS you've installed the SDK on. - let extra = match windows_sdk_v8_subdir(sess) { - Some(e) => e, - None => return None, - }; - ["winv6.3", "win8", "win7"].iter().map(|p| path.join(p)).find(|part| { - fs::metadata(part).is_ok() - }).map(|path| { - path.join("um").join(extra) - }) - } else if let Some(mut ver) = ver { - // Windows SDK 10 splits the libraries into architectures the same - // as Windows SDK 8.x, except for the addition of arm64. - // Additionally, the SDK 10 is split by Windows 10 build numbers - // rather than the OS version like the SDK 8.x does. - let extra = match windows_sdk_v10_subdir(sess) { - Some(e) => e, - None => return None, - }; - // To get the correct directory we need to get the Windows SDK 10 - // version, and so far it looks like the "ProductVersion" of the SDK - // corresponds to the folder name that the libraries are located in - // except that the folder contains an extra ".0". For now just - // append a ".0" to look for find the directory we're in. This logic - // will likely want to be refactored one day. - ver.push(".0"); - let p = path.join(ver).join("um").join(extra); - fs::metadata(&p).ok().map(|_| p) + // When choosing the linker toolchain to use, we have to choose the one + // which matches the host architecture. Otherwise we end up in situations + // where someone on 32-bit Windows is trying to cross compile to 64-bit and + // it tries to invoke the native 64-bit linker which won't work. + // + // FIXME - This currently functions based on the host architecture of rustc + // itself but it should instead detect the bitness of the OS itself. + // + // FIXME - Figure out what happens when the host architecture is arm. + // + // FIXME - Some versions of MSVC may not come with all these toolchains. + // Consider returning an array of toolchains and trying them one at a time + // until the linker is found. + fn bin_subdir(arch: &str) -> Option<&'static str> { + if cfg!(target_arch = "x86_64") { + match arch { + "x86" => Some("amd64_x86"), + "x86_64" => Some("amd64"), + "arm" => Some("amd64_arm"), + _ => None, + } + } else if cfg!(target_arch = "x86") { + match arch { + "x86" => Some(""), + "x86_64" => Some("x86_amd64"), + "arm" => Some("x86_arm"), + _ => None, + } } else { None } } - - fn windows_sdk_v8_subdir(sess: &Session) -> Option<&'static str> { - match &sess.target.target.arch[..] { + fn lib_subdir(arch: &str) -> Option<&'static str> { + match arch { "x86" => Some("x86"), "x86_64" => Some("x64"), "arm" => Some("arm"), - _ => return None, + _ => None, } } - - fn windows_sdk_v10_subdir(sess: &Session) -> Option<&'static str> { - match &sess.target.target.arch[..] { - "x86" => Some("x86"), - "x86_64" => Some("x64"), + // MSVC's x86 libraries are not in a subfolder + fn vc_lib_subdir(arch: &str) -> Option<&'static str> { + match arch { + "x86" => Some(""), + "x86_64" => Some("amd64"), "arm" => Some("arm"), - "aarch64" => Some("arm64"), // FIXME - Check if aarch64 is correct - _ => return None, + _ => None, } } - - fn ucrt_install_dir(vs_install_dir: &Path) -> Option<(PathBuf, String)> { - let is_vs_14 = vs_install_dir.iter().filter_map(|p| p.to_str()).any(|s| { - s == "Microsoft Visual Studio 14.0" - }); - if !is_vs_14 { - return None - } - let key = r"SOFTWARE\Microsoft\Windows Kits\Installed Roots"; - let sdk_dir = LOCAL_MACHINE.open(key.as_ref()).and_then(|p| { - p.query_str("KitsRoot10") - }).map(PathBuf::from); - let sdk_dir = match sdk_dir { - Ok(p) => p, - Err(..) => return None, - }; - (move || -> io::Result<_> { - let mut max = None; - let mut max_s = None; - for entry in try!(fs::read_dir(&sdk_dir.join("Lib"))) { - let entry = try!(entry); - if let Ok(s) = entry.file_name().into_string() { - if let Ok(u) = s.replace(".", "").parse::<usize>() { - if Some(u) > max { - max = Some(u); - max_s = Some(s); - } - } - } - } - Ok(max_s.map(|m| (sdk_dir, m))) - })().ok().and_then(|x| x) - } } +// If we're not on Windows, then there's no registry to search through and MSVC +// wouldn't be able to run, so we just call `link.exe` and hope for the best. #[cfg(not(windows))] pub fn link_exe_cmd(_sess: &Session) -> Command { Command::new("link.exe") diff --git a/src/librustc_trans/back/msvc/registry.rs b/src/librustc_trans/back/msvc/registry.rs index 63fb19c9772a8..24179a0cccd22 100644 --- a/src/librustc_trans/back/msvc/registry.rs +++ b/src/librustc_trans/back/msvc/registry.rs @@ -11,7 +11,6 @@ use std::io; use std::ffi::{OsString, OsStr}; use std::os::windows::prelude::*; -use std::ops::RangeFrom; use std::ptr; use libc::{c_void, c_long}; @@ -34,7 +33,6 @@ const KEY_NOTIFY: REGSAM = 0x0010; const SYNCHRONIZE: REGSAM = 0x00100000; const REG_SZ: DWORD = 1; const ERROR_SUCCESS: i32 = 0; -const ERROR_NO_MORE_ITEMS: DWORD = 259; enum __HKEY__ {} pub type HKEY = *mut __HKEY__; @@ -56,14 +54,6 @@ extern "system" { lpType: LPDWORD, lpData: LPBYTE, lpcbData: LPDWORD) -> LONG; - fn RegEnumKeyExW(hKey: HKEY, - dwIndex: DWORD, - lpName: LPWSTR, - lpcName: LPDWORD, - lpReserved: LPDWORD, - lpClass: LPWSTR, - lpcClass: LPDWORD, - lpftLastWriteTime: PFILETIME) -> LONG; fn RegCloseKey(hKey: HKEY) -> LONG; } @@ -76,11 +66,6 @@ enum Repr { Owned(OwnedKey), } -pub struct Iter<'a> { - idx: RangeFrom<DWORD>, - key: &'a RegistryKey, -} - unsafe impl Sync for RegistryKey {} unsafe impl Send for RegistryKey {} @@ -108,10 +93,6 @@ impl RegistryKey { } } - pub fn iter(&self) -> Iter { - Iter { idx: 0.., key: self } - } - pub fn query_str(&self, name: &str) -> io::Result<OsString> { let name: &OsStr = name.as_ref(); let name = name.encode_wide().chain(Some(0)).collect::<Vec<_>>(); @@ -155,25 +136,3 @@ impl Drop for OwnedKey { unsafe { RegCloseKey(self.0); } } } - -impl<'a> Iterator for Iter<'a> { - type Item = io::Result<OsString>; - - fn next(&mut self) -> Option<io::Result<OsString>> { - self.idx.next().and_then(|i| unsafe { - let mut v = Vec::with_capacity(256); - let mut len = v.capacity() as DWORD; - let ret = RegEnumKeyExW(self.key.raw(), i, v.as_mut_ptr(), &mut len, - ptr::null_mut(), ptr::null_mut(), ptr::null_mut(), - ptr::null_mut()); - if ret == ERROR_NO_MORE_ITEMS as LONG { - None - } else if ret != ERROR_SUCCESS { - Some(Err(io::Error::from_raw_os_error(ret as i32))) - } else { - v.set_len(len as usize); - Some(Ok(OsString::from_wide(&v))) - } - }) - } -} diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 1b450092dbd5d..af6510cb3870e 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -261,7 +261,7 @@ pub fn main_args(args: &[String]) -> isize { match (should_test, markdown_input) { (true, true) => { - return markdown::test(input, libs, externs, test_args) + return markdown::test(input, cfgs, libs, externs, test_args) } (true, false) => { return test::run(input, cfgs, libs, externs, test_args, crate_name) diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs index ac64fd3bec0e7..03d2c1a1b4d0d 100644 --- a/src/librustdoc/markdown.rs +++ b/src/librustdoc/markdown.rs @@ -142,13 +142,13 @@ pub fn render(input: &str, mut output: PathBuf, matches: &getopts::Matches, } /// Run any tests/code examples in the markdown file `input`. -pub fn test(input: &str, libs: SearchPaths, externs: core::Externs, +pub fn test(input: &str, cfgs: Vec<String>, libs: SearchPaths, externs: core::Externs, mut test_args: Vec<String>) -> isize { let input_str = load_or_return!(input, 1, 2); let mut opts = TestOptions::default(); opts.no_crate_inject = true; - let mut collector = Collector::new(input.to_string(), libs, externs, + let mut collector = Collector::new(input.to_string(), cfgs, libs, externs, true, opts); find_testable_code(&input_str, &mut collector); test_args.insert(0, "rustdoctest".to_string()); diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 3e303b29d5c7a..3322794c7781e 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -85,7 +85,7 @@ pub fn run(input: &str, rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess)); let mut cfg = config::build_configuration(&sess); - cfg.extend(config::parse_cfgspecs(cfgs)); + cfg.extend(config::parse_cfgspecs(cfgs.clone())); let krate = driver::phase_1_parse_input(&sess, cfg, &input); let krate = driver::phase_2_configure_and_expand(&sess, &cstore, krate, "rustdoc-test", None) @@ -122,6 +122,7 @@ pub fn run(input: &str, let (krate, _) = passes::unindent_comments(krate); let mut collector = Collector::new(krate.name.to_string(), + cfgs, libs, externs, false, @@ -168,7 +169,7 @@ fn scrape_test_config(krate: &::rustc_front::hir::Crate) -> TestOptions { return opts; } -fn runtest(test: &str, cratename: &str, libs: SearchPaths, +fn runtest(test: &str, cratename: &str, cfgs: Vec<String>, libs: SearchPaths, externs: core::Externs, should_panic: bool, no_run: bool, as_test_harness: bool, opts: &TestOptions) { @@ -239,7 +240,8 @@ fn runtest(test: &str, cratename: &str, libs: SearchPaths, let outdir = TempDir::new("rustdoctest").ok().expect("rustdoc needs a tempdir"); let out = Some(outdir.path().to_path_buf()); - let cfg = config::build_configuration(&sess); + let mut cfg = config::build_configuration(&sess); + cfg.extend(config::parse_cfgspecs(cfgs)); let libdir = sess.target_filesearch(PathKind::All).get_lib_path(); let mut control = driver::CompileController::basic(); if no_run { @@ -349,6 +351,7 @@ fn partition_source(s: &str) -> (String, String) { pub struct Collector { pub tests: Vec<testing::TestDescAndFn>, names: Vec<String>, + cfgs: Vec<String>, libs: SearchPaths, externs: core::Externs, cnt: usize, @@ -359,11 +362,12 @@ pub struct Collector { } impl Collector { - pub fn new(cratename: String, libs: SearchPaths, externs: core::Externs, + pub fn new(cratename: String, cfgs: Vec<String>, libs: SearchPaths, externs: core::Externs, use_headers: bool, opts: TestOptions) -> Collector { Collector { tests: Vec::new(), names: Vec::new(), + cfgs: cfgs, libs: libs, externs: externs, cnt: 0, @@ -384,6 +388,7 @@ impl Collector { format!("{}_{}", self.names.join("::"), self.cnt) }; self.cnt += 1; + let cfgs = self.cfgs.clone(); let libs = self.libs.clone(); let externs = self.externs.clone(); let cratename = self.cratename.to_string(); @@ -399,6 +404,7 @@ impl Collector { testfn: testing::DynTestFn(Box::new(move|| { runtest(&test, &cratename, + cfgs, libs, externs, should_panic, diff --git a/src/test/rustdoc/issue-30252.rs b/src/test/rustdoc/issue-30252.rs new file mode 100644 index 0000000000000..11d161fe188e9 --- /dev/null +++ b/src/test/rustdoc/issue-30252.rs @@ -0,0 +1,16 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags:--test --cfg feature="bar" + +/// ```rust +/// assert_eq!(cfg!(feature = "bar"), true); +/// ``` +pub fn foo() {}