Skip to content

Commit d92b435

Browse files
committed
rustpkg: Accept package IDs like github.com/foo/bar#0.3
If the package ID is of the form s#v, where v is a valid version string, fetch tag v of that package.
1 parent 53b8352 commit d92b435

File tree

8 files changed

+542
-341
lines changed

8 files changed

+542
-341
lines changed

src/librustpkg/crate.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
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 core::path::Path;
12+
use core::vec;
13+
14+
/// A crate is a unit of Rust code to be compiled into a binary or library
15+
pub struct Crate {
16+
file: Path,
17+
flags: ~[~str],
18+
cfgs: ~[~str]
19+
}
20+
21+
impl Crate {
22+
23+
pub fn new(p: &Path) -> Crate {
24+
Crate {
25+
file: copy *p,
26+
flags: ~[],
27+
cfgs: ~[]
28+
}
29+
}
30+
31+
fn flag(&self, flag: ~str) -> Crate {
32+
Crate {
33+
flags: vec::append(copy self.flags, [flag]),
34+
.. copy *self
35+
}
36+
}
37+
38+
fn flags(&self, flags: ~[~str]) -> Crate {
39+
Crate {
40+
flags: vec::append(copy self.flags, flags),
41+
.. copy *self
42+
}
43+
}
44+
45+
fn cfg(&self, cfg: ~str) -> Crate {
46+
Crate {
47+
cfgs: vec::append(copy self.cfgs, [cfg]),
48+
.. copy *self
49+
}
50+
}
51+
52+
fn cfgs(&self, cfgs: ~[~str]) -> Crate {
53+
Crate {
54+
cfgs: vec::append(copy self.cfgs, cfgs),
55+
.. copy *self
56+
}
57+
}
58+
}

src/librustpkg/package_id.rs

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,8 @@
99
// except according to those terms.
1010

1111
pub use package_path::{RemotePath, LocalPath, normalize, hash};
12-
use extra::semver;
1312
use core::prelude::*;
14-
use core::result;
15-
use core::prelude::*;
16-
use version::{default_version, try_getting_version, Version};
13+
use version::{try_getting_version, Version, NoVersion, split_version};
1714

1815
/// Path-fragment identifier of a package such as
1916
/// 'github.com/graydon/test'; path must be a relative
@@ -38,6 +35,21 @@ impl PkgId {
3835
pub fn new(s: &str) -> PkgId {
3936
use conditions::bad_pkg_id::cond;
4037

38+
let mut given_version = None;
39+
40+
// Did the user request a specific version?
41+
let s = match split_version(s) {
42+
Some((path, v)) => {
43+
debug!("s = %s, path = %s, v = %s", s, path, v.to_str());
44+
given_version = Some(v);
45+
path
46+
}
47+
None => {
48+
debug!("%s has no explicit version", s);
49+
s
50+
}
51+
};
52+
4153
let p = Path(s);
4254
if p.is_absolute {
4355
return cond.raise((p, ~"absolute pkgid"));
@@ -49,9 +61,12 @@ impl PkgId {
4961
let local_path = normalize(copy remote_path);
5062
let short_name = (copy local_path).filestem().expect(fmt!("Strange path! %s", s));
5163

52-
let version = match try_getting_version(remote_path) {
64+
let version = match given_version {
5365
Some(v) => v,
54-
None => default_version()
66+
None => match try_getting_version(&remote_path) {
67+
Some(v) => v,
68+
None => NoVersion
69+
}
5570
};
5671

5772
PkgId {
@@ -69,13 +84,17 @@ impl PkgId {
6984
}
7085

7186
pub fn short_name_with_version(&self) -> ~str {
72-
fmt!("%s-%s", self.short_name, self.version.to_str())
87+
fmt!("%s%s", self.short_name, self.version.to_str())
7388
}
7489
}
7590

7691
impl ToStr for PkgId {
7792
fn to_str(&self) -> ~str {
93+
let maybe_dash = match self.version {
94+
NoVersion => "",
95+
_ => "-"
96+
};
7897
// should probably use the filestem and not the whole path
79-
fmt!("%s-%s", self.local_path.to_str(), self.version.to_str())
98+
fmt!("%s%s%s", self.local_path.to_str(), maybe_dash, self.version.to_str())
8099
}
81100
}

src/librustpkg/package_source.rs

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
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+
}

src/librustpkg/path_util.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
use core::prelude::*;
1414
pub use package_path::{RemotePath, LocalPath};
15-
pub use package_id::{PkgId, Version};
15+
pub use package_id::PkgId;
1616
pub use target::{OutputType, Main, Lib, Test, Bench, Target, Build, Install};
1717
use core::libc::consts::os::posix88::{S_IRUSR, S_IWUSR, S_IXUSR};
1818
use core::os::mkdir_recursive;
@@ -210,11 +210,17 @@ pub fn target_executable_in_workspace(pkgid: &PkgId, workspace: &Path) -> Path {
210210
}
211211

212212

213-
/// Returns the executable that would be installed for <pkgid>
214-
/// in <workspace>
213+
/// Returns the installed path for <built_library> in <workspace>
215214
/// As a side effect, creates the lib-dir if it doesn't exist
216-
pub fn target_library_in_workspace(pkgid: &PkgId, workspace: &Path) -> Path {
217-
target_file_in_workspace(pkgid, workspace, Lib, Install)
215+
pub fn target_library_in_workspace(workspace: &Path,
216+
built_library: &Path) -> Path {
217+
use conditions::bad_path::cond;
218+
let result = workspace.push("lib");
219+
if !os::path_exists(&result) && !mkdir_recursive(&result, u_rwx) {
220+
cond.raise((copy result, ~"I couldn't create the library directory"));
221+
}
222+
result.push(built_library.filename().expect(fmt!("I don't know how to treat %s as a library",
223+
built_library.to_str())))
218224
}
219225

220226
/// Returns the test executable that would be installed for <pkgid>

0 commit comments

Comments
 (0)