Skip to content

Commit 7d08c76

Browse files
committed
feat(build): an object action's outputs reach a static library, not only a linked image
A package whose device code is its point declares `kind = "lib"`. Until this, the actions its build program emitted were dropped — with a warning, and with the archive coming out containing none of them: build.mcpp action 'cuda:wkv' has role = "object" but this build produces no executable, shared library or test binary to link its outputs into Measured on llama.cpp's CUDA backend, which is 305 `.cu` files behind exactly such a target: every action reported that line and the build succeeded, having produced a library with no device code in it. The archive rule already consumes `lu.objects`; a static library was simply absent from the predicate that decides which link units an object action attaches to. The objects an action produced belong there for the same reason a compiled `.cpp`'s do — a target's content is what it was told to contain. e2e 608 asserts the member list of the archive rather than the exit status: an `ar` handed nothing still writes a well-formed archive and reports success. This is the engine half of the multi-device design's C-6.
1 parent e01f4d4 commit 7d08c76

2 files changed

Lines changed: 101 additions & 5 deletions

File tree

‎src/build/prepare.cppm‎

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9582,8 +9582,19 @@ prepare_build(bool print_fingerprint,
95829582
// under plain `mcpp build`, where that unit does not exist.
95839583
// (`[resources]` makes the opposite call on purpose: an icon
95849584
// belongs to what ships, not to a test runner.)
9585+
//
9586+
// ⭐⭐ A STATIC LIBRARY IS ONE OF THEM, and leaving it out was
9587+
// the whole of what C-6 needed. A package whose device code is
9588+
// its point -- ggml's CUDA backend is 305 `.cu` files behind a
9589+
// `kind = "lib"` target -- emitted its actions, watched every
9590+
// one of them be dropped with a warning, and produced an
9591+
// archive with no device code in it. The archive rule already
9592+
// consumes `lu.objects`, so the objects an action produced
9593+
// belong there for exactly the reason a compiled `.cpp`'s do:
9594+
// the target's content is what it was told to contain.
95859595
const bool image = lu.kind == mcpp::build::LinkUnit::Binary
95869596
|| lu.kind == mcpp::build::LinkUnit::SharedLibrary
9597+
|| lu.kind == mcpp::build::LinkUnit::StaticLibrary
95879598
|| lu.kind == mcpp::build::LinkUnit::TestBinary;
95889599
const bool wanted = a.targets.empty()
95899600
? image
@@ -9602,11 +9613,12 @@ prepare_build(bool print_fingerprint,
96029613
if (!attached && a.targets.empty()) {
96039614
mcpp::diag::degraded("action/no-target", std::format(
96049615
"build.mcpp action '{}' has role = \"object\" but this build "
9605-
"produces no executable, shared library or test binary to "
9606-
"link its outputs into", a.id.empty() ? "<unnamed>" : a.id),
9616+
"produces no target to put its outputs into",
9617+
a.id.empty() ? "<unnamed>" : a.id),
96079618
"the action never runs and its outputs are never produced",
9608-
"add a [targets.<name>] that links, or name the targets "
9609-
"explicitly with .target(\"…\")");
9619+
"add a [targets.<name>] — a bin, a lib, a shared lib or a "
9620+
"test all take one — or name the targets explicitly with "
9621+
".target(\"…\")");
96109622
}
96119623
}
96129624
if (!unknownObjectTargets.empty()) {
@@ -9620,7 +9632,7 @@ prepare_build(bool print_fingerprint,
96209632
" targets in this build: [{}]\n"
96219633
" (a target gated by required_features is absent unless those "
96229634
"features are active; test binaries exist only under `mcpp "
9623-
"test`, so name none and the outputs reach every image "
9635+
"test`, so name none and the outputs reach every target "
96249636
"including them)",
96259637
bad, known.empty() ? std::string("none") : known));
96269638
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#!/usr/bin/env bash
2+
# requires: gcc
3+
# An `object`-role action's outputs join a STATIC LIBRARY, not only an
4+
# executable. That is the engine half of the multi-device design's C-6: a
5+
# package whose device code is its point declares `kind = "lib"`, and until
6+
# this the actions it emitted were dropped with a warning and the archive came
7+
# out with none of them in it.
8+
#
9+
# Nothing here names a device. The "device compiler" is the toolchain's own C
10+
# compiler and the property under test is which link units an action attaches
11+
# to, which is not a vendor question.
12+
set -e
13+
14+
TMP=$(mktemp -d)
15+
trap "rm -rf $TMP" EXIT
16+
cd "$TMP"
17+
18+
"$MCPP" new archived > /dev/null; cd archived
19+
rm -f src/*.cppm src/main.cpp
20+
mkdir -p src
21+
cat > src/host.c <<'EOF2'
22+
extern int from_action(void);
23+
int host_value(void) { return from_action(); }
24+
EOF2
25+
cat > mcpp.toml <<'EOF2'
26+
[package]
27+
name = "archived"
28+
version = "0.1.0"
29+
[language]
30+
standard = "c++23"
31+
[build]
32+
sources = ["src/*.c"]
33+
[targets.archived]
34+
kind = "lib"
35+
EOF2
36+
cat > build.mcpp <<'EOF2'
37+
import std;
38+
import mcpp;
39+
int main() {
40+
const std::string out = std::string(mcpp::out_dir());
41+
const std::string src = out + "/piece.c";
42+
{ std::ofstream f(src, std::ios::trunc);
43+
f << "int from_action(void) { return 7; }\n"; }
44+
const std::string obj = out + "/piece.o";
45+
mcpp::action a;
46+
a.id = "piece"; a.role = "object"; a.description = "compile the piece";
47+
a.arg((std::string(mcpp::toolchain_dir()) + "/bin/gcc").c_str());
48+
a.arg("-c"); a.arg(src.c_str()); a.arg("-o"); a.arg(obj.c_str());
49+
a.input(src.c_str());
50+
a.output(obj.c_str());
51+
a.submit();
52+
return 0;
53+
}
54+
EOF2
55+
56+
# ── One: the action runs and its object is IN the archive ────────────────
57+
"$MCPP" build > build.log 2>&1 || { cat build.log; echo "FAIL: the build failed"; exit 1; }
58+
59+
grep -q "produces no target" build.log && {
60+
cat build.log
61+
echo "FAIL: the action was dropped for want of a linked image"
62+
exit 1
63+
}
64+
65+
lib=$(find target -name 'libarchived.a' | head -1)
66+
[ -n "$lib" ] || { echo "FAIL: no static library was produced"; exit 1; }
67+
68+
# ⭐ THE MEMBER LIST IS THE ASSERTION, not the exit status: an `ar` that was
69+
# handed nothing still writes a well-formed archive and reports success.
70+
members=$(ar t "$lib" 2>/dev/null | tr '\n' ' ')
71+
case "$members" in
72+
*piece.o*) ;;
73+
*) echo "FAIL: piece.o is not a member of the archive (members: $members)"; exit 1 ;;
74+
esac
75+
echo "PASS: an object action's output is archived into a static library"
76+
77+
# ── Two: the symbol is really there ─────────────────────────────────────
78+
if command -v nm > /dev/null 2>&1; then
79+
nm "$lib" 2>/dev/null | grep -q "from_action" || {
80+
echo "FAIL: the archive has the member but not its symbol"; exit 1; }
81+
echo "PASS: the archived member carries its symbol"
82+
fi
83+
84+
echo "PASS: object actions reach a static library"

0 commit comments

Comments
 (0)