diff --git a/Cargo.lock b/Cargo.lock index 71a690cc4..01c5ad646 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -29,6 +29,7 @@ dependencies = [ "staticfile 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/Cargo.toml b/Cargo.toml index a1376d8df..6706c2000 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ libc = "0.2" badge = { version = "0", path = "src/web/badge" } error-chain = "0.10" comrak = { version = "0.1", default-features = false } +toml = "0.4" # iron dependencies iron = "0.5" diff --git a/src/db/add_package.rs b/src/db/add_package.rs index d3a331a8f..40063ffb5 100644 --- a/src/db/add_package.rs +++ b/src/db/add_package.rs @@ -1,5 +1,6 @@ use ChrootBuilderResult; +use Metadata; use utils::source_path; use regex::Regex; @@ -38,6 +39,7 @@ pub fn add_package_into_database(conn: &Connection, &TargetKind::Lib(_) => true, _ => false, }; + let metadata = Metadata::from_package(pkg); let release_id: i32 = { let rows = try!(conn.query("SELECT id FROM releases WHERE crate_id = $1 AND version = $2", @@ -51,11 +53,11 @@ pub fn add_package_into_database(conn: &Connection, homepage_url, description, description_long, readme, authors, keywords, have_examples, downloads, files, doc_targets, is_library, doc_rustc_version, - documentation_url + documentation_url, default_target ) VALUES ( $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, - $20, $21, $22, $23, $24 + $20, $21, $22, $23, $24, $25 ) RETURNING id", &[&crate_id, @@ -81,7 +83,8 @@ pub fn add_package_into_database(conn: &Connection, &doc_targets.to_json(), &is_library, &res.rustc_version, - &pkg.manifest().metadata().documentation])); + &pkg.manifest().metadata().documentation, + &metadata.default_target])); // return id rows.get(0).get(0) @@ -108,7 +111,8 @@ pub fn add_package_into_database(conn: &Connection, doc_targets = $21, is_library = $22, doc_rustc_version = $23, - documentation_url = $24 + documentation_url = $24, + default_target = $25 WHERE crate_id = $1 AND version = $2", &[&crate_id, &format!("{}", pkg.manifest().version()), @@ -133,7 +137,8 @@ pub fn add_package_into_database(conn: &Connection, &doc_targets.to_json(), &is_library, &res.rustc_version, - &pkg.manifest().metadata().documentation])); + &pkg.manifest().metadata().documentation, + &metadata.default_target])); rows.get(0).get(0) } }; diff --git a/src/db/mod.rs b/src/db/mod.rs index f188dac40..8ed55e23f 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -112,6 +112,7 @@ pub fn create_tables(conn: &Connection) -> Result<(), Error> { files JSON, doc_targets JSON DEFAULT '[]', doc_rustc_version VARCHAR(100) NOT NULL, + default_target VARCHAR(100), UNIQUE (crate_id, version) )", "CREATE TABLE authors ( diff --git a/src/docbuilder/metadata.rs b/src/docbuilder/metadata.rs new file mode 100644 index 000000000..4ccabb7bb --- /dev/null +++ b/src/docbuilder/metadata.rs @@ -0,0 +1,167 @@ + +use std::path::Path; +use cargo::core::Package; +use toml::Value; + + +/// Metadata for custom builds +/// +/// You can customize docs.rs builds by defining `[package.metadata.docs.rs]` table in your +/// crates' `Cargo.toml`. +/// +/// An example metadata: +/// +/// ```text +/// [package] +/// name = "test" +/// +/// [package.metadata.docs.rs] +/// features = [ "feature1", "feature2" ] +/// all-features = true +/// no-default-features = true +/// default-target = "x86_64-unknown-linux-gnu" +/// rustdoc-args = [ "--example-rustdoc-arg" ] +/// dependencies = [ "example-system-dependency" ] +/// ``` +/// +/// You can define one or more fields in your `Cargo.toml`. +pub struct Metadata { + /// List of features docs.rs will build. + /// + /// By default, docs.rs will only build default features. + pub features: Option>, + + /// Set `all-features` to true if you want docs.rs to build all features for your crate + pub all_features: bool, + + /// Docs.rs will always build default features. + /// + /// Set `no-default-fatures` to `false` if you want to build only certain features. + pub no_default_features: bool, + + /// Docs.rs is running on `x86_64-unknown-linux-gnu` target system and default documentation + /// is always built on this target. You can change default target by setting this. + pub default_target: Option, + + /// List of command line arguments for `rustdoc`. + pub rustdoc_args: Option>, + + /// System dependencies. + /// + /// Docs.rs is running on a Debian jessie. + pub dependencies: Option>, +} + + + +impl Metadata { + pub fn from_package(pkg: &Package) -> Metadata { + Metadata::from_manifest(pkg.manifest_path()) + } + + pub fn from_manifest>(path: P) -> Metadata { + use std::fs::File; + use std::io::Read; + let mut f = match File::open(path) { + Ok(f) => f, + Err(_) => return Metadata::default(), + }; + let mut s = String::new(); + if let Err(_) = f.read_to_string(&mut s) { + return Metadata::default(); + } + Metadata::from_str(&s) + } + + + // This is similar to Default trait but it's private + fn default() -> Metadata { + Metadata { + features: None, + all_features: false, + no_default_features: false, + default_target: None, + rustdoc_args: None, + dependencies: None, + } + } + + + fn from_str(manifest: &str) -> Metadata { + let mut metadata = Metadata::default(); + + let manifest = match manifest.parse::() { + Ok(m) => m, + Err(_) => return metadata, + }; + + if let Some(table) = manifest.get("package").and_then(|p| p.as_table()) + .and_then(|p| p.get("metadata")).and_then(|p| p.as_table()) + .and_then(|p| p.get("docs")).and_then(|p| p.as_table()) + .and_then(|p| p.get("rs")).and_then(|p| p.as_table()) { + metadata.features = table.get("features").and_then(|f| f.as_array()) + .and_then(|f| f.iter().map(|v| v.as_str().map(|v| v.to_owned())).collect()); + metadata.no_default_features = table.get("no-default-features") + .and_then(|v| v.as_bool()).unwrap_or(metadata.no_default_features); + metadata.all_features = table.get("all-features") + .and_then(|v| v.as_bool()).unwrap_or(metadata.all_features); + metadata.default_target = table.get("default-target") + .and_then(|v| v.as_str()).map(|v| v.to_owned()); + metadata.rustdoc_args = table.get("rustdoc-args").and_then(|f| f.as_array()) + .and_then(|f| f.iter().map(|v| v.as_str().map(|v| v.to_owned())).collect()); + metadata.dependencies = table.get("dependencies").and_then(|f| f.as_array()) + .and_then(|f| f.iter().map(|v| v.as_str().map(|v| v.to_owned())).collect()); + } + + metadata + } +} + + + +#[cfg(test)] +mod test { + extern crate env_logger; + use super::Metadata; + + #[test] + fn test_cratesfyi_metadata() { + let _ = env_logger::init(); + let manifest = r#" + [package] + name = "test" + + [package.metadata.docs.rs] + features = [ "feature1", "feature2" ] + all-features = true + no-default-features = true + default-target = "x86_64-unknown-linux-gnu" + rustc-args = [ "--example-rustc-arg" ] + rustdoc-args = [ "--example-rustdoc-arg" ] + dependencies = [ "example-system-dependency" ] + "#; + + let metadata = Metadata::from_str(manifest); + + assert!(metadata.features.is_some()); + assert!(metadata.all_features == true); + assert!(metadata.no_default_features == true); + assert!(metadata.default_target.is_some()); + assert!(metadata.rustdoc_args.is_some()); + + let features = metadata.features.unwrap(); + assert_eq!(features.len(), 2); + assert_eq!(features[0], "feature1".to_owned()); + assert_eq!(features[1], "feature2".to_owned()); + + assert_eq!(metadata.default_target.unwrap(), "x86_64-unknown-linux-gnu".to_owned()); + + let rustdoc_args = metadata.rustdoc_args.unwrap(); + assert_eq!(rustdoc_args.len(), 1); + assert_eq!(rustdoc_args[0], "--example-rustdoc-arg".to_owned()); + + let dependencies = metadata.dependencies.unwrap(); + assert_eq!(dependencies.len(), 1); + assert_eq!(dependencies[0], "example-system-dependency".to_owned()); + } +} diff --git a/src/docbuilder/mod.rs b/src/docbuilder/mod.rs index 6e065a653..eb32d46c0 100644 --- a/src/docbuilder/mod.rs +++ b/src/docbuilder/mod.rs @@ -1,5 +1,6 @@ pub mod options; +pub mod metadata; mod chroot_builder; mod crates; mod queue; diff --git a/src/lib.rs b/src/lib.rs index d1633dbec..e4311567f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,10 +27,12 @@ extern crate libc; extern crate badge; extern crate crates_index_diff; extern crate git2; +extern crate toml; pub use self::docbuilder::DocBuilder; pub use self::docbuilder::ChrootBuilderResult; pub use self::docbuilder::options::DocBuilderOptions; +pub use self::docbuilder::metadata::Metadata; pub use self::web::start_web_server; pub mod error; diff --git a/src/utils/build_doc.rs b/src/utils/build_doc.rs index f16cc1e5c..bc9058de4 100644 --- a/src/utils/build_doc.rs +++ b/src/utils/build_doc.rs @@ -12,6 +12,8 @@ use cargo::util::{CargoResult, Config, human, Filesystem}; use cargo::sources::SourceConfigMap; use cargo::ops::{self, Packages, DefaultExecutor}; +use Metadata; + /// Builds documentation of a crate and version. /// @@ -43,13 +45,15 @@ pub fn build_doc(name: &str, vers: Option<&str>, target: Option<&str>) -> CargoR let target_dir = PathBuf::from(current_dir) .join(format!("{}-{}", pkg.manifest().name(), pkg.manifest().version())); + let metadata = Metadata::from_package(&pkg); + let opts = ops::CompileOptions { config: &config, jobs: None, target: target, - features: &[], - all_features: false, - no_default_features: false, + features: &metadata.features.unwrap_or(Vec::new()), + all_features: metadata.all_features, + no_default_features: metadata.no_default_features, spec: Packages::Packages(&[]), mode: ops::CompileMode::Doc { deps: false }, release: false, @@ -60,7 +64,7 @@ pub fn build_doc(name: &str, vers: Option<&str>, target: Option<&str>) -> CargoR &[], false, &[], false), target_rustc_args: None, - target_rustdoc_args: None, + target_rustdoc_args: metadata.rustdoc_args.as_ref().map(Vec::as_slice), }; let ws = try!(Workspace::ephemeral(pkg, &config, Some(Filesystem::new(target_dir)), false));