Skip to content

Commit ad7a3dd

Browse files
committed
dist-apk follows the engine's strip decision: --no-strip and --debug-symbols reach the packed libraries (#649 E5)
1 parent b5f759e commit ad7a3dd

2 files changed

Lines changed: 109 additions & 4 deletions

File tree

dist/apk.cppm

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,16 @@ struct options {
221221
// drops the symbol table and the debug information -- what the Android
222222
// Gradle plugin does to every library it packages, and what `mcpp pack`
223223
// reports it did.
224+
//
225+
// FROM 0.12.0 THE ENGINE'S DECISION GOVERNS AS WELL. An engine that strips
226+
// what the graph built (mcpp 2026.9.16.1, #649 E5) tells a build program
227+
// whether this packaging pass strips (`MCPP_PACK_STRIP`, "1" or "0") and
228+
// where `--debug-symbols` sends the separated debug information
229+
// (`MCPP_PACK_DEBUG_SYMBOLS_DIR`), so `mcpp pack --no-strip` and
230+
// `--debug-symbols <dir>` reach the libraries this member packs as they
231+
// reach the ones the engine stages. A library is stripped unless either
232+
// this option or the engine says to keep it. An older engine publishes
233+
// neither variable, and the member strips as before.
224234
bool keep_debug_symbols = false;
225235

226236
// LEVEL 1, KOTLIN (0.11.0). One or more directories of `.kt` sources,
@@ -1998,10 +2008,24 @@ inline plan plan_for(options opt = {}) {
19982008
}
19992009
const bool inPlaceLibraries = loads_native_libraries_in_place(manifestBytes);
20002010

2011+
// THE ENGINE'S STRIP DECISION (0.12.0), read through the environment
2012+
// rather than an `mcpp::` accessor so the member still builds, and still
2013+
// strips, under an engine that predates the accessor. See
2014+
// `options::keep_debug_symbols`.
2015+
const char* engineStrip = std::getenv("MCPP_PACK_STRIP");
2016+
const bool engineKeeps = engineStrip && std::string_view(engineStrip) == "0";
2017+
const char* engineDebugDir = std::getenv("MCPP_PACK_DEBUG_SYMBOLS_DIR");
2018+
const std::string debugDir = engineDebugDir ? engineDebugDir : "";
2019+
20012020
// THE BUILD'S OWN llvm-strip (0.11.1): the one beside the compiler mcpp
2002-
// resolved for this row, the NDK's.
2021+
// resolved for this row, the NDK's. With a debug-symbols directory,
2022+
// `llvm-objcopy` beside it separates the debug information first, in the
2023+
// order the engine's own strip uses (`src/pack/strip.cppm`): a copy of the
2024+
// debug sections while the library still has them, then the stripped
2025+
// library with a `.gnu_debuglink` naming that copy.
20032026
std::string llvmStrip;
2004-
if (!opt.keep_debug_symbols) {
2027+
std::string llvmObjcopy;
2028+
if (!opt.keep_debug_symbols && !engineKeeps) {
20052029
const std::string toolchain = mcpp::toolchain_dir();
20062030
const fs::path candidate = fs::path(toolchain) / "bin" / "llvm-strip";
20072031
if (!toolchain.empty() && is_file(candidate.string())) {
@@ -2011,6 +2035,17 @@ inline plan plan_for(options opt = {}) {
20112035
"mcpp.dist.apk: no llvm-strip beside the toolchain ({}); the native libraries are packed "
20122036
"with their debug information.", toolchain.empty() ? "none reported" : toolchain).c_str());
20132037
}
2038+
if (!llvmStrip.empty() && !debugDir.empty()) {
2039+
const fs::path objcopy = fs::path(toolchain) / "bin" / "llvm-objcopy";
2040+
if (is_file(objcopy.string())) {
2041+
llvmObjcopy = objcopy.string();
2042+
} else {
2043+
mcpp::warning(std::format(
2044+
"mcpp.dist.apk: --debug-symbols names {}, and there is no llvm-objcopy beside the "
2045+
"toolchain ({}) to separate the libraries' debug information; they are stripped "
2046+
"without it.", debugDir, toolchain).c_str());
2047+
}
2048+
}
20142049
}
20152050

20162051
// ── the temporary staging tree: lib/<abi>/, assets/ ─────────────────
@@ -2047,10 +2082,38 @@ inline plan plan_for(options opt = {}) {
20472082
}
20482083
std::error_code ec;
20492084
fs::create_directories(dst.parent_path(), ec);
2085+
const std::string leaf = fs::path(so).filename().string();
2086+
if (!llvmObjcopy.empty()) {
2087+
// One subdirectory per ABI: a package carries the same library name
2088+
// once for every ABI, and a flat directory would let the last one
2089+
// overwrite the others' debug information.
2090+
const fs::path debugFile = fs::path(debugDir) / abi / (leaf + ".debug");
2091+
fs::create_directories(debugFile.parent_path(), ec);
2092+
step keep;
2093+
keep.id = std::format("{}:debug:{}:{}", bundle ? "aab" : "apk", abi, leaf);
2094+
keep.role = "artifact";
2095+
keep.description = "LLVM-OBJCOPY --only-keep-debug " + abi + "/" + leaf;
2096+
keep.output = debugFile.string();
2097+
keep.argv = { llvmObjcopy, "--only-keep-debug", so, keep.output };
2098+
keep.inputs = { so };
2099+
p.steps.push_back(keep);
2100+
2101+
step strip;
2102+
strip.id = std::format("{}:strip:{}:{}", bundle ? "aab" : "apk", abi, leaf);
2103+
strip.role = "artifact";
2104+
strip.description = "LLVM-OBJCOPY --strip-unneeded " + abi + "/" + leaf;
2105+
strip.output = dst.string();
2106+
strip.argv = { llvmObjcopy, "--strip-unneeded",
2107+
"--add-gnu-debuglink=" + debugFile.string(), so, strip.output };
2108+
strip.inputs = { so, keep.output };
2109+
p.steps.push_back(strip);
2110+
libInputs.push_back(strip.output);
2111+
return;
2112+
}
20502113
step strip;
2051-
strip.id = std::format("{}:strip:{}:{}", bundle ? "aab" : "apk", abi, fs::path(so).filename().string());
2114+
strip.id = std::format("{}:strip:{}:{}", bundle ? "aab" : "apk", abi, leaf);
20522115
strip.role = "artifact";
2053-
strip.description = "LLVM-STRIP " + abi + "/" + fs::path(so).filename().string();
2116+
strip.description = "LLVM-STRIP " + abi + "/" + leaf;
20542117
strip.output = dst.string();
20552118
strip.argv = { llvmStrip, "--strip-unneeded", "-o", strip.output, so };
20562119
strip.inputs = { so };

tests/apk-consumer/check-apk-features.sh

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,3 +308,45 @@ grep -q '\.symtab' sections-l.log || fail "keep_debug_symbols packed a stripped
308308
unset APK_CONSUMER_KEEP_DEBUG_SYMBOLS
309309
rm -rf k
310310
echo "ok: keep_debug_symbols packs the library with its symbol table"
311+
312+
# ── (m),(n) 0.12.0: the engine's strip decision reaches the packed libraries ─
313+
#
314+
# An engine that strips what the graph built (mcpp 2026.9.16.1) publishes its
315+
# decision to the build program, so (m) `mcpp pack --no-strip` packs the library
316+
# with its symbol table, and (n) `--debug-symbols <dir>` separates its debug
317+
# information into `<dir>/<abi>/<library>.debug` and leaves a `.gnu_debuglink`
318+
# in the packed copy. An older engine publishes nothing, so these legs are
319+
# skipped there; (k) is the behaviour that engine keeps.
320+
engine_version=$("$MCPP" --version | awk '{print $2}')
321+
if [ "$(printf '%s\n%s\n' 2026.9.16.1 "$engine_version" | sort -V | head -1)" = 2026.9.16.1 ]; then
322+
echo "== (m) mcpp pack --no-strip =="
323+
rm -rf target k
324+
"$MCPP" pack --format apk --target "$TARGET" --no-strip > pack-m.log 2>&1 || fail "pack --no-strip failed" pack-m.log
325+
APK=$(find target -name 'apk-consumer.apk' | head -1)
326+
[ -n "$APK" ] || fail "no apk-consumer.apk" pack-m.log
327+
mkdir -p k && unzip -q -o "$APK" "$LIB" -d k
328+
readelf -S "k/$LIB" > sections-m.log
329+
grep -q '\.symtab' sections-m.log || fail "--no-strip packed a stripped library" sections-m.log
330+
rm -rf k
331+
echo "ok: --no-strip packs the library with its symbol table"
332+
333+
echo "== (n) mcpp pack --debug-symbols <dir> =="
334+
rm -rf target k debug-n
335+
DEBUG_DIR="$PWD/debug-n"
336+
"$MCPP" pack --format apk --target "$TARGET" --debug-symbols "$DEBUG_DIR" > pack-n.log 2>&1 \
337+
|| fail "pack --debug-symbols failed" pack-n.log
338+
APK=$(find target -name 'apk-consumer.apk' | head -1)
339+
[ -n "$APK" ] || fail "no apk-consumer.apk" pack-n.log
340+
mkdir -p k && unzip -q -o "$APK" "$LIB" -d k
341+
readelf -S "k/$LIB" > sections-n.log
342+
if grep -qE '\.symtab|\.debug_' sections-n.log; then fail "the packed library keeps its symbol table or debug information" sections-n.log; fi
343+
grep -q '\.gnu_debuglink' sections-n.log || fail "the packed library does not name its debug file" sections-n.log
344+
DEBUG_FILE="$DEBUG_DIR/x86_64/libapk-consumer.so.debug"
345+
[ -f "$DEBUG_FILE" ] || fail "no $DEBUG_FILE" pack-n.log
346+
readelf -S "$DEBUG_FILE" > sections-n-debug.log
347+
grep -q '\.debug_' sections-n-debug.log || fail "the separated file carries no debug sections" sections-n-debug.log
348+
rm -rf k debug-n
349+
echo "ok: --debug-symbols separates the library's debug information and links the packed copy to it"
350+
else
351+
echo "skip: (m),(n) need an engine that publishes MCPP_PACK_STRIP (2026.9.16.1+); this is $engine_version"
352+
fi

0 commit comments

Comments
 (0)