Skip to content

Commit 0cd65ec

Browse files
committed
Add support for CARGO_TARGET_DIR_PREFIX
This change adds support for a new environment variable, CARGO_TARGET_DIR_PREFIX, to cargo. This variable, when set, is treated as a prefix to the target directory. Note that support for the functionality behind this variable is not trivial to implement with the current design. In particular, we wanted to stick as close to the existing CARGO_TARGET_DIR logic. However, the Config in which it is implemented really does not know anything about the directory of the particular crate we concerned with. As a quick work around to this problem, we just pass in the path to the Cargo.toml from the "upper layer". That works, but ultimately it would be better to make the other layer handle the CARGO_TARGET_DIR_PREFIX logic. This change addresses rust-lang#5544. TODO: Definitely not finished. This patch needs more tests and may need additional config.toml support (?). TODO: There is also the potential for a permission related problems. E.g., when user root compiles something below /tmp/ and then user nobody tries to do the same the resulting directory ${CARGO_TARGET_DIR_PREFIX}/tmp/ may be owned by root, causing the build for nobody to fail with a permission denied error.
1 parent c4fcfb7 commit 0cd65ec

File tree

6 files changed

+83
-6
lines changed

6 files changed

+83
-6
lines changed

src/cargo/core/workspace.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ impl<'cfg> Workspace<'cfg> {
138138
/// root and all member packages. It will then validate the workspace
139139
/// before returning it, so `Ok` is only returned for valid workspaces.
140140
pub fn new(manifest_path: &Path, config: &'cfg Config) -> CargoResult<Workspace<'cfg>> {
141-
let target_dir = config.target_dir()?;
141+
let target_dir = config.target_dir(manifest_path)?;
142142

143143
let mut ws = Workspace {
144144
config,
@@ -198,13 +198,13 @@ impl<'cfg> Workspace<'cfg> {
198198
{
199199
let key = ws.current_manifest.parent().unwrap();
200200
let id = package.package_id();
201-
let package = MaybePackage::Package(package);
202-
ws.packages.packages.insert(key.to_path_buf(), package);
203201
ws.target_dir = if let Some(dir) = target_dir {
204202
Some(dir)
205203
} else {
206-
ws.config.target_dir()?
204+
ws.config.target_dir(package.manifest_path())?
207205
};
206+
let package = MaybePackage::Package(package);
207+
ws.packages.packages.insert(key.to_path_buf(), package);
208208
ws.members.push(ws.current_manifest.clone());
209209
ws.member_ids.insert(id);
210210
ws.default_members.push(ws.current_manifest.clone());

src/cargo/ops/cargo_install.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ fn install_one(
204204
let mut needs_cleanup = false;
205205
let overidden_target_dir = if source_id.is_path() {
206206
None
207-
} else if let Some(dir) = config.target_dir()? {
207+
} else if let Some(dir) = config.target_dir(pkg.manifest_path())? {
208208
Some(dir)
209209
} else if let Ok(td) = TempFileBuilder::new().prefix("cargo-install").tempdir() {
210210
let p = td.path().to_owned();

src/cargo/util/config.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,11 +315,26 @@ impl Config {
315315
&self.cwd
316316
}
317317

318-
pub fn target_dir(&self) -> CargoResult<Option<Filesystem>> {
318+
pub fn target_dir(&self, manifest: impl Into<PathBuf>) -> CargoResult<Option<Filesystem>> {
319319
if let Some(ref dir) = self.target_dir {
320320
Ok(Some(dir.clone()))
321321
} else if let Some(dir) = env::var_os("CARGO_TARGET_DIR") {
322322
Ok(Some(Filesystem::new(self.cwd.join(dir))))
323+
} else if let Some(dir) = env::var_os("CARGO_TARGET_DIR_PREFIX") {
324+
let prefix = Path::new(&dir);
325+
if !prefix.is_absolute() {
326+
failure::bail!("CARGO_TARGET_DIR_PREFIX must describe an absolute path");
327+
}
328+
let mut manifest = manifest.into();
329+
let result = manifest.pop();
330+
assert!(result);
331+
332+
match manifest.strip_prefix("/") {
333+
Ok(dir) => Ok(Some(Filesystem::new(prefix.join(&dir).join("target")))),
334+
// FIXME: This logic is probably not safe on Windows. Not sure how
335+
// to make a path relative there.
336+
Err(_) => failure::bail!("Current directory must be an absolute path"),
337+
}
323338
} else if let Some(val) = self.get_path("build.target-dir")? {
324339
let val = self.cwd.join(val.val);
325340
Ok(Some(Filesystem::new(val)))

src/doc/src/reference/environment-variables.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ system:
1313
checkouts of crates. By default these are stored under `$HOME/.cargo`, but
1414
this variable overrides the location of this directory. Once a crate is cached
1515
it is not removed by the clean command.
16+
* `CARGO_TARGET_DIR_PREFIX` — Prefix to the location where to place all
17+
generated artifacts. The current working directory will be appended to this
18+
prefix to form the final path for generated artifacts. Note that
19+
`CARGO_TARGET_DIR`, if set, takes precedence over this variable.
1620
* `CARGO_TARGET_DIR` — Location of where to place all generated artifacts,
1721
relative to the current working directory.
1822
* `RUSTC` — Instead of running `rustc`, Cargo will execute this specified

tests/testsuite/build.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3017,6 +3017,63 @@ fn explicit_color_config_is_propagated_to_rustc() {
30173017
.run();
30183018
}
30193019

3020+
#[test]
3021+
fn custom_target_dir_prefix() {
3022+
fn test(cwd: &str) {
3023+
let tmpdir = tempfile::Builder::new()
3024+
.tempdir()
3025+
.unwrap()
3026+
.path()
3027+
.to_path_buf();
3028+
3029+
let p = project()
3030+
.file(
3031+
"Cargo.toml",
3032+
r#"
3033+
[package]
3034+
name = "foo"
3035+
version = "0.0.1"
3036+
authors = []
3037+
"#,
3038+
)
3039+
.file("src/main.rs", "fn main() {}")
3040+
.build();
3041+
3042+
let root = p.root();
3043+
let root_suffix = root.strip_prefix("/").unwrap();
3044+
let exe_name = format!("foo{}", env::consts::EXE_SUFFIX);
3045+
3046+
p.cargo("build")
3047+
.env("CARGO_TARGET_DIR_PREFIX", tmpdir.clone())
3048+
.cwd(p.root().join(cwd))
3049+
.run();
3050+
3051+
assert!(
3052+
tmpdir
3053+
.clone()
3054+
.join(root_suffix)
3055+
.join("target/debug")
3056+
.join(&exe_name)
3057+
.is_file()
3058+
);
3059+
assert!(!&p.root().join("target/debug").join(&exe_name).is_file());
3060+
3061+
p.cargo("build").run();
3062+
assert!(
3063+
tmpdir
3064+
.clone()
3065+
.join(root_suffix)
3066+
.join("target/debug")
3067+
.join(&exe_name)
3068+
.is_file()
3069+
);
3070+
assert!(&p.root().join("target/debug").join(&exe_name).is_file())
3071+
};
3072+
3073+
test(".");
3074+
test("src");
3075+
}
3076+
30203077
#[test]
30213078
fn compiler_json_error_format() {
30223079
let p = project()

tests/testsuite/support/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1688,6 +1688,7 @@ fn _process(t: &OsStr) -> cargo::util::ProcessBuilder {
16881688
.env_remove("GIT_AUTHOR_EMAIL")
16891689
.env_remove("GIT_COMMITTER_NAME")
16901690
.env_remove("GIT_COMMITTER_EMAIL")
1691+
.env_remove("CARGO_TARGET_DIR_PREFIX") // we assume no prefix
16911692
.env_remove("CARGO_TARGET_DIR") // we assume 'target'
16921693
.env_remove("MSYSTEM"); // assume cmd.exe everywhere on windows
16931694
p

0 commit comments

Comments
 (0)