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

Workspace awareness #47

Merged
merged 1 commit into from
Feb 19, 2018
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
134 changes: 94 additions & 40 deletions src/bundle/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,20 @@ struct PackageSettings {
metadata: Option<MetadataSettings>,
}

#[derive(Clone, Debug, Deserialize)]
struct WorkspaceSettings {
members: Option<Vec<String>>
}

#[derive(Clone, Debug, Deserialize)]
struct CargoSettings {
package: PackageSettings,
package: Option<PackageSettings>, // "Ancestor" workspace Cargo.toml files may not have package info
workspace: Option<WorkspaceSettings>, // "Ancestor" workspace Cargo.toml files may declare workspaces
}

#[derive(Clone, Debug)]
pub struct Settings {
cargo_settings: CargoSettings,
package: PackageSettings,
package_type: Option<PackageType>, // If `None`, use the default package type for this os
target: Option<(String, TargetInfo)>,
project_out_directory: PathBuf,
Expand All @@ -63,6 +69,19 @@ pub struct Settings {
bundle_settings: BundleSettings,
}

impl CargoSettings {
/*
Try to load a set of CargoSettings from a "Cargo.toml" file in the specified directory
*/
fn load(dir: &PathBuf) -> ::Result<Self> {
let toml_path = dir.join("Cargo.toml");
let mut toml_str = String::new();
let mut toml_file = File::open(toml_path)?;
toml_file.read_to_string(&mut toml_str)?;
toml::from_str(&toml_str).map_err(|e| e.into())
}
}

impl Settings {
pub fn new(current_dir: PathBuf, matches: &ArgMatches) -> ::Result<Self> {
let package_type = match matches.value_of("format") {
Expand All @@ -79,22 +98,14 @@ impl Settings {
Some(triple) => Some((triple.to_string(), TargetInfo::from_str(triple)?)),
None => None,
};
let target_dir = {
let mut path = current_dir.join("target");
if let Some((ref triple, _)) = target {
path.push(triple);
}
path.push(if is_release { "release" } else { "debug" });
path
};
let cargo_settings: CargoSettings = {
let toml_path = current_dir.join("Cargo.toml");
let mut toml_str = String::new();
let mut toml_file = File::open(toml_path)?;
toml_file.read_to_string(&mut toml_str)?;
toml::from_str(&toml_str)?
let cargo_settings = CargoSettings::load(&current_dir)?;
let package = match cargo_settings.package {
Some(package_info) => package_info,
None => bail!("No 'package' info found in 'Cargo.toml'")
};
let binary_path = target_dir.join(&cargo_settings.package.name);
let workspace_dir = Settings::get_workspace_dir(&current_dir);
let target_dir = Settings::get_target_dir(&workspace_dir, &target, is_release);
let binary_path = target_dir.join(&package.name);
let binary_name = match binary_path.file_name().and_then(OsStr::to_str) {
Some(name) => name.to_string(),
None => bail!("Could not get file name of binary file."),
Expand All @@ -107,24 +118,67 @@ impl Settings {
let mut toml_file = File::open(toml_path)?;
toml_file.read_to_string(&mut toml_str)?;
toml::from_str(&toml_str)?
} else if let Some(bundle_settings) = cargo_settings.package.metadata.as_ref().and_then(|metadata| metadata.bundle.as_ref()) {
} else if let Some(bundle_settings) = package.metadata.as_ref().
and_then(|metadata| metadata.bundle.as_ref()) {
bundle_settings.clone()
} else {
bail!("No [package.metadata.bundle] section or Bundle.toml file found.");
}
};
Ok(Settings {
cargo_settings: cargo_settings,
package_type: package_type,
target: target,
is_release: is_release,
package,
package_type,
target,
is_release,
project_out_directory: target_dir,
binary_path: binary_path,
binary_name: binary_name,
bundle_settings: bundle_settings,
binary_path,
binary_name,
bundle_settings,
})
}

/*
The target_dir where binaries will be compiled to by cargo can vary:
- this directory is a member of a workspace project
- overridden by CARGO_TARGET_DIR environment variable
- specified in build.target-dir configuration key
- if the build is a 'release' or 'debug' build

This function determines where 'target' dir is and suffixes it with 'release' or 'debug'
to determine where the compiled binary will be located.
*/
fn get_target_dir(project_root_dir: &PathBuf, target: &Option<(String, TargetInfo)>, is_release: bool)
-> PathBuf {
let mut path = project_root_dir.join("target");
if let &Some((ref triple, _)) = target {
path.push(triple);
}
path.push(if is_release { "release" } else { "debug" });
path
}

/*
The specification of the Cargo.toml Manifest that covers the "workspace" section is here:
https://doc.rust-lang.org/cargo/reference/manifest.html#the-workspace-section

Determining if the current project folder is part of a workspace:
- Walk up the file system, looking for a Cargo.toml file.
- Stop at the first one found.
- If one is found before reaching "/" then this folder belongs to that parent workspace
*/
fn get_workspace_dir(current_dir: &PathBuf) -> PathBuf {
let mut dir = current_dir.clone();
while dir.pop() {
let set = CargoSettings::load(&dir);
if set.is_ok() {
return dir;
}
}

// Nothing found walking up the file system, return the starting directory
current_dir.clone()
}

/// Returns the directory where the bundle should be placed.
pub fn project_out_directory(&self) -> &Path {
&self.project_out_directory
Expand Down Expand Up @@ -184,7 +238,7 @@ impl Settings {
pub fn is_release_build(&self) -> bool { self.is_release }

pub fn bundle_name(&self) -> &str {
self.bundle_settings.name.as_ref().unwrap_or(&self.cargo_settings.package.name)
self.bundle_settings.name.as_ref().unwrap_or(&self.package.name)
}

pub fn bundle_identifier(&self) -> &str {
Expand All @@ -209,26 +263,27 @@ impl Settings {
}

pub fn version_string(&self) -> &str {
self.bundle_settings.version.as_ref().unwrap_or(&self.cargo_settings.package.version)
self.bundle_settings.version.as_ref().unwrap_or(&self.package.version)
}

pub fn copyright_string(&self) -> Option<&str> {
self.bundle_settings.copyright.as_ref().map(String::as_str)
}

pub fn author_names(&self) -> std::slice::Iter<String> {
match self.cargo_settings.package.authors {
match self.package.authors {
Some(ref names) => names.iter(),
None => [].iter(),
}
}

pub fn homepage_url(&self) -> &str {
&self.cargo_settings.package.homepage.as_ref().map(String::as_str).unwrap_or("")
&self.package.homepage.as_ref().map(String::as_str).unwrap_or("")
}

pub fn short_description(&self) -> &str {
self.bundle_settings.short_description.as_ref().unwrap_or(&self.cargo_settings.package.description)
self.bundle_settings.short_description.as_ref().
unwrap_or(&self.package.description)
}

pub fn long_description(&self) -> Option<&str> {
Expand All @@ -249,7 +304,7 @@ impl<'a> ResourcePaths<'a> {
pattern_iter: patterns.iter(),
glob_iter: None,
walk_iter: None,
allow_walk: allow_walk,
allow_walk,
}
}
}
Expand Down Expand Up @@ -340,15 +395,14 @@ mod tests {
[dependencies]\n\
rand = \"0.4\"\n";
let cargo_settings: CargoSettings = toml::from_str(toml_str).unwrap();
assert_eq!(cargo_settings.package.name, "example");
assert_eq!(cargo_settings.package.version, "0.1.0");
assert_eq!(cargo_settings.package.description,
"An example application.");
assert_eq!(cargo_settings.package.homepage, None);
assert_eq!(cargo_settings.package.authors,
Some(vec!["Jane Doe".to_string()]));
assert!(cargo_settings.package.metadata.is_some());
let metadata = cargo_settings.package.metadata.as_ref().unwrap();
let package = cargo_settings.package.unwrap();
assert_eq!(package.name, "example");
assert_eq!(package.version, "0.1.0");
assert_eq!(package.description, "An example application.");
assert_eq!(package.homepage, None);
assert_eq!(package.authors, Some(vec!["Jane Doe".to_string()]));
assert!(package.metadata.is_some());
let metadata = package.metadata.as_ref().unwrap();
assert!(metadata.bundle.is_some());
let bundle = metadata.bundle.as_ref().unwrap();
assert_eq!(bundle.name, Some("Example Application".to_string()));
Expand Down
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ error_chain! {
}

/// Runs `cargo build` to make sure the binary file is up-to-date.
fn build_project_if_unbuilt(settings: &Settings) -> Result<()> {
fn build_project_if_unbuilt(settings: &Settings) -> ::Result<()> {
let mut args = vec!["build".to_string()];
if let Some(triple) = settings.target_triple() {
args.push(format!("--target={}", triple));
Expand Down