Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 34 additions & 6 deletions crates/base-db/src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,11 +295,30 @@ pub struct CrateData {
pub is_proc_macro: bool,
}

#[derive(Default, Debug, Clone, PartialEq, Eq)]
#[derive(Default, Clone, PartialEq, Eq)]
pub struct Env {
entries: FxHashMap<String, String>,
}

impl fmt::Debug for Env {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
struct EnvDebug<'s>(Vec<(&'s String, &'s String)>);

impl fmt::Debug for EnvDebug<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_map().entries(self.0.iter().copied()).finish()
}
}
f.debug_struct("Env")
.field("entries", &{
let mut entries: Vec<_> = self.entries.iter().collect();
entries.sort();
EnvDebug(entries)
})
.finish()
}
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Dependency {
pub crate_id: CrateId,
Expand Down Expand Up @@ -331,10 +350,11 @@ impl CrateGraph {
version: Option<String>,
cfg_options: Arc<CfgOptions>,
potential_cfg_options: Option<Arc<CfgOptions>>,
env: Env,
mut env: Env,
is_proc_macro: bool,
origin: CrateOrigin,
) -> CrateId {
env.entries.shrink_to_fit();
let data = CrateData {
root_file_id,
edition,
Expand Down Expand Up @@ -651,16 +671,24 @@ impl FromIterator<(String, String)> for Env {
}

impl Env {
pub fn set(&mut self, env: &str, value: String) {
self.entries.insert(env.to_owned(), value);
pub fn set(&mut self, env: &str, value: impl Into<String>) {
self.entries.insert(env.to_owned(), value.into());
}

pub fn get(&self, env: &str) -> Option<String> {
self.entries.get(env).cloned()
}

pub fn iter(&self) -> impl Iterator<Item = (&str, &str)> {
self.entries.iter().map(|(k, v)| (k.as_str(), v.as_str()))
pub fn extend_from_other(&mut self, other: &Env) {
self.entries.extend(other.entries.iter().map(|(x, y)| (x.to_owned(), y.to_owned())));
}
}

impl From<Env> for Vec<(String, String)> {
fn from(env: Env) -> Vec<(String, String)> {
let mut entries: Vec<_> = env.entries.into_iter().collect();
entries.sort();
entries
}
}

Expand Down
7 changes: 0 additions & 7 deletions crates/cfg/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,6 @@ impl CfgOptions {
self.enabled.insert(CfgAtom::KeyValue { key, value });
}

pub fn difference<'a>(
&'a self,
other: &'a CfgOptions,
) -> impl Iterator<Item = &'a CfgAtom> + 'a {
self.enabled.difference(&other.enabled)
}

pub fn apply_diff(&mut self, diff: CfgDiff) {
for atom in diff.enable {
self.enabled.insert(atom);
Expand Down
3 changes: 1 addition & 2 deletions crates/load-cargo/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -407,8 +407,7 @@ impl ProcMacroExpander for Expander {
call_site: Span,
mixed_site: Span,
) -> Result<tt::Subtree<Span>, ProcMacroExpansionError> {
let env = env.iter().map(|(k, v)| (k.to_owned(), v.to_owned())).collect();
match self.0.expand(subtree, attrs, env, def_site, call_site, mixed_site) {
match self.0.expand(subtree, attrs, env.clone(), def_site, call_site, mixed_site) {
Ok(Ok(subtree)) => Ok(subtree),
Ok(Err(err)) => Err(ProcMacroExpansionError::Panic(err.0)),
Err(err) => Err(ProcMacroExpansionError::System(err.to_string())),
Expand Down
10 changes: 4 additions & 6 deletions crates/proc-macro-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub mod msg;
mod process;
mod version;

use base_db::Env;
use indexmap::IndexSet;
use paths::AbsPathBuf;
use rustc_hash::FxHashMap;
Expand Down Expand Up @@ -152,16 +153,13 @@ impl ProcMacro {
&self,
subtree: &tt::Subtree<Span>,
attr: Option<&tt::Subtree<Span>>,
env: Vec<(String, String)>,
env: Env,
def_site: Span,
call_site: Span,
mixed_site: Span,
) -> Result<Result<tt::Subtree<Span>, PanicMessage>, ServerError> {
let version = self.process.lock().unwrap_or_else(|e| e.into_inner()).version();
let current_dir = env
.iter()
.find(|(name, _)| name == "CARGO_MANIFEST_DIR")
.map(|(_, value)| value.clone());
let current_dir = env.get("CARGO_MANIFEST_DIR");

let mut span_data_table = IndexSet::default();
let def_site = span_data_table.insert_full(def_site).0;
Expand All @@ -172,7 +170,7 @@ impl ProcMacro {
macro_name: self.name.to_string(),
attributes: attr.map(|subtree| FlatTree::new(subtree, version, &mut span_data_table)),
lib: self.dylib_path.to_path_buf().into(),
env,
env: env.into(),
current_dir,
has_global_spans: ExpnGlobals {
serialize: version >= HAS_GLOBAL_SPANS,
Expand Down
4 changes: 3 additions & 1 deletion crates/project-model/src/build_scripts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,18 @@ use serde::Deserialize;
use toolchain::Tool;

use crate::{
cfg_flag::CfgFlag, utf8_stdout, CargoConfig, CargoFeatures, CargoWorkspace, InvocationLocation,
cfg::CfgFlag, utf8_stdout, CargoConfig, CargoFeatures, CargoWorkspace, InvocationLocation,
InvocationStrategy, Package, Sysroot, TargetKind,
};

/// Output of the build script and proc-macro building steps for a workspace.
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct WorkspaceBuildScripts {
outputs: ArenaMap<Package, BuildScriptOutput>,
error: Option<String>,
}

/// Output of the build script and proc-macro building step for a concrete package.
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub(crate) struct BuildScriptOutput {
/// List of config flags defined by this package's build script.
Expand Down
32 changes: 32 additions & 0 deletions crates/project-model/src/cargo_workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,20 @@ pub struct PackageData {
pub active_features: Vec<String>,
/// String representation of package id
pub id: String,
/// Authors as given in the `Cargo.toml`
pub authors: Vec<String>,
/// Description as given in the `Cargo.toml`
pub description: Option<String>,
/// Homepage as given in the `Cargo.toml`
pub homepage: Option<String>,
/// License as given in the `Cargo.toml`
pub license: Option<String>,
/// License file as given in the `Cargo.toml`
pub license_file: Option<Utf8PathBuf>,
/// Readme file as given in the `Cargo.toml`
pub readme: Option<Utf8PathBuf>,
/// Rust version as given in the `Cargo.toml`
pub rust_version: Option<semver::Version>,
/// The contents of [package.metadata.rust-analyzer]
pub metadata: RustAnalyzerPackageMetaData,
}
Expand Down Expand Up @@ -225,6 +239,10 @@ impl TargetKind {
}
TargetKind::Other
}

pub fn is_executable(self) -> bool {
matches!(self, TargetKind::Bin | TargetKind::Example)
}
}

// Deserialize helper for the cargo metadata
Expand Down Expand Up @@ -330,6 +348,13 @@ impl CargoWorkspace {
repository,
edition,
metadata,
authors,
description,
homepage,
license,
license_file,
readme,
rust_version,
..
} = meta_pkg;
let meta = from_value::<PackageMetadata>(metadata).unwrap_or_default();
Expand Down Expand Up @@ -358,6 +383,13 @@ impl CargoWorkspace {
is_member,
edition,
repository,
authors,
description,
homepage,
license,
license_file,
readme,
rust_version,
dependencies: Vec::new(),
features: features.into_iter().collect(),
active_features: Vec::new(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
//! rustc main.rs --cfg foo --cfg 'feature="bar"'
use std::{fmt, str::FromStr};

use cfg::CfgOptions;
use cfg::{CfgDiff, CfgOptions};
use rustc_hash::FxHashMap;
use serde::Serialize;

#[derive(Clone, Eq, PartialEq, Debug, Serialize)]
Expand Down Expand Up @@ -70,3 +71,27 @@ impl fmt::Display for CfgFlag {
}
}
}

/// A set of cfg-overrides per crate.
#[derive(Default, Debug, Clone, Eq, PartialEq)]
pub struct CfgOverrides {
/// A global set of overrides matching all crates.
pub global: CfgDiff,
/// A set of overrides matching specific crates.
pub selective: FxHashMap<String, CfgDiff>,
}

impl CfgOverrides {
pub fn len(&self) -> usize {
self.global.len() + self.selective.values().map(|it| it.len()).sum::<usize>()
}

pub fn apply(&self, cfg_options: &mut CfgOptions, name: &str) {
if !self.global.is_empty() {
cfg_options.apply_diff(self.global.clone());
};
if let Some(diff) = self.selective.get(name) {
cfg_options.apply_diff(diff.clone());
};
}
}
85 changes: 85 additions & 0 deletions crates/project-model/src/env.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
//! Cargo-like environment variables injection.
use base_db::Env;
use rustc_hash::FxHashMap;
use toolchain::Tool;

use crate::{utf8_stdout, ManifestPath, PackageData, Sysroot, TargetKind};

/// Recreates the compile-time environment variables that Cargo sets.
///
/// Should be synced with
/// <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates>
///
/// FIXME: ask Cargo to provide this data instead of re-deriving.
pub(crate) fn inject_cargo_package_env(env: &mut Env, package: &PackageData) {
// FIXME: Missing variables:
// CARGO_BIN_NAME, CARGO_BIN_EXE_<name>

let manifest_dir = package.manifest.parent();
env.set("CARGO_MANIFEST_DIR", manifest_dir.as_str());

env.set("CARGO_PKG_VERSION", package.version.to_string());
env.set("CARGO_PKG_VERSION_MAJOR", package.version.major.to_string());
env.set("CARGO_PKG_VERSION_MINOR", package.version.minor.to_string());
env.set("CARGO_PKG_VERSION_PATCH", package.version.patch.to_string());
env.set("CARGO_PKG_VERSION_PRE", package.version.pre.to_string());

env.set("CARGO_PKG_AUTHORS", package.authors.join(":").clone());

env.set("CARGO_PKG_NAME", package.name.clone());
env.set("CARGO_PKG_DESCRIPTION", package.description.as_deref().unwrap_or_default());
env.set("CARGO_PKG_HOMEPAGE", package.homepage.as_deref().unwrap_or_default());
env.set("CARGO_PKG_REPOSITORY", package.repository.as_deref().unwrap_or_default());
env.set("CARGO_PKG_LICENSE", package.license.as_deref().unwrap_or_default());
env.set(
"CARGO_PKG_LICENSE_FILE",
package.license_file.as_ref().map(ToString::to_string).unwrap_or_default(),
);
env.set(
"CARGO_PKG_README",
package.readme.as_ref().map(ToString::to_string).unwrap_or_default(),
);

env.set(
"CARGO_PKG_RUST_VERSION",
package.rust_version.as_ref().map(ToString::to_string).unwrap_or_default(),
);
}

pub(crate) fn inject_cargo_env(env: &mut Env) {
env.set("CARGO", Tool::Cargo.path().to_string());
}

pub(crate) fn inject_rustc_tool_env(env: &mut Env, cargo_name: &str, kind: TargetKind) {
_ = kind;
// FIXME
// if kind.is_executable() {
// env.set("CARGO_BIN_NAME", cargo_name);
// }
env.set("CARGO_CRATE_NAME", cargo_name.replace('-', "_"));
}

pub(crate) fn cargo_config_env(
cargo_toml: &ManifestPath,
extra_env: &FxHashMap<String, String>,
sysroot: Option<&Sysroot>,
) -> FxHashMap<String, String> {
let mut cargo_config = Sysroot::tool(sysroot, Tool::Cargo);
cargo_config.envs(extra_env);
cargo_config
.current_dir(cargo_toml.parent())
.args(["-Z", "unstable-options", "config", "get", "env"])
.env("RUSTC_BOOTSTRAP", "1");
// if successful we receive `env.key.value = "value" per entry
tracing::debug!("Discovering cargo config env by {:?}", cargo_config);
utf8_stdout(cargo_config).map(parse_output_cargo_config_env).unwrap_or_default()
}

fn parse_output_cargo_config_env(stdout: String) -> FxHashMap<String, String> {
stdout
.lines()
.filter_map(|l| l.strip_prefix("env."))
.filter_map(|l| l.split_once(".value = "))
.map(|(key, value)| (key.to_owned(), value.trim_matches('"').to_owned()))
.collect()
}
6 changes: 4 additions & 2 deletions crates/project-model/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@

mod build_scripts;
mod cargo_workspace;
mod cfg_flag;
mod cfg;
mod env;
mod manifest_path;
mod project_json;
mod rustc_cfg;
Expand Down Expand Up @@ -47,10 +48,11 @@ pub use crate::{
CargoConfig, CargoFeatures, CargoWorkspace, Package, PackageData, PackageDependency,
RustLibSource, Target, TargetData, TargetKind,
},
cfg::CfgOverrides,
manifest_path::ManifestPath,
project_json::{ProjectJson, ProjectJsonData},
sysroot::Sysroot,
workspace::{CfgOverrides, PackageRoot, ProjectWorkspace},
workspace::{FileLoader, PackageRoot, ProjectWorkspace},
};

#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
Expand Down
2 changes: 1 addition & 1 deletion crates/project-model/src/project_json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ use rustc_hash::FxHashMap;
use serde::{de, Deserialize, Serialize};
use span::Edition;

use crate::cfg_flag::CfgFlag;
use crate::cfg::CfgFlag;

/// Roots and crates that compose this Rust project.
#[derive(Clone, Debug, Eq, PartialEq)]
Expand Down
5 changes: 1 addition & 4 deletions crates/project-model/src/rustc_cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use anyhow::Context;
use rustc_hash::FxHashMap;
use toolchain::Tool;

use crate::{cfg_flag::CfgFlag, utf8_stdout, ManifestPath, Sysroot};
use crate::{cfg::CfgFlag, utf8_stdout, ManifestPath, Sysroot};

/// Determines how `rustc --print cfg` is discovered and invoked.
pub(crate) enum RustcCfgConfig<'a> {
Expand Down Expand Up @@ -32,9 +32,6 @@ pub(crate) fn get(
}
}

// Add miri cfg, which is useful for mir eval in stdlib
res.push(CfgFlag::Atom("miri".into()));

let rustc_cfgs = get_rust_cfgs(target, extra_env, config);

let rustc_cfgs = match rustc_cfgs {
Expand Down
Loading