|
| 1 | +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT |
| 2 | +// file at the top-level directory of this distribution and at |
| 3 | +// http://rust-lang.org/COPYRIGHT. |
| 4 | +// |
| 5 | +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
| 6 | +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| 7 | +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
| 8 | +// option. This file may not be copied, modified, or distributed |
| 9 | +// except according to those terms. |
| 10 | + |
| 11 | +use target::*; |
| 12 | +use package_id::PkgId; |
| 13 | +use core::path::Path; |
| 14 | +use core::option::*; |
| 15 | +use core::{os, run, str, vec}; |
| 16 | +use context::*; |
| 17 | +use crate::Crate; |
| 18 | +use path_util::pkgid_src_in_workspace; |
| 19 | +use util::{compile_crate, note}; |
| 20 | +use version::{ExactRevision, SemanticVersion, NoVersion}; |
| 21 | + |
| 22 | +// An enumeration of the unpacked source of a package workspace. |
| 23 | +// This contains a list of files found in the source workspace. |
| 24 | +pub struct PkgSrc { |
| 25 | + root: Path, // root of where the package source code lives |
| 26 | + dst_dir: Path, // directory where we will put the compiled output |
| 27 | + id: PkgId, |
| 28 | + libs: ~[Crate], |
| 29 | + mains: ~[Crate], |
| 30 | + tests: ~[Crate], |
| 31 | + benchs: ~[Crate], |
| 32 | +} |
| 33 | + |
| 34 | +condition! { |
| 35 | + build_err: (~str) -> (); |
| 36 | +} |
| 37 | + |
| 38 | +impl PkgSrc { |
| 39 | + |
| 40 | + pub fn new(src_dir: &Path, dst_dir: &Path, |
| 41 | + id: &PkgId) -> PkgSrc { |
| 42 | + PkgSrc { |
| 43 | + root: copy *src_dir, |
| 44 | + dst_dir: copy *dst_dir, |
| 45 | + id: copy *id, |
| 46 | + libs: ~[], |
| 47 | + mains: ~[], |
| 48 | + tests: ~[], |
| 49 | + benchs: ~[] |
| 50 | + } |
| 51 | + } |
| 52 | + |
| 53 | + |
| 54 | + fn check_dir(&self) -> Path { |
| 55 | + use conditions::nonexistent_package::cond; |
| 56 | + |
| 57 | + debug!("Pushing onto root: %s | %s", self.id.remote_path.to_str(), |
| 58 | + self.root.to_str()); |
| 59 | + let dir; |
| 60 | + let dirs = pkgid_src_in_workspace(&self.id, &self.root); |
| 61 | + debug!("Checking dirs: %?", dirs); |
| 62 | + let path = dirs.find(|d| os::path_exists(d)); |
| 63 | + match path { |
| 64 | + Some(d) => dir = d, |
| 65 | + None => dir = match self.fetch_git() { |
| 66 | + None => cond.raise((copy self.id, ~"supplied path for package dir does not \ |
| 67 | + exist, and couldn't interpret it as a URL fragment")), |
| 68 | + Some(d) => d |
| 69 | + } |
| 70 | + } |
| 71 | + if !os::path_is_dir(&dir) { |
| 72 | + cond.raise((copy self.id, ~"supplied path for package dir is a \ |
| 73 | + non-directory")); |
| 74 | + } |
| 75 | + |
| 76 | + dir |
| 77 | + } |
| 78 | + |
| 79 | + /// Try interpreting self's package id as a remote package, and try |
| 80 | + /// fetching it and caching it in a local directory. Return the cached directory |
| 81 | + /// if this was successful, None otherwise |
| 82 | + /// (right now we only support git) |
| 83 | + pub fn fetch_git(&self) -> Option<Path> { |
| 84 | + |
| 85 | + let mut local = self.root.push("src"); |
| 86 | + local = local.push(self.id.to_str()); |
| 87 | + // Git can't clone into a non-empty directory |
| 88 | + os::remove_dir_recursive(&local); |
| 89 | + |
| 90 | + let url = fmt!("https://%s", self.id.remote_path.to_str()); |
| 91 | + let branch_args = match self.id.version { |
| 92 | + NoVersion => ~[], |
| 93 | + ExactRevision(ref s) => ~[~"--branch", copy *s], |
| 94 | + SemanticVersion(ref s) => ~[~"--branch", s.to_str()] |
| 95 | + }; |
| 96 | + |
| 97 | + |
| 98 | + note(fmt!("git clone %s %s %?", url, local.to_str(), branch_args)); |
| 99 | + |
| 100 | + if run::process_output("git", |
| 101 | + ~[~"clone", copy url, local.to_str()] + branch_args).status != 0 { |
| 102 | + note(fmt!("fetching %s failed: can't clone repository", url)); |
| 103 | + None |
| 104 | + } |
| 105 | + else { |
| 106 | + Some(local) |
| 107 | + } |
| 108 | + } |
| 109 | + |
| 110 | + |
| 111 | + // If a file named "pkg.rs" in the current directory exists, |
| 112 | + // return the path for it. Otherwise, None |
| 113 | + pub fn package_script_option(&self, cwd: &Path) -> Option<Path> { |
| 114 | + let maybe_path = cwd.push("pkg.rs"); |
| 115 | + if os::path_exists(&maybe_path) { |
| 116 | + Some(maybe_path) |
| 117 | + } |
| 118 | + else { |
| 119 | + None |
| 120 | + } |
| 121 | + } |
| 122 | + |
| 123 | + /// True if the given path's stem is self's pkg ID's stem |
| 124 | + /// or if the pkg ID's stem is <rust-foo> and the given path's |
| 125 | + /// stem is foo |
| 126 | + /// Requires that dashes in p have already been normalized to |
| 127 | + /// underscores |
| 128 | + fn stem_matches(&self, p: &Path) -> bool { |
| 129 | + let self_id = self.id.local_path.filestem(); |
| 130 | + if self_id == p.filestem() { |
| 131 | + return true; |
| 132 | + } |
| 133 | + else { |
| 134 | + for self_id.each |pth| { |
| 135 | + if pth.starts_with("rust_") // because p is already normalized |
| 136 | + && match p.filestem() { |
| 137 | + Some(s) => str::eq_slice(s, pth.slice(5, pth.len())), |
| 138 | + None => false |
| 139 | + } { return true; } |
| 140 | + } |
| 141 | + } |
| 142 | + false |
| 143 | + } |
| 144 | + |
| 145 | + fn push_crate(cs: &mut ~[Crate], prefix: uint, p: &Path) { |
| 146 | + assert!(p.components.len() > prefix); |
| 147 | + let mut sub = Path(""); |
| 148 | + for vec::slice(p.components, prefix, |
| 149 | + p.components.len()).each |c| { |
| 150 | + sub = sub.push(*c); |
| 151 | + } |
| 152 | + debug!("found crate %s", sub.to_str()); |
| 153 | + cs.push(Crate::new(&sub)); |
| 154 | + } |
| 155 | + |
| 156 | + /// Infers crates to build. Called only in the case where there |
| 157 | + /// is no custom build logic |
| 158 | + pub fn find_crates(&mut self) { |
| 159 | + use conditions::missing_pkg_files::cond; |
| 160 | + |
| 161 | + let dir = self.check_dir(); |
| 162 | + debug!("Called check_dir, I'm in %s", dir.to_str()); |
| 163 | + let prefix = dir.components.len(); |
| 164 | + debug!("Matching against %?", self.id.local_path.filestem()); |
| 165 | + for os::walk_dir(&dir) |pth| { |
| 166 | + match pth.filename() { |
| 167 | + Some(~"lib.rs") => PkgSrc::push_crate(&mut self.libs, |
| 168 | + prefix, |
| 169 | + pth), |
| 170 | + Some(~"main.rs") => PkgSrc::push_crate(&mut self.mains, |
| 171 | + prefix, |
| 172 | + pth), |
| 173 | + Some(~"test.rs") => PkgSrc::push_crate(&mut self.tests, |
| 174 | + prefix, |
| 175 | + pth), |
| 176 | + Some(~"bench.rs") => PkgSrc::push_crate(&mut self.benchs, |
| 177 | + prefix, |
| 178 | + pth), |
| 179 | + _ => () |
| 180 | + } |
| 181 | + } |
| 182 | + |
| 183 | + if self.libs.is_empty() && self.mains.is_empty() |
| 184 | + && self.tests.is_empty() && self.benchs.is_empty() { |
| 185 | + |
| 186 | + note(~"Couldn't infer any crates to build.\n\ |
| 187 | + Try naming a crate `main.rs`, `lib.rs`, \ |
| 188 | + `test.rs`, or `bench.rs`."); |
| 189 | + cond.raise(copy self.id); |
| 190 | + } |
| 191 | + |
| 192 | + debug!("found %u libs, %u mains, %u tests, %u benchs", |
| 193 | + self.libs.len(), |
| 194 | + self.mains.len(), |
| 195 | + self.tests.len(), |
| 196 | + self.benchs.len()) |
| 197 | + } |
| 198 | + |
| 199 | + fn build_crates(&self, |
| 200 | + ctx: &Ctx, |
| 201 | + dst_dir: &Path, |
| 202 | + src_dir: &Path, |
| 203 | + crates: &[Crate], |
| 204 | + cfgs: &[~str], |
| 205 | + what: OutputType) { |
| 206 | + for crates.each |&crate| { |
| 207 | + let path = &src_dir.push_rel(&crate.file).normalize(); |
| 208 | + note(fmt!("build_crates: compiling %s", path.to_str())); |
| 209 | + note(fmt!("build_crates: destination dir is %s", dst_dir.to_str())); |
| 210 | + |
| 211 | + let result = compile_crate(ctx, |
| 212 | + &self.id, |
| 213 | + path, |
| 214 | + dst_dir, |
| 215 | + crate.flags, |
| 216 | + crate.cfgs + cfgs, |
| 217 | + false, |
| 218 | + what); |
| 219 | + if !result { |
| 220 | + build_err::cond.raise(fmt!("build failure on %s", |
| 221 | + path.to_str())); |
| 222 | + } |
| 223 | + debug!("Result of compiling %s was %?", |
| 224 | + path.to_str(), result); |
| 225 | + } |
| 226 | + } |
| 227 | + |
| 228 | + pub fn build(&self, ctx: &Ctx, dst_dir: Path, cfgs: ~[~str]) { |
| 229 | + let dir = self.check_dir(); |
| 230 | + debug!("Building libs in %s", dir.to_str()); |
| 231 | + self.build_crates(ctx, &dst_dir, &dir, self.libs, cfgs, Lib); |
| 232 | + debug!("Building mains"); |
| 233 | + self.build_crates(ctx, &dst_dir, &dir, self.mains, cfgs, Main); |
| 234 | + debug!("Building tests"); |
| 235 | + self.build_crates(ctx, &dst_dir, &dir, self.tests, cfgs, Test); |
| 236 | + debug!("Building benches"); |
| 237 | + self.build_crates(ctx, &dst_dir, &dir, self.benchs, cfgs, Bench); |
| 238 | + } |
| 239 | +} |
0 commit comments