Skip to content

Commit 98712e2

Browse files
committed
fix: preserve Windows target architecture in driver links and resources
1 parent 5d6e065 commit 98712e2

6 files changed

Lines changed: 134 additions & 1 deletion

File tree

‎src/build/flags.cppm‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1955,7 +1955,8 @@ CompileFlags compute_flags(const BuildPlan& plan) {
19551955
// ask the driver for a different CRT than the objects already have.
19561956
const std::string msvcAbiCrtLd =
19571957
msvcAbiCrtWord.empty() ? std::string{} : (" " + msvcAbiCrtWord);
1958-
f.ld = std::format("{}{} -fuse-ld=lld{}{}{}{}", full_static,
1958+
// 链接驱动也必须保留目标架构,否则 clang 会按宿主架构选择 CRT。
1959+
f.ld = std::format("{}{}{} -fuse-ld=lld{}{}{}{}", full_static, crossTarget,
19591960
msvcAbiCrtLd, msvcSysroot, link_intent_ld,
19601961
user_ldflags, link_extra);
19611962
f.ldC = f.ld; // the CRT model applies to C TUs on this ABI too

‎src/build/prepare/windows_resources.cpp‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@ step13_resource_compiler(PrepareState& state, BuildContext& ctx) {
126126
}
127127
}
128128
for (auto& ru : ctx.plan.resourceUnits) {
129+
if (auto target = rsrc::coff_target_flag(*tool, state.tc->targetTriple);
130+
!target.empty())
131+
ru.flags.push_back(std::move(target));
129132
ru.flags.push_back(msvcStyle ? "/C" : "--codepage=65001");
130133
if (msvcStyle) ru.flags.push_back("65001");
131134
for (auto const& d : ru.includeDirs) ru.flags.push_back(ip + d.string());

‎src/build/resources.cppm‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ struct RcTool {
8080
std::optional<RcTool> find_rc_tool(const mcpp::toolchain::Toolchain& tc,
8181
std::string_view dialectId);
8282

83+
std::string coff_target_flag(const RcTool& tool, std::string_view targetTriple);
84+
8385
// Split a Windows environment list (PATH, INCLUDE, LIB) into its entries.
8486
//
8587
// `;` is the ONLY separator, and that is not a simplification. Every value that
@@ -166,6 +168,18 @@ compile_utf8_manifest(const mcpp::toolchain::Toolchain& tc,
166168

167169
namespace mcpp::build::resources {
168170

171+
std::string coff_target_flag(const RcTool& tool, std::string_view targetTriple) {
172+
if (tool.style != "gnu") return {};
173+
auto trip = mcpp::toolchain::triple::parse(targetTriple);
174+
if (!trip) return {};
175+
// LLVM 接受完整 triple;GNU windres 则使用 BFD 格式名。
176+
if (tool.name().find("llvm-windres") != std::string::npos)
177+
return "--target=" + trip->llvm_triple();
178+
if (trip->arch == "i686") return "--target=pe-i386";
179+
if (trip->arch == "x86_64") return "--target=pe-x86-64";
180+
return {};
181+
}
182+
169183
namespace {
170184

171185
bool exists_file(const std::filesystem::path& p) {
@@ -321,6 +335,8 @@ compile_utf8_manifest(const mcpp::toolchain::Toolchain& tc,
321335

322336
// The same spelling as the `rc_object` rule of the ninja backend.
323337
std::vector<std::string> argv = {tool->path.string()};
338+
if (auto target = coff_target_flag(*tool, tc.targetTriple); !target.empty())
339+
argv.push_back(std::move(target));
324340
if (msvcStyle)
325341
for (auto a : {"/nologo", "/C", "65001", "/fo"}) argv.emplace_back(a);
326342
else
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#!/usr/bin/env bash
2+
# requires: msvc
3+
# mcpp#775: the driver link and COFF resource must target the selected machine.
4+
set -e
5+
source "$(dirname "$0")/_host_path.sh"
6+
TMP=$(mktemp -d)
7+
trap 'rm -rf "$TMP"' EXIT
8+
fail() { echo "FAIL: $1"; shift; for f in "$@"; do cat "$f" 2>/dev/null; done; exit 1; }
9+
MCPP="${MCPP:-mcpp}"
10+
REGISTRY_HOST=$(host_path "${MCPP_HOME:-$HOME/.mcpp}/registry")
11+
export MCPP_HOME="$TMP/home"
12+
mkdir -p "$MCPP_HOME"
13+
printf '[xlings]\nhome = "%s"\n' "$REGISTRY_HOST" > "$MCPP_HOME/config.toml"
14+
cd "$TMP"
15+
hexof() { od -An -v -tx1 "$1" | tr -d ' \n'; }
16+
pe_machine() {
17+
local offset
18+
offset=$(od -An -tu4 -j60 -N4 "$1" | tr -d ' \n')
19+
od -An -tu2 -j"$((offset + 4))" -N2 "$1" | tr -d ' \n'
20+
}
21+
for arch in i686 x86_64; do
22+
machine=332
23+
[ "$arch" != x86_64 ] || machine=34404
24+
for case_name in c cxx dll; do
25+
mkdir -p "$arch/$case_name/src"
26+
cd "$arch/$case_name"
27+
kind=bin
28+
suffix=exe
29+
main_line='main = "src/main.cpp"'
30+
if [ "$case_name" = dll ]; then
31+
kind=shared
32+
suffix=dll
33+
main_line=''
34+
printf 'extern "C" __declspec(dllexport) int answer() { return 42; }\n' > src/main.cpp
35+
elif [ "$case_name" = c ]; then
36+
main_line='main = "src/main.c"'
37+
printf 'int main(void) { return 0; }\n' > src/main.c
38+
else
39+
printf 'int main() { return 0; }\n' > src/main.cpp
40+
fi
41+
cat > mcpp.toml <<EOF
42+
[package]
43+
name = "probe"
44+
version = "0.1.0"
45+
[toolchain]
46+
windows = "llvm@20.1.7"
47+
[build]
48+
cxx_runtime = "host-coupled"
49+
[target.$arch-windows-msvc]
50+
sysroot = "msvc@system"
51+
[targets.probe]
52+
kind = "$kind"
53+
$main_line
54+
windows_auto_export = false
55+
EOF
56+
# The first build isolates the driver regression from the resource one.
57+
"$MCPP" build --target "$arch-windows-msvc" > first.log 2>&1 \
58+
|| fail "$arch/$case_name: driver link failed" first.log
59+
image=$(find target -name "probe.$suffix" | head -1)
60+
[ -n "$image" ] || fail "$arch/$case_name: image missing" first.log
61+
[ "$(pe_machine "$image")" = "$machine" ] || fail "wrong PE machine: $image"
62+
[ "$kind" != bin ] || "$image" || fail "executable failed: $image"
63+
printf '\n[resources]\nfiles = ["app.rc"]\n' >> mcpp.toml
64+
printf '101 RCDATA { 0x1357, 0x2468, 0xabcdef01L }\n' > app.rc
65+
"$MCPP" build --target "$arch-windows-msvc" > resource.log 2>&1 \
66+
|| fail "$arch/$case_name: resource link failed" resource.log
67+
object=$(find target -path '*/res/*.o' | head -1)
68+
[ -n "$object" ] || fail "$arch/$case_name: COFF resource missing" resource.log
69+
[ "$(od -An -tu2 -N2 "$object" | tr -d ' \n')" = "$machine" ] \
70+
|| fail "wrong COFF machine: $object"
71+
[[ "$(hexof "$image")" == *5713682401efcdab* ]] || fail "resource data missing"
72+
printf '101 RCDATA { 0x7654, 0x3210, 0xfedcba98L }\n' > app.rc
73+
"$MCPP" build --target "$arch-windows-msvc" > rebuild.log 2>&1 \
74+
|| fail "$arch/$case_name: resource rebuild failed" rebuild.log
75+
[ "$(pe_machine "$image")" = "$machine" ] || fail "rebuild changed PE machine"
76+
[[ "$(hexof "$image")" == *5476103298badcfe* ]] || fail "resource data stayed stale"
77+
cd "$TMP"
78+
done
79+
done
80+
echo "PASS: 889 Windows driver target and COFF resources"

‎tests/unit/test_build_resources.cpp‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,21 @@ import mcpp.build.resources;
77
namespace res = mcpp::build::resources;
88
namespace fs = std::filesystem;
99

10+
TEST(BuildResources, CoffTargetFollowsTheTargetInsteadOfTheHost) {
11+
const res::RcTool llvm{"/selected/bin/llvm-windres.exe", "gnu"};
12+
EXPECT_EQ(res::coff_target_flag(llvm, "i686-windows-msvc"),
13+
"--target=i686-pc-windows-msvc");
14+
EXPECT_EQ(res::coff_target_flag(llvm, "x86_64-windows-msvc"),
15+
"--target=x86_64-pc-windows-msvc");
16+
EXPECT_EQ(res::coff_target_flag(llvm, "aarch64-windows-msvc"),
17+
"--target=aarch64-pc-windows-msvc");
18+
const res::RcTool gnu{"/selected/bin/windres", "gnu"};
19+
EXPECT_EQ(res::coff_target_flag(gnu, "i686-windows-gnu"), "--target=pe-i386");
20+
EXPECT_EQ(res::coff_target_flag(gnu, "x86_64-windows-gnu"), "--target=pe-x86-64");
21+
EXPECT_TRUE(res::coff_target_flag({"rc.exe", "msvc"}, "i686-windows-msvc").empty());
22+
EXPECT_TRUE(res::coff_target_flag({"llvm-rc.exe", "msvc"}, "x86_64-windows-msvc").empty());
23+
}
24+
1025
namespace {
1126

1227
mcpp::manifest::Package sample_package() {

‎tests/unit/test_ninja_backend.cpp‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,24 @@ BuildPlan minimal_plan() {
7575

7676
} // namespace
7777

78+
TEST(NinjaBackend, WindowsDriverLinksRetainTheSelectedTargetForCAndCxx) {
79+
for (const auto arch : {"i686", "x86_64", "aarch64"}) {
80+
auto plan = minimal_plan();
81+
plan.toolchain.compiler = mcpp::toolchain::CompilerId::Clang;
82+
plan.toolchain.binaryPath = "/selected/bin/clang++";
83+
plan.toolchain.targetTriple = std::string(arch) + "-pc-windows-msvc";
84+
plan.toolchain.crossTargetFlag = "--target=" + plan.toolchain.targetTriple;
85+
plan.manifest.buildConfig.cxxRuntime = "host-coupled";
86+
plan.targetSide.compiler.interfaceName = "llvm";
87+
plan.targetSide.kernelAbi.interfaceName = "windows";
88+
plan.targetSide.cAbi.interfaceName = "msvc";
89+
plan.targetSide.cxx.interfaceName = "msvc";
90+
auto flags = compute_flags(plan);
91+
EXPECT_NE(flags.ld.find(plan.toolchain.crossTargetFlag), std::string::npos);
92+
EXPECT_NE(flags.ldC.find(plan.toolchain.crossTargetFlag), std::string::npos);
93+
}
94+
}
95+
7896
TEST(NinjaBackend, BitcodeExportToolsComeFromTheSelectedLlvmAndAreQuotedAsWords) {
7997
auto plan = minimal_plan();
8098
plan.toolchain.compiler = mcpp::toolchain::CompilerId::Clang;

0 commit comments

Comments
 (0)