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

Avoid fs::canonicalize for project model paths #65

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
28 changes: 26 additions & 2 deletions crates/elp/src/bin/build_info_cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@
* of this source tree.
*/

use std::env;
use std::fs;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;

use anyhow::anyhow;
use anyhow::Context;
use anyhow::Result;
use elp_ide::elp_ide_db::elp_base_db::AbsPath;
use elp_ide::elp_ide_db::elp_base_db::AbsPathBuf;
Expand All @@ -23,13 +27,15 @@ use elp_project_model::IncludeParentDirs;
use elp_project_model::Project;
use elp_project_model::ProjectBuildData;
use elp_project_model::ProjectManifest;
use paths::Utf8PathBuf;

use crate::args::BuildInfo;
use crate::args::ProjectInfo;

pub(crate) fn save_build_info(args: BuildInfo, query_config: &BuckQueryConfig) -> Result<()> {
let root = fs::canonicalize(&args.project)?;
let root = AbsPathBuf::assert_utf8(root);
let project = Utf8PathBuf::from_path_buf(args.project.clone())
.expect("expected the project to be valid UTF-8");
let root = current_dir()?.absolutize(project);
let (_elp_config, manifest) = ProjectManifest::discover(&root)?;
let project = Project::load(&manifest, EqwalizerConfig::default(), query_config)?;
let mut writer = File::create(&args.to)?;
Expand Down Expand Up @@ -96,3 +102,21 @@ fn load_fallback(
let project = Project::load(&manifest, elp_config.eqwalizer, query_config)?;
Ok((manifest, project))
}

fn current_dir() -> Result<AbsPathBuf> {
let mut cwd = env::current_dir().context("couldn't determine the current working directory")?;

// Prefer $PWD which is not canonicalized. On Unix `current_dir` uses getcwd(3) which returns
// the current directory canonicalized. Instead this behaves like `pwd -L` and leaves symlinks
// unresolved.
if let Some(pwd) = std::env::var_os("PWD").map(PathBuf::from) {
if pwd.canonicalize().ok().is_some_and(|pwd| pwd == cwd) {
cwd = pwd;
}
}

Ok(AbsPathBuf::assert(
Utf8PathBuf::from_path_buf(cwd)
.map_err(|_| anyhow!("couldn't convert current working directory to UTF-8"))?,
))
}
1 change: 0 additions & 1 deletion crates/elp/src/project_loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ impl ProjectLoader {
let mut project_roots = FxHashMap::default();
let otp_root = Otp::find_otp();
if let Ok(otp_root) = otp_root {
let otp_root = AbsPathBuf::assert(otp_root);
project_roots.insert(otp_root, None);
}
let start = SystemTime::now();
Expand Down
6 changes: 3 additions & 3 deletions crates/project_model/src/buck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ impl BuckProject {
pub fn load_from_config(
buck_conf: &BuckConfig,
query_config: &BuckQueryConfig,
) -> Result<(BuckProject, Vec<ProjectAppData>, Utf8PathBuf), anyhow::Error> {
) -> Result<(BuckProject, Vec<ProjectAppData>, AbsPathBuf), anyhow::Error> {
let target_info = load_buck_targets(buck_conf, query_config)?;
let otp_root = Otp::find_otp()?;
let project_app_data = targets_to_project_data(&target_info.targets, &otp_root);
Expand Down Expand Up @@ -591,7 +591,7 @@ fn examine_path(path: &AbsPath, dir_based_on_buck_file: &AbsPath) -> Option<AbsP

pub fn targets_to_project_data(
targets: &FxHashMap<TargetFullName, Target>,
otp_root: &Utf8Path,
otp_root: &AbsPath,
) -> Vec<ProjectAppData> {
let it = targets
.values()
Expand Down Expand Up @@ -634,7 +634,7 @@ pub fn targets_to_project_data(
.filter(|target| target.target_type != TargetType::ErlangTest)
.filter_map(|target| target.dir.parent().map(|p| p.to_path_buf()))
.collect();
global_inc.push(AbsPathBuf::assert(otp_root.to_path_buf()));
global_inc.push(otp_root.to_path_buf());
for (_, mut acc) in accs {
acc.add_global_includes(global_inc.clone());
result.push(acc.into());
Expand Down
18 changes: 2 additions & 16 deletions crates/project_model/src/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ extern crate serde;

use std::fs;

use anyhow::Context;
use anyhow::Result;
use eetf::Atom;
use eetf::Term;
Expand All @@ -21,8 +20,6 @@ use indexmap::indexset;
use indexmap::IndexSet;
use paths::AbsPath;
use paths::AbsPathBuf;
use paths::Utf8Path;
use paths::Utf8PathBuf;
use serde::Deserialize;
use serde::Serialize;

Expand Down Expand Up @@ -63,13 +60,9 @@ fn default_src_dirs() -> Vec<String> {

impl JsonProjectAppData {
pub fn to_project_app_data(&self, root_path: &AbsPath, is_dep: bool) -> Result<ProjectAppData> {
let dir = canonicalize(root_path.join(&self.dir))
.with_context(|| format!("Checking dir: {}", &self.dir))?;
let dir = root_path.absolutize(&self.dir);
let ebin = match &self.ebin {
Some(ebin) => Some(
canonicalize(dir.join(ebin))
.with_context(|| format!("Checking ebin dir: {ebin}"))?,
),
Some(ebin) => Some(dir.absolutize(ebin)),
None => None,
};
let include_dirs = self.include_dirs.iter().map(|inc| dir.join(inc)).collect();
Expand Down Expand Up @@ -223,10 +216,3 @@ pub(crate) fn gen_app_data(

(apps, deps)
}

fn canonicalize(path: impl AsRef<Utf8Path>) -> Result<AbsPathBuf> {
let abs = fs::canonicalize(path.as_ref())?;
Ok(AbsPathBuf::assert(
Utf8PathBuf::from_path_buf(abs).expect("Could not convert to UTF8"),
))
}
9 changes: 4 additions & 5 deletions crates/project_model/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -947,25 +947,24 @@ impl Project {
ProjectManifest::Json(config) => {
let otp_root = Otp::find_otp()?;
let config_path = config.config_path().to_path_buf();
let (mut apps, deps) = json::gen_app_data(config, AbsPath::assert(&otp_root));
let (mut apps, deps) = json::gen_app_data(config, &otp_root);
let project = StaticProject { config_path };
apps.extend(deps);
(ProjectBuildData::Static(project), apps, otp_root)
}
ProjectManifest::NoManifest(config) => {
let otp_root = Otp::find_otp()?;
let abs_otp_root = AbsPath::assert(&otp_root);
let config_path = config.config_path().to_path_buf();
let mut apps = config.to_project_app_data(abs_otp_root);
let mut apps = config.to_project_app_data(&otp_root);
let eqwalizer_support_app =
eqwalizer_support::eqwalizer_suppport_data(abs_otp_root);
eqwalizer_support::eqwalizer_suppport_data(&otp_root);
let project = StaticProject { config_path };
apps.push(eqwalizer_support_app);
(ProjectBuildData::Static(project), apps, otp_root)
}
};

let (otp, otp_project_apps) = Otp::discover(otp_root);
let (otp, otp_project_apps) = Otp::discover(&otp_root);
project_apps.extend(otp_project_apps);
Ok(Project {
otp,
Expand Down
26 changes: 14 additions & 12 deletions crates/project_model/src/otp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ use anyhow::bail;
use anyhow::Result;
use elp_log::timeit;
use lazy_static::lazy_static;
use paths::AbsPath;
use paths::AbsPathBuf;
use paths::Utf8Path;
use paths::Utf8PathBuf;

use crate::AppName;
Expand All @@ -33,7 +33,7 @@ lazy_static! {
}

lazy_static! {
pub static ref OTP_ROOT: Utf8PathBuf =
pub static ref OTP_ROOT: AbsPathBuf =
Otp::find_otp().expect("tests should always be able to find OTP");
pub static ref OTP_ERTS_DIR: AbsPathBuf = get_erts_dir();
pub static ref OTP_ERLANG_MODULE: (PathBuf, String) = get_erlang_module();
Expand Down Expand Up @@ -62,7 +62,7 @@ pub fn supports_eep59_doc_attributes() -> bool {
}

fn get_erts_dir() -> AbsPathBuf {
let (_otp, apps) = Otp::discover(OTP_ROOT.to_path_buf());
let (_otp, apps) = Otp::discover(&OTP_ROOT);
for app in apps {
if app.name == AppName("erts".to_string()) {
return app.dir;
Expand All @@ -78,7 +78,7 @@ fn get_erlang_module() -> (PathBuf, String) {
}

impl Otp {
pub fn find_otp() -> Result<Utf8PathBuf> {
pub fn find_otp() -> Result<AbsPathBuf> {
let _timer = timeit!("find otp");
let erl = ERL.read().unwrap();
let output = Command::new(&*erl)
Expand All @@ -99,8 +99,8 @@ impl Otp {
}
let path = String::from_utf8(output.stdout)?;
let result: Utf8PathBuf = format!("{}/lib", path).into();
let result = fs::canonicalize(result)?;
Ok(Utf8PathBuf::from_path_buf(result).expect("Could not create Utf8PathBuf"))
AbsPathBuf::try_from(result)
.map_err(|err| anyhow::anyhow!("expected an absolute path: {}", err))
}
pub fn otp_version() -> Result<String> {
let _timer = timeit!("otp_version");
Expand All @@ -125,27 +125,29 @@ impl Otp {
Ok(val)
}

pub fn discover(path: Utf8PathBuf) -> (Otp, Vec<ProjectAppData>) {
let apps = Self::discover_otp_apps(&path);
pub fn discover(path: &AbsPath) -> (Otp, Vec<ProjectAppData>) {
let apps = Self::discover_otp_apps(path);
(
Otp {
lib_dir: AbsPathBuf::assert(path),
lib_dir: path.to_path_buf(),
},
apps,
)
}

fn discover_otp_apps(path: &Utf8Path) -> Vec<ProjectAppData> {
fn discover_otp_apps(path: &AbsPath) -> Vec<ProjectAppData> {
log::info!("Loading OTP apps from {:?}", path);
if let Ok(entries) = fs::read_dir(path) {
entries
.into_iter()
.filter_map(|entry| {
let entry = entry.ok()?;
let name = entry.file_name();
let path = fs::canonicalize(entry.path()).expect("Could not canonicalize path");
// `DirEntry::path` joins the path passed to `fs::read_dir`, so these paths
// must be absolute.
let dir = AbsPathBuf::assert(
Utf8PathBuf::from_path_buf(path).expect("Could not convert to Utf8PathBuf"),
Utf8PathBuf::from_path_buf(entry.path())
.expect("Could not convert to Utf8PathBuf"),
);
Some(ProjectAppData::otp_app_data(name.to_str()?, &dir))
})
Expand Down
4 changes: 2 additions & 2 deletions crates/project_model/src/rebar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,14 +142,14 @@ impl RebarProject {
pub fn from_rebar_build_info(
path: impl AsRef<Path>,
rebar_config: RebarConfig,
) -> Result<(RebarProject, Utf8PathBuf, Vec<ProjectAppData>)> {
) -> Result<(RebarProject, AbsPathBuf, Vec<ProjectAppData>)> {
Self::_from_rebar_build_info(path.as_ref(), rebar_config)
}

fn _from_rebar_build_info(
path: &Path,
rebar_config: RebarConfig,
) -> Result<(RebarProject, Utf8PathBuf, Vec<ProjectAppData>)> {
) -> Result<(RebarProject, AbsPathBuf, Vec<ProjectAppData>)> {
let data = fs::read(path)?;
let mut build_info = eetf::Term::decode(&*data)?;
let otp_root = into_abs_path(map_pop(&mut build_info, "otp_lib_dir")?)?;
Expand Down