// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use target::*; use package_id::PkgId; use std::path::Path; use std::{os, str}; use context::*; use crate::Crate; use messages::*; use source_control::{git_clone, git_clone_general}; use path_util::pkgid_src_in_workspace; use util::compile_crate; // An enumeration of the unpacked source of a package workspace. // This contains a list of files found in the source workspace. pub struct PkgSrc { root: Path, // root of where the package source code lives dst_dir: Path, // directory where we will put the compiled output id: PkgId, libs: ~[Crate], mains: ~[Crate], tests: ~[Crate], benchs: ~[Crate], } condition! { build_err: (~str) -> (); } impl PkgSrc { pub fn new(src_dir: &Path, dst_dir: &Path, id: &PkgId) -> PkgSrc { PkgSrc { root: (*src_dir).clone(), dst_dir: (*dst_dir).clone(), id: (*id).clone(), libs: ~[], mains: ~[], tests: ~[], benchs: ~[] } } fn check_dir(&self) -> Path { use conditions::nonexistent_package::cond; debug!("Pushing onto root: %s | %s", self.id.remote_path.to_str(), self.root.to_str()); let dir; let dirs = pkgid_src_in_workspace(&self.id, &self.root); debug!("Checking dirs: %?", dirs); let path = dirs.iter().find_(|&d| os::path_exists(d)); match path { Some(d) => dir = (*d).clone(), None => dir = match self.fetch_git() { None => cond.raise((self.id.clone(), ~"supplied path for package dir does not \ exist, and couldn't interpret it as a URL fragment")), Some(d) => d } } if !os::path_is_dir(&dir) { cond.raise((self.id.clone(), ~"supplied path for package dir is a \ non-directory")); } dir } /// Try interpreting self's package id as a git repository, and try /// fetching it and caching it in a local directory. Return the cached directory /// if this was successful, None otherwise. Similarly, if the package id /// refers to a git repo on the local version, also check it out. /// (right now we only support git) pub fn fetch_git(&self) -> Option { let mut local = self.root.push("src"); local = local.push(self.id.to_str()); // Git can't clone into a non-empty directory os::remove_dir_recursive(&local); debug!("Checking whether %s exists locally. Cwd = %s, does it? %?", self.id.local_path.to_str(), os::getcwd().to_str(), os::path_exists(&*self.id.local_path)); if os::path_exists(&*self.id.local_path) { debug!("%s exists locally! Cloning it into %s", self.id.local_path.to_str(), local.to_str()); git_clone(&*self.id.local_path, &local, &self.id.version); return Some(local); } let url = fmt!("https://%s", self.id.remote_path.to_str()); note(fmt!("Fetching package: git clone %s %s [version=%s]", url, local.to_str(), self.id.version.to_str())); if git_clone_general(url, &local, &self.id.version) { Some(local) } else { None } } // If a file named "pkg.rs" in the current directory exists, // return the path for it. Otherwise, None pub fn package_script_option(&self, cwd: &Path) -> Option { let maybe_path = cwd.push("pkg.rs"); if os::path_exists(&maybe_path) { Some(maybe_path) } else { None } } /// True if the given path's stem is self's pkg ID's stem /// or if the pkg ID's stem is and the given path's /// stem is foo /// Requires that dashes in p have already been normalized to /// underscores fn stem_matches(&self, p: &Path) -> bool { let self_id = self.id.local_path.filestem(); if self_id == p.filestem() { return true; } else { for pth in self_id.iter() { if pth.starts_with("rust_") // because p is already normalized && match p.filestem() { Some(s) => str::eq_slice(s, pth.slice(5, pth.len())), None => false } { return true; } } } false } fn push_crate(cs: &mut ~[Crate], prefix: uint, p: &Path) { assert!(p.components.len() > prefix); let mut sub = Path(""); for c in p.components.slice(prefix, p.components.len()).iter() { sub = sub.push(*c); } debug!("found crate %s", sub.to_str()); cs.push(Crate::new(&sub)); } /// Infers crates to build. Called only in the case where there /// is no custom build logic pub fn find_crates(&mut self) { use conditions::missing_pkg_files::cond; let dir = self.check_dir(); debug!("Called check_dir, I'm in %s", dir.to_str()); let prefix = dir.components.len(); debug!("Matching against %?", self.id.local_path.filestem()); do os::walk_dir(&dir) |pth| { match pth.filename() { Some(~"lib.rs") => PkgSrc::push_crate(&mut self.libs, prefix, pth), Some(~"main.rs") => PkgSrc::push_crate(&mut self.mains, prefix, pth), Some(~"test.rs") => PkgSrc::push_crate(&mut self.tests, prefix, pth), Some(~"bench.rs") => PkgSrc::push_crate(&mut self.benchs, prefix, pth), _ => () } true }; if self.libs.is_empty() && self.mains.is_empty() && self.tests.is_empty() && self.benchs.is_empty() { note("Couldn't infer any crates to build.\n\ Try naming a crate `main.rs`, `lib.rs`, \ `test.rs`, or `bench.rs`."); cond.raise(self.id.clone()); } debug!("found %u libs, %u mains, %u tests, %u benchs", self.libs.len(), self.mains.len(), self.tests.len(), self.benchs.len()) } fn build_crates(&self, ctx: &Ctx, dst_dir: &Path, src_dir: &Path, crates: &[Crate], cfgs: &[~str], what: OutputType) { for crate in crates.iter() { let path = &src_dir.push_rel(&crate.file).normalize(); note(fmt!("build_crates: compiling %s", path.to_str())); note(fmt!("build_crates: destination dir is %s", dst_dir.to_str())); let result = compile_crate(ctx, &self.id, path, dst_dir, crate.flags, crate.cfgs + cfgs, false, what); if !result { build_err::cond.raise(fmt!("build failure on %s", path.to_str())); } debug!("Result of compiling %s was %?", path.to_str(), result); } } pub fn build(&self, ctx: &Ctx, dst_dir: Path, cfgs: ~[~str]) { let dir = self.check_dir(); debug!("Building libs in %s", dir.to_str()); self.build_crates(ctx, &dst_dir, &dir, self.libs, cfgs, Lib); debug!("Building mains"); self.build_crates(ctx, &dst_dir, &dir, self.mains, cfgs, Main); debug!("Building tests"); self.build_crates(ctx, &dst_dir, &dir, self.tests, cfgs, Test); debug!("Building benches"); self.build_crates(ctx, &dst_dir, &dir, self.benchs, cfgs, Bench); } }