Skip to content

Commit 650cbf2

Browse files
authored
Support custom JSON targets by mimicking cargo target resolution (#225)
1 parent e2020c7 commit 650cbf2

5 files changed

Lines changed: 106 additions & 18 deletions

File tree

Cargo.lock

Lines changed: 3 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ indicatif = "0.18.3"
3131
paste = "1.0.12"
3232
path-slash = "0.2.0"
3333
serde = { version = "1.0.216", features = ["derive"] }
34+
serde_json = "1.0.150"
3435
tar = "0.4.46"
3536
tracing-subscriber = { version = "0.3.17", features = ["fmt"] }
3637
ureq = { version = "3.0.12", default-features = false, features = [

src/compiler/clang.rs

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,18 @@ impl Clang {
5050
}
5151

5252
for target in &targets {
53-
if target.contains("msvc") {
53+
let (cargo_target_name, llvm_target, is_custom_target) =
54+
crate::compiler::common::resolve_target_info(target)?;
55+
56+
if llvm_target.contains("msvc") {
5457
let msvc_sysroot_dir = self
5558
.setup_msvc_sysroot(cache_dir.clone())
5659
.context("Failed to setup MSVC sysroot")?;
60+
5761
// x86_64-pc-windows-msvc -> x86_64-windows-msvc
58-
let target_no_vendor = target.replace("-pc-", "-");
59-
let target_unknown_vendor = target.replace("-pc-", "-unknown-");
60-
let env_target = target.to_lowercase().replace('-', "_");
62+
let target_no_vendor = llvm_target.replace("-pc-", "-");
63+
let target_unknown_vendor = llvm_target.replace("-pc-", "-unknown-");
64+
let env_target = cargo_target_name.to_lowercase().replace('-', "_");
6165

6266
setup_llvm_tools(&env_path, &cache_dir).context("Failed to setup LLVM tools")?;
6367
setup_target_compiler_and_linker_env(cmd, &env_target, "clang");
@@ -87,7 +91,19 @@ impl Clang {
8791
format!("-I{dir}/include -I{dir}/include/c++/stl -I{dir}/include/__msvc_vcruntime_intrinsics", dir = sysroot_dir),
8892
);
8993

90-
let mut rustflags = get_rustflags(&workdir, target)?.unwrap_or_default();
94+
let mut rustflags =
95+
get_rustflags(&workdir, &cargo_target_name)?.unwrap_or_default();
96+
if is_custom_target {
97+
rustflags.flags.push("-Zunstable-options".to_string());
98+
let mut paths = std::env::var_os("RUST_TARGET_PATH")
99+
.map(|v| std::env::split_paths(&v).collect::<Vec<_>>())
100+
.unwrap_or_default();
101+
102+
if !paths.contains(&workdir) {
103+
paths.insert(0, workdir.clone());
104+
cmd.env("RUST_TARGET_PATH", std::env::join_paths(paths)?);
105+
}
106+
}
91107
rustflags.flags.extend([
92108
"-C".to_string(),
93109
"linker-flavor=lld-link".to_string(),
@@ -96,7 +112,7 @@ impl Clang {
96112
]);
97113

98114
// Check if static CRT is enabled
99-
let is_static_crt = is_static_crt_enabled(&workdir, target)?;
115+
let is_static_crt = is_static_crt_enabled(&workdir, &cargo_target_name)?;
100116
if is_static_crt {
101117
// When using static CRT, we need to link against the static version of libucrt
102118
// instead of the import library. This resolves issues with symbols like
@@ -129,7 +145,7 @@ impl Clang {
129145

130146
// CMake support
131147
let cmake_toolchain = self
132-
.setup_cmake_toolchain(target, &sysroot_dir, &cache_dir, is_static_crt)
148+
.setup_cmake_toolchain(&llvm_target, &sysroot_dir, &cache_dir, is_static_crt)
133149
.with_context(|| format!("Failed to setup CMake toolchain for {}", target))?;
134150
setup_cmake_env(cmd, target, cmake_toolchain);
135151
}

src/compiler/clang_cl.rs

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use xwin::util::ProgressTarget;
1616
use crate::cache::prepare_xwin_cache_dir;
1717
use crate::compiler::common::{
1818
adjust_canonicalization, default_build_target_from_config, get_rustflags, http_agent,
19-
is_static_crt_enabled, setup_cmake_env, setup_env_path, setup_llvm_tools,
19+
is_static_crt_enabled, resolve_target_info, setup_cmake_env, setup_env_path, setup_llvm_tools,
2020
setup_target_compiler_and_linker_env,
2121
};
2222
use crate::options::XWinOptions;
@@ -58,10 +58,12 @@ impl<'a> ClangCl<'a> {
5858
}
5959

6060
for target in &targets {
61-
if target.contains("msvc") {
61+
let (cargo_target_name, llvm_target, is_custom_target) = resolve_target_info(target)?;
62+
63+
if llvm_target.contains("msvc") {
6264
self.setup_msvc_crt_with_retry(xwin_cache_dir.clone())
6365
.context("Failed to setup MSVC CRT")?;
64-
let env_target = target.to_lowercase().replace('-', "_");
66+
let env_target = cargo_target_name.to_lowercase().replace('-', "_");
6567

6668
setup_clang_cl_symlink(&env_path, &cache_dir)
6769
.context("Failed to setup clang-cl symlink")?;
@@ -72,7 +74,7 @@ impl<'a> ClangCl<'a> {
7274
let user_set_c_flags = env::var("CFLAGS").unwrap_or_default();
7375
let user_set_cxx_flags = env::var("CXXFLAGS").unwrap_or_default();
7476

75-
let target_arch = target
77+
let target_arch = llvm_target
7678
.split_once('-')
7779
.map(|(x, _)| x)
7880
.context("invalid target triple")?;
@@ -83,7 +85,7 @@ impl<'a> ClangCl<'a> {
8385

8486
let xwin_dir = adjust_canonicalization(xwin_cache_dir.to_slash_lossy().to_string());
8587
let mut cl_flags = vec![
86-
format!("--target={target}"),
88+
format!("--target={llvm_target}"),
8789
"-Wno-unused-command-line-argument".to_string(),
8890
"-fuse-ld=lld-link".to_string(),
8991
format!("/imsvc {dir}/crt/include", dir = xwin_dir),
@@ -140,13 +142,26 @@ impl<'a> ClangCl<'a> {
140142
};
141143
cmd.env("LIB", lib_value);
142144

143-
let mut rustflags = get_rustflags(&workdir, target)?.unwrap_or_default();
145+
let mut rustflags =
146+
get_rustflags(&workdir, &cargo_target_name)?.unwrap_or_default();
147+
if is_custom_target {
148+
rustflags.flags.push("-Zunstable-options".to_string());
149+
150+
let mut paths = std::env::var_os("RUST_TARGET_PATH")
151+
.map(|v| std::env::split_paths(&v).collect::<Vec<_>>())
152+
.unwrap_or_default();
153+
154+
if !paths.contains(&workdir) {
155+
paths.insert(0, workdir.clone());
156+
cmd.env("RUST_TARGET_PATH", std::env::join_paths(paths)?);
157+
}
158+
}
144159
rustflags
145160
.flags
146161
.extend(["-C".to_string(), "linker-flavor=lld-link".to_string()]);
147162

148163
// Check if static CRT is enabled
149-
let is_static_crt = is_static_crt_enabled(&workdir, target)?;
164+
let is_static_crt = is_static_crt_enabled(&workdir, &cargo_target_name)?;
150165
if is_static_crt {
151166
// When using static CRT, we need to link against the static version of libucrt
152167
// instead of the import library. This resolves issues with symbols like
@@ -190,7 +205,7 @@ impl<'a> ClangCl<'a> {
190205

191206
// CMake support
192207
let cmake_toolchain = self
193-
.setup_cmake_toolchain(target, &xwin_cache_dir, is_static_crt)
208+
.setup_cmake_toolchain(&llvm_target, &xwin_cache_dir, is_static_crt)
194209
.with_context(|| format!("Failed to setup CMake toolchain for {}", target))?;
195210
setup_cmake_env(cmd, target, cmake_toolchain);
196211
}

src/compiler/common.rs

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,66 @@
1-
use anyhow::Result;
1+
use anyhow::{Context, Result};
22
use fs_err as fs;
33
use std::env;
44
use std::ffi::{OsStr, OsString};
55
use std::path::{Path, PathBuf};
66
use std::process::Command;
77
use which::which_in;
88

9+
/// Mimics Cargo's target resolution to find and parse custom JSON targets.
10+
pub fn resolve_target_info(target: &str) -> Result<(String, String, bool)> {
11+
let mut target_path = None;
12+
13+
// 1. Explicit file path
14+
if target.ends_with(".json") {
15+
target_path = Some(PathBuf::from(target));
16+
} else {
17+
// 2. Check current directory for {target}.json
18+
let local_file = PathBuf::from(format!("{}.json", target));
19+
if local_file.exists() {
20+
target_path = Some(local_file);
21+
} else {
22+
// 3. Check RUST_TARGET_PATH environment variable
23+
if let Ok(rust_target_path) = std::env::var("RUST_TARGET_PATH") {
24+
for dir in std::env::split_paths(&rust_target_path) {
25+
let p = dir.join(format!("{}.json", target));
26+
if p.exists() {
27+
target_path = Some(p);
28+
break;
29+
}
30+
}
31+
}
32+
}
33+
}
34+
35+
// 4. If we found a JSON file, parse it
36+
if let Some(path) = target_path {
37+
let stem = path
38+
.file_stem()
39+
.unwrap_or_default()
40+
.to_string_lossy()
41+
.to_string();
42+
43+
let content = fs::read_to_string(&path).context(format!(
44+
"Failed to read custom target file: {}",
45+
path.display()
46+
))?;
47+
48+
let json: serde_json::Value =
49+
serde_json::from_str(&content).context("Failed to parse custom target JSON")?;
50+
51+
let llvm_target = json
52+
.get("llvm-target")
53+
.and_then(|v| v.as_str())
54+
.context("Custom target JSON missing 'llvm-target' field")?
55+
.to_string();
56+
57+
return Ok((stem, llvm_target, true));
58+
}
59+
60+
// 5. No JSON file found; assume it's a built-in target (e.g., "x86_64-pc-windows-msvc")
61+
Ok((target.to_string(), target.to_string(), false))
62+
}
63+
964
/// Sets up the environment path by adding necessary directories to the existing `PATH`.
1065
///
1166
/// On macOS, it checks for specific LLVM installation paths based on the architecture

0 commit comments

Comments
 (0)