@@ -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 };
0 commit comments