|  | 
| 11 | 11 | 
 | 
| 12 | 12 | use std::path::PathBuf; | 
| 13 | 13 | 
 | 
| 14 |  | -use cargo_test_support::prelude::*; | 
| 15 | 14 | use cargo_test_support::{paths, project, str}; | 
|  | 15 | +use cargo_test_support::{prelude::*, Project}; | 
| 16 | 16 | use std::env::consts::{DLL_PREFIX, DLL_SUFFIX, EXE_SUFFIX}; | 
| 17 | 17 | 
 | 
| 18 | 18 | #[cargo_test] | 
| @@ -569,7 +569,7 @@ fn template_cargo_cache_home() { | 
| 569 | 569 | } | 
| 570 | 570 | 
 | 
| 571 | 571 | #[cargo_test] | 
| 572 |  | -fn template_workspace_manfiest_path_hash() { | 
|  | 572 | +fn template_workspace_path_hash() { | 
| 573 | 573 |     let p = project() | 
| 574 | 574 |         .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) | 
| 575 | 575 |         .file( | 
| @@ -609,6 +609,81 @@ fn template_workspace_manfiest_path_hash() { | 
| 609 | 609 |     assert_exists(&p.root().join(&format!("target-dir/debug/foo{EXE_SUFFIX}"))); | 
| 610 | 610 | } | 
| 611 | 611 | 
 | 
|  | 612 | +/// Verify that the {workspace-path-hash} does not changes if cargo is run from inside of | 
|  | 613 | +/// a symlinked directory. | 
|  | 614 | +/// The test approach is to build a project twice from the non-symlinked directory and a symlinked | 
|  | 615 | +/// directory and then compare the build-dir paths. | 
|  | 616 | +#[cargo_test] | 
|  | 617 | +fn template_workspace_path_hash_should_handle_symlink() { | 
|  | 618 | +    #[cfg(unix)] | 
|  | 619 | +    use std::os::unix::fs::symlink; | 
|  | 620 | +    #[cfg(windows)] | 
|  | 621 | +    use std::os::windows::fs::symlink_dir as symlink; | 
|  | 622 | + | 
|  | 623 | +    let p = project() | 
|  | 624 | +        .file("src/lib.rs", "") | 
|  | 625 | +        .file( | 
|  | 626 | +            "Cargo.toml", | 
|  | 627 | +            r#" | 
|  | 628 | +             [package] | 
|  | 629 | +             name = "foo" | 
|  | 630 | +             version = "1.0.0" | 
|  | 631 | +             authors = [] | 
|  | 632 | +             edition = "2015" | 
|  | 633 | +             "#, | 
|  | 634 | +        ) | 
|  | 635 | +        .file( | 
|  | 636 | +            ".cargo/config.toml", | 
|  | 637 | +            r#" | 
|  | 638 | +             [build] | 
|  | 639 | +             build-dir = "foo/{workspace-path-hash}/build-dir" | 
|  | 640 | +             "#, | 
|  | 641 | +        ) | 
|  | 642 | +        .build(); | 
|  | 643 | + | 
|  | 644 | +    // Build from the non-symlinked directory | 
|  | 645 | +    p.cargo("check -Z build-dir") | 
|  | 646 | +        .masquerade_as_nightly_cargo(&["build-dir"]) | 
|  | 647 | +        .enable_mac_dsym() | 
|  | 648 | +        .run(); | 
|  | 649 | + | 
|  | 650 | +    // Parse and verify the hash dir created from the non-symlinked dir | 
|  | 651 | +    let foo_dir = p.root().join("foo"); | 
|  | 652 | +    assert_exists(&foo_dir); | 
|  | 653 | +    let original_hash_dir = parse_workspace_manifest_path_hash(&foo_dir); | 
|  | 654 | +    verify_layouts(&p, &original_hash_dir); | 
|  | 655 | + | 
|  | 656 | +    // Create a symlink of the project root. | 
|  | 657 | +    let mut symlinked_dir = p.root().clone(); | 
|  | 658 | +    symlinked_dir.pop(); | 
|  | 659 | +    symlinked_dir = symlinked_dir.join("symlink-dir"); | 
|  | 660 | +    symlink(p.root(), &symlinked_dir).unwrap(); | 
|  | 661 | + | 
|  | 662 | +    // Remove the foo dir (which contains the build-dir) before we rebuild from a symlinked dir. | 
|  | 663 | +    foo_dir.rm_rf(); | 
|  | 664 | + | 
|  | 665 | +    // Run cargo from the symlinked dir | 
|  | 666 | +    p.cargo("check -Z build-dir") | 
|  | 667 | +        .cwd(&symlinked_dir) | 
|  | 668 | +        .masquerade_as_nightly_cargo(&["build-dir"]) | 
|  | 669 | +        .enable_mac_dsym() | 
|  | 670 | +        .run(); | 
|  | 671 | + | 
|  | 672 | +    // Parse and verify the hash created from the symlinked dir | 
|  | 673 | +    assert_exists(&foo_dir); | 
|  | 674 | +    let symlink_hash_dir = parse_workspace_manifest_path_hash(&foo_dir); | 
|  | 675 | +    verify_layouts(&p, &symlink_hash_dir); | 
|  | 676 | + | 
|  | 677 | +    // Verify the hash dir created from the symlinked and non-symlinked dirs are the same. | 
|  | 678 | +    assert_eq!(original_hash_dir, symlink_hash_dir); | 
|  | 679 | + | 
|  | 680 | +    fn verify_layouts(p: &Project, build_dir_parent: &PathBuf) { | 
|  | 681 | +        let build_dir = build_dir_parent.as_path().join("build-dir"); | 
|  | 682 | +        assert_build_dir_layout(build_dir, "debug"); | 
|  | 683 | +        assert_artifact_dir_layout(p.root().join("target"), "debug"); | 
|  | 684 | +    } | 
|  | 685 | +} | 
|  | 686 | + | 
| 612 | 687 | fn parse_workspace_manifest_path_hash(hash_dir: &PathBuf) -> PathBuf { | 
| 613 | 688 |     // Since the hash will change between test runs simply find the first directories and assume | 
| 614 | 689 |     // that is the hash dir. The format is a 2 char directory followed by the remaining hash in the | 
|  | 
0 commit comments