Skip to content

Commit d244bf5

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 8f40fc5 commit d244bf5

6 files changed

Lines changed: 89 additions & 7 deletions

File tree

src/cargo/core/workspace.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,6 @@ impl<'gctx> Workspace<'gctx> {
208208
/// before returning it, so `Ok` is only returned for valid workspaces.
209209
pub fn new(manifest_path: &Path, gctx: &'gctx GlobalContext) -> CargoResult<Workspace<'gctx>> {
210210
let mut ws = Workspace::new_default(manifest_path.to_path_buf(), gctx);
211-
ws.target_dir = gctx.target_dir()?;
212211

213212
if manifest_path.is_relative() {
214213
bail!(
@@ -219,6 +218,12 @@ impl<'gctx> Workspace<'gctx> {
219218
ws.root_manifest = ws.find_root(manifest_path)?;
220219
}
221220

221+
if let Some(ref root_manifest) = ws.root_manifest {
222+
ws.target_dir = gctx.target_dir(root_manifest)?;
223+
} else {
224+
ws.target_dir = gctx.target_dir(manifest_path)?;
225+
}
226+
222227
ws.custom_metadata = ws
223228
.load_workspace_config()?
224229
.and_then(|cfg| cfg.custom_metadata);
@@ -273,13 +278,13 @@ impl<'gctx> Workspace<'gctx> {
273278
ws.require_optional_deps = require_optional_deps;
274279
let key = ws.current_manifest.parent().unwrap();
275280
let id = package.package_id();
276-
let package = MaybePackage::Package(package);
277-
ws.packages.packages.insert(key.to_path_buf(), package);
278281
ws.target_dir = if let Some(dir) = target_dir {
279282
Some(dir)
280283
} else {
281-
ws.gctx.target_dir()?
284+
ws.gctx.target_dir(package.manifest_path())?
282285
};
286+
let package = MaybePackage::Package(package);
287+
ws.packages.packages.insert(key.to_path_buf(), package);
283288
ws.members.push(ws.current_manifest.clone());
284289
ws.member_ids.insert(id);
285290
ws.default_members.push(ws.current_manifest.clone());

src/cargo/ops/cargo_install.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,8 @@ impl<'gctx> InstallablePackage<'gctx> {
305305
let mut td_opt = None;
306306
let mut needs_cleanup = false;
307307
if !self.source_id.is_path() {
308-
let target_dir = if let Some(dir) = self.gctx.target_dir()? {
308+
let manifest_path = self.pkg.manifest_path().to_path_buf();
309+
let target_dir = if let Some(dir) = self.gctx.target_dir(manifest_path)? {
309310
dir
310311
} else if let Ok(td) = TempFileBuilder::new().prefix("cargo-install").tempdir() {
311312
let p = td.path().to_owned();

src/cargo/util/context/mod.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -604,7 +604,7 @@ impl GlobalContext {
604604
/// Returns `None` if the user has not chosen an explicit directory.
605605
///
606606
/// Callers should prefer `Workspace::target_dir` instead.
607-
pub fn target_dir(&self) -> CargoResult<Option<Filesystem>> {
607+
pub fn target_dir(&self, manifest: impl Into<PathBuf>) -> CargoResult<Option<Filesystem>> {
608608
if let Some(dir) = &self.target_dir {
609609
Ok(Some(dir.clone()))
610610
} else if let Some(dir) = self.get_env_os("CARGO_TARGET_DIR") {
@@ -617,6 +617,21 @@ impl GlobalContext {
617617
}
618618

619619
Ok(Some(Filesystem::new(self.cwd.join(dir))))
620+
} else if let Some(dir) = env::var_os("CARGO_TARGET_DIR_PREFIX") {
621+
let prefix = Path::new(&dir);
622+
if !prefix.is_absolute() {
623+
bail!("CARGO_TARGET_DIR_PREFIX must describe an absolute path");
624+
}
625+
let mut manifest = manifest.into();
626+
let result = manifest.pop();
627+
assert!(result);
628+
629+
match manifest.strip_prefix("/") {
630+
Ok(dir) => Ok(Some(Filesystem::new(prefix.join(&dir).join("target")))),
631+
// FIXME: This logic is probably not safe on Windows. Not sure how
632+
// to make a path relative there.
633+
Err(_) => bail!("Current directory must be an absolute path"),
634+
}
620635
} else if let Some(val) = &self.build_config()?.target_dir {
621636
let path = val.resolve_path(self);
622637

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ system:
2020
location of this directory. Once a crate is cached it is not removed by the
2121
clean command.
2222
For more details refer to the [guide](../guide/cargo-home.md).
23+
* `CARGO_TARGET_DIR_PREFIX` — Prefix to the location where to place all
24+
generated artifacts. The current working directory will be appended to this
25+
prefix to form the final path for generated artifacts. Note that
26+
`CARGO_TARGET_DIR`, if set, takes precedence over this variable.
2327
* `CARGO_TARGET_DIR` --- Location of where to place all generated artifacts,
2428
relative to the current working directory. See [`build.target-dir`] to set
2529
via config.

tests/testsuite/build.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4126,6 +4126,63 @@ fn panic_abort_compiles_with_panic_abort() {
41264126
}
41274127

41284128
#[allow(deprecated)]
4129+
#[cargo_test]
4130+
fn custom_target_dir_prefix() {
4131+
fn test(cwd: &str) {
4132+
let tmpdir = tempfile::Builder::new()
4133+
.tempdir()
4134+
.unwrap()
4135+
.path()
4136+
.to_path_buf();
4137+
4138+
let p = project()
4139+
.file(
4140+
"Cargo.toml",
4141+
r#"
4142+
[package]
4143+
name = "foo"
4144+
version = "0.0.1"
4145+
authors = []
4146+
"#,
4147+
)
4148+
.file("src/main.rs", "fn main() {}")
4149+
.build();
4150+
4151+
let root = p.root();
4152+
let root_suffix = root.strip_prefix("/").unwrap();
4153+
let exe_name = format!("foo{}", env::consts::EXE_SUFFIX);
4154+
4155+
p.cargo("build")
4156+
.env("CARGO_TARGET_DIR_PREFIX", tmpdir.clone())
4157+
.cwd(p.root().join(cwd))
4158+
.run();
4159+
4160+
assert!(
4161+
tmpdir
4162+
.clone()
4163+
.join(root_suffix)
4164+
.join("target/debug")
4165+
.join(&exe_name)
4166+
.is_file()
4167+
);
4168+
assert!(!&p.root().join("target/debug").join(&exe_name).is_file());
4169+
4170+
p.cargo("build").run();
4171+
assert!(
4172+
tmpdir
4173+
.clone()
4174+
.join(root_suffix)
4175+
.join("target/debug")
4176+
.join(&exe_name)
4177+
.is_file()
4178+
);
4179+
assert!(&p.root().join("target/debug").join(&exe_name).is_file())
4180+
}
4181+
4182+
test(".");
4183+
test("src");
4184+
}
4185+
41294186
#[cargo_test]
41304187
fn compiler_json_error_format() {
41314188
let p = project()

tests/testsuite/config.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1604,7 +1604,7 @@ target-dir = ''
16041604
let gctx = new_gctx();
16051605

16061606
assert_error(
1607-
gctx.target_dir().unwrap_err(),
1607+
gctx.target_dir(Path::new("")).unwrap_err(),
16081608
"the target directory is set to an empty string in [..]/.cargo/config.toml",
16091609
);
16101610
}

0 commit comments

Comments
 (0)