Repository: intellij-rust/intellij-rust
Issue: custom target spec not resolved correctly

<!--
Hello and thank you for the issue!
If you would like to report a bug, we have added some points below that you can fill out.
Consider using "Help | Create New Issue" action that fills out all necessary information automatically.
Feel free to remove all the irrelevant text to request a new feature.
-->

## Environment

* **IntelliJ Rust plugin version:** 0.4.179.4903-221
* **Rust toolchain version:** 1.63.0 & 1.64.0 (displayed in settings, actually 1.66 nightly)
* **IDE name and version:** CLion 2022.1.3, Build #CL-221.5921.27, built on June 21, 2022
* **Operating system:** macOS 12.5.1, 12.6 (Intel & M2 chip)

## Problem description
When having a relative path to a custom target definition json file in `.cargo/config.toml`, `Refresh Cargo project` fails with

```text
Execution failed (exit code 101).
/Users/tsatke/.cargo/bin/cargo +nightly-aarch64-apple-darwin metadata --verbose --format-version 1 --all-features --filter-platform x86_64-martim.json
stdout : error: target path "x86_64-martim.json" is not a valid file

Caused by:
  No such file or directory (os error 2)

stderr : 

```

## Steps to reproduce

Put the following into `.cargo/config.toml`.

```toml
[build]
target = "x86_64-martim.json"
```

The file is valid and is located in the project root.

When executing the above command manually in the project root, it succeeds and prints a lot of information (as is probably expected). **Changing the target path to an absolute one makes the problems disappear**, which makes me think that the command may be executed in the wrong directory?

<!--
Please include as much of your codebase as needed to reproduce the error.
If the relevant files are large, please provide a link to a public repository or a [Gist](https://gist.github.com/).

If you provide a screenshot of some code, please also copy-paste the corresponding code snippet here.
-->


Changed files:
src/test/kotlin/org/rust/cargo/toolchain/CargoConfigTest.kt
src/main/kotlin/org/rust/cargo/toolchain/tools/Cargo.kt
src/test/kotlin/org/rust/cargo/project/model/CargoPackagesTest.kt
src/test/kotlin/org/rust/cargo/project/model/CargoStdlibPackagesTest.kt

diff --git a/src/main/kotlin/org/rust/cargo/toolchain/tools/Cargo.kt b/src/main/kotlin/org/rust/cargo/toolchain/tools/Cargo.kt
index b6a7901a9..1e1f215a2 100644
--- a/src/main/kotlin/org/rust/cargo/toolchain/tools/Cargo.kt
+++ b/src/main/kotlin/org/rust/cargo/toolchain/tools/Cargo.kt
@@ -24,6 +24,7 @@ import com.intellij.openapi.util.registry.Registry
 import com.intellij.openapi.util.registry.RegistryValue
 import com.intellij.openapi.vfs.VirtualFile
 import com.intellij.util.execution.ParametersListUtil
+import com.intellij.util.io.systemIndependentPath
 import com.intellij.util.net.HttpConfigurable
 import com.intellij.util.text.SemVer
 import org.jetbrains.annotations.TestOnly
@@ -230,7 +231,7 @@ class Cargo(
      */
     fun getConfig(
         owner: Project,
-        projectDirectory: Path?
+        projectDirectory: Path
     ): RsResult<CargoConfig, RsProcessExecutionOrDeserializationException> {
         val parameters = mutableListOf("-Z", "unstable-options", "config", "get")
 
@@ -248,7 +249,7 @@ class Cargo(
         }
 
         val buildTargetNode = tree.at("/build/target")
-        val buildTarget = if (buildTargetNode.isMissingNode) null else buildTargetNode.asText()
+        var buildTarget = if (buildTargetNode.isMissingNode) null else buildTargetNode.asText()
         val env = tree.at("/env").fields().asSequence().toList().mapNotNull { field ->
             // Value can be either string or object with additional `forced` and `relative` params.
             // https://doc.rust-lang.org/cargo/reference/config.html#env
@@ -267,6 +268,12 @@ class Cargo(
             }
         }.toMap()
 
+        // If build target ends with `.json`, it's a custom toolchain.
+        // To make it work in all cases (for example, fetching stdlib metadata for the same build target),
+        // we save the corresponding path as absolute one not to depend on working directory
+        if (buildTarget != null && buildTarget.endsWith(".json")) {
+            buildTarget = projectDirectory.resolve(buildTarget).toAbsolutePath().systemIndependentPath
+        }
         return Ok(CargoConfig(buildTarget, env))
     }
 
diff --git a/src/test/kotlin/org/rust/cargo/project/model/CargoPackagesTest.kt b/src/test/kotlin/org/rust/cargo/project/model/CargoPackagesTest.kt
index fb734297a..b90cb2639 100644
--- a/src/test/kotlin/org/rust/cargo/project/model/CargoPackagesTest.kt
+++ b/src/test/kotlin/org/rust/cargo/project/model/CargoPackagesTest.kt
@@ -90,6 +90,64 @@ class CargoPackagesTest : RsWithToolchainTestBase() {
         workspace.checkPackage("windows_package", shouldExist = true)
     }
 
+    @MinRustcVersion("1.53.0")
+    fun `test target specific dependencies with custom build target`() {
+        buildProject {
+            dir(".cargo") {
+                toml("config", """
+                    [build]
+                    target = "custom-target.json"
+                """)
+            }
+            file("custom-target.json", """
+                {
+                    "llvm-target": "aarch64-unknown-none",
+                    "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
+                    "arch": "aarch64",
+                    "target-endian": "little",
+                    "target-pointer-width": "64",
+                    "target-c-int-width": "32",
+                    "os": "none",
+                    "executables": true,
+                    "linker-flavor": "ld.lld",
+                    "linker": "rust-lld",
+                    "panic-strategy": "abort",
+                    "disable-redzone": true,
+                    "features": "-mmx,-sse,+soft-float"
+                }
+            """)
+            toml("Cargo.toml", """
+                [package]
+                name = "sandbox"
+                version = "0.1.0"
+                authors = []
+
+                [dependencies]
+                common_package = { path = "common_package" }
+
+                [target.'cfg(target_arch = "aarch64")'.dependencies]
+                arm_package = { path = "arm_package" }
+
+                [target.'cfg(target_arch = "x86_64")'.dependencies]
+                x86_package = { path = "x86_package" }
+            """)
+
+            dir("src") {
+                rust("lib.rs", "")
+            }
+
+            dependencyPackage("common_package")
+            dependencyPackage("arm_package")
+            dependencyPackage("x86_package")
+        }
+
+        val workspace = project.cargoProjects.singleProject().workspaceOrFail()
+
+        workspace.checkPackage("common_package", shouldExist = true)
+        workspace.checkPackage("arm_package", shouldExist = true)
+        workspace.checkPackage("x86_package", shouldExist = false)
+    }
+
     private fun FileTreeBuilder.dependencyPackage(name: String) {
         dir(name) {
             toml("Cargo.toml", """
diff --git a/src/test/kotlin/org/rust/cargo/project/model/CargoStdlibPackagesTest.kt b/src/test/kotlin/org/rust/cargo/project/model/CargoStdlibPackagesTest.kt
index 53305412d..60afe4651 100644
--- a/src/test/kotlin/org/rust/cargo/project/model/CargoStdlibPackagesTest.kt
+++ b/src/test/kotlin/org/rust/cargo/project/model/CargoStdlibPackagesTest.kt
@@ -43,6 +43,48 @@ class CargoStdlibPackagesTest : RsWithToolchainTestBase() {
         }
     }
 
+    fun `test target specific dependencies with custom build target`() {
+        buildProject {
+            dir(".cargo") {
+                toml("config", """
+                    [build]
+                    target = "custom-target.json"
+                """)
+            }
+            file("custom-target.json", """
+                {
+                    "llvm-target": "aarch64-unknown-none",
+                    "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
+                    "arch": "aarch64",
+                    "target-endian": "little",
+                    "target-pointer-width": "64",
+                    "target-c-int-width": "32",
+                    "os": "none",
+                    "executables": true,
+                    "linker-flavor": "ld.lld",
+                    "linker": "rust-lld",
+                    "panic-strategy": "abort",
+                    "disable-redzone": true,
+                    "features": "-mmx,-sse,+soft-float"
+                }
+            """)
+            toml("Cargo.toml", """
+                [package]
+                name = "sandbox"
+                version = "0.1.0"
+                authors = []
+            """)
+
+            dir("src") {
+                rust("lib.rs", "")
+            }
+        }
+
+        val workspace = project.cargoProjects.singleProject().workspaceOrFail()
+        val hashbrownPkg = workspace.packages.first { it.name == HASHBROWN }
+        assertEquals(PackageOrigin.STDLIB_DEPENDENCY, hashbrownPkg.origin)
+    }
+
     fun `test recover corrupted stdlib dependency directory`() {
         buildProject {
             toml("Cargo.toml", """
diff --git a/src/test/kotlin/org/rust/cargo/toolchain/CargoConfigTest.kt b/src/test/kotlin/org/rust/cargo/toolchain/CargoConfigTest.kt
index 412685a79..f8cfc999a 100644
--- a/src/test/kotlin/org/rust/cargo/toolchain/CargoConfigTest.kt
+++ b/src/test/kotlin/org/rust/cargo/toolchain/CargoConfigTest.kt
@@ -43,6 +43,40 @@ class CargoConfigTest : RsWithToolchainTestBase() {
         assertEquals("wasm32-unknown-unknown", buildTarget)
     }
 
+    fun `test custom build target`() {
+        val testProject = buildProject {
+            dir(".cargo") {
+                toml("config", """
+                    [build]
+                    target = "custom-target.json"
+                """)
+            }
+            file("custom-target.json", """
+                {
+                    "llvm-target": "aarch64-unknown-none",
+                    "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
+                    "arch": "aarch64",
+                    "target-endian": "little",
+                    "target-pointer-width": "64",
+                    "target-c-int-width": "32",
+                    "os": "none",
+                    "executables": true,
+                    "linker-flavor": "ld.lld",
+                    "linker": "rust-lld",
+                    "panic-strategy": "abort",
+                    "disable-redzone": true,
+                    "features": "-mmx,-sse,+soft-float"
+                }
+            """)
+            file("Cargo.toml", CARGO_TOML)
+            dir("src") { file("main.rs") }
+        }
+
+        val buildTarget = project.cargoProjects.singleWorkspace().cargoConfig.buildTarget
+
+        assertEquals(testProject.root.findChild("custom-target.json")!!.path, buildTarget)
+    }
+
     fun `test env`() {
         buildProject {
             dir(".cargo") {