-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(cli): load Cargo configuration to check default build target (#4990
- Loading branch information
1 parent
fa44c44
commit 436f3d8
Showing
3 changed files
with
143 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
"cli.rs": patch | ||
"cli.js": patch | ||
--- | ||
|
||
Check if the default build target is set in the Cargo configuration. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
use anyhow::{Context, Result}; | ||
use serde::Deserialize; | ||
use std::{ | ||
fs, | ||
path::{Path, PathBuf}, | ||
}; | ||
|
||
struct PathAncestors<'a> { | ||
current: Option<&'a Path>, | ||
} | ||
|
||
impl<'a> PathAncestors<'a> { | ||
fn new(path: &'a Path) -> PathAncestors<'a> { | ||
PathAncestors { | ||
current: Some(path), | ||
} | ||
} | ||
} | ||
|
||
impl<'a> Iterator for PathAncestors<'a> { | ||
type Item = &'a Path; | ||
|
||
fn next(&mut self) -> Option<&'a Path> { | ||
if let Some(path) = self.current { | ||
self.current = path.parent(); | ||
|
||
Some(path) | ||
} else { | ||
None | ||
} | ||
} | ||
} | ||
|
||
#[derive(Default, Deserialize)] | ||
pub struct BuildConfig { | ||
target: Option<String>, | ||
} | ||
|
||
#[derive(Deserialize)] | ||
pub struct ConfigSchema { | ||
build: Option<BuildConfig>, | ||
} | ||
|
||
#[derive(Default)] | ||
pub struct Config { | ||
build: BuildConfig, | ||
} | ||
|
||
impl Config { | ||
pub fn load(path: &Path) -> Result<Self> { | ||
let mut config = Self::default(); | ||
|
||
for current in PathAncestors::new(path) { | ||
if let Some(path) = get_file_path(¤t.join(".cargo"), "config", true)? { | ||
let contents = fs::read_to_string(&path) | ||
.with_context(|| format!("failed to read configuration file `{}`", path.display()))?; | ||
let toml: ConfigSchema = toml::from_str(&contents) | ||
.with_context(|| format!("could not parse TOML configuration in `{}`", path.display()))?; | ||
|
||
if let Some(target) = toml.build.and_then(|b| b.target) { | ||
config.build.target = Some(target); | ||
break; | ||
} | ||
} | ||
} | ||
|
||
Ok(config) | ||
} | ||
|
||
pub fn build(&self) -> &BuildConfig { | ||
&self.build | ||
} | ||
} | ||
|
||
impl BuildConfig { | ||
pub fn target(&self) -> Option<&str> { | ||
self.target.as_deref() | ||
} | ||
} | ||
|
||
/// The purpose of this function is to aid in the transition to using | ||
/// .toml extensions on Cargo's config files, which were historically not used. | ||
/// Both 'config.toml' and 'credentials.toml' should be valid with or without extension. | ||
/// When both exist, we want to prefer the one without an extension for | ||
/// backwards compatibility, but warn the user appropriately. | ||
fn get_file_path( | ||
dir: &Path, | ||
filename_without_extension: &str, | ||
warn: bool, | ||
) -> Result<Option<PathBuf>> { | ||
let possible = dir.join(filename_without_extension); | ||
let possible_with_extension = dir.join(format!("{}.toml", filename_without_extension)); | ||
|
||
if possible.exists() { | ||
if warn && possible_with_extension.exists() { | ||
// We don't want to print a warning if the version | ||
// without the extension is just a symlink to the version | ||
// WITH an extension, which people may want to do to | ||
// support multiple Cargo versions at once and not | ||
// get a warning. | ||
let skip_warning = if let Ok(target_path) = fs::read_link(&possible) { | ||
target_path == possible_with_extension | ||
} else { | ||
false | ||
}; | ||
|
||
if !skip_warning { | ||
log::warn!( | ||
"Both `{}` and `{}` exist. Using `{}`", | ||
possible.display(), | ||
possible_with_extension.display(), | ||
possible.display() | ||
); | ||
} | ||
} | ||
|
||
Ok(Some(possible)) | ||
} else if possible_with_extension.exists() { | ||
Ok(Some(possible_with_extension)) | ||
} else { | ||
Ok(None) | ||
} | ||
} |