@@ -482,13 +482,22 @@ mcpp::toolchain::triple::ArtifactNaming naming_for(const mcpp::toolchain::Toolch
482482// Reading the host constants here made ninja declare an output the compiler
483483// never writes (Linux -> PE: declared `bin/foo`, produced `bin/foo.exe`), so
484484// the link edge could never be satisfied and reran on every build.
485+ //
486+ // `asSharedObject` (#622 A3) is set by the caller for an `Application` target
487+ // exactly when `toolchain::triple::application_form(triple)` answered
488+ // `SharedObject` — the file name derives from the link FORM, and this
489+ // function has no triple of its own to ask, by design (it is called on the
490+ // dependency-target axis too, where the naming has already been resolved).
491+ // It is inert for every other kind.
485492std::filesystem::path target_output (const mcpp::manifest::Target& t,
486- const mcpp::toolchain::triple::ArtifactNaming& n) {
493+ const mcpp::toolchain::triple::ArtifactNaming& n,
494+ bool asSharedObject = false ) {
487495 if (t.kind == mcpp::manifest::Target::Library) {
488496 return std::filesystem::path (" bin" ) /
489497 std::format (" {}{}{}" , n.libPrefix , t.name , n.staticLibExt );
490498 }
491- if (t.kind == mcpp::manifest::Target::SharedLibrary) {
499+ if (t.kind == mcpp::manifest::Target::SharedLibrary
500+ || (t.kind == mcpp::manifest::Target::Application && asSharedObject)) {
492501 return std::filesystem::path (" bin" ) /
493502 std::format (" {}{}{}" , n.libPrefix , t.name , n.sharedLibExt );
494503 }
@@ -1370,7 +1379,11 @@ make_plan(const mcpp::manifest::Manifest& manifest,
13701379 // disambiguate the common single-binary project).
13711380 for (auto & t : manifest.targets ) {
13721381 if (t.main .empty ()) continue ;
1373- if (t.kind != mcpp::manifest::Target::Binary
1382+ // `is_program()` (#622 A3): an `app` also has an entry `main` --
1383+ // compiled as an ordinary executable entry on every row but Android,
1384+ // and as a translation unit of the shared library there -- so it
1385+ // occupies an object address in this census exactly as `bin` does.
1386+ if (!t.is_program ()
13741387 && t.kind != mcpp::manifest::Target::TestBinary) continue ;
13751388 auto entry = projectRoot / t.main ;
13761389 if (scannedSources.contains (entry)) continue ;
@@ -1792,7 +1805,26 @@ make_plan(const mcpp::manifest::Manifest& manifest,
17921805 lu.kind = LinkUnit::TestBinary;
17931806 lu.output = target_output (t, naming);
17941807 if (!t.main .empty ()) lu.entryMain = projectRoot / t.main ;
1808+ } else if (t.kind == mcpp::manifest::Target::Application
1809+ && mcpp::toolchain::triple::application_form (targetTriple)
1810+ == mcpp::toolchain::triple::ApplicationForm::SharedObject) {
1811+ // #622 A3: on `*-linux-android` an `app` links as `shared` does --
1812+ // this row is the one case the form function answers
1813+ // `SharedObject` for. `main` still names the file compiled INTO
1814+ // the library (its actual entry is the platform's JNI/
1815+ // NativeActivity contract, not mcpp's), so it is synthesized the
1816+ // same way a `Binary`'s entry is, below.
1817+ lu.kind = LinkUnit::SharedLibrary;
1818+ lu.output = target_output (t, naming, /* asSharedObject=*/ true );
1819+ lu.importLibrary = import_library_for (t, naming);
1820+ lu.soname = t.soname ;
1821+ lu.exportPatterns = t.exportPatterns ;
1822+ lu.runtimeAliases = runtime_aliases_for_target (t, naming);
1823+ if (!t.main .empty ()) lu.entryMain = projectRoot / t.main ;
17951824 } else {
1825+ // `Binary`, or an `Application` whose row form is `Executable`
1826+ // (every row this record verifies except Android) -- identical
1827+ // to `bin` in every respect, which is the point of the kind.
17961828 lu.kind = LinkUnit::Binary;
17971829 lu.output = target_output (t, naming);
17981830 if (!t.main .empty ()) lu.entryMain = projectRoot / t.main ;
@@ -1817,7 +1849,13 @@ make_plan(const mcpp::manifest::Manifest& manifest,
18171849 // false → inline (the pre-archive behavior, always provides the entry).
18181850 bool entryDefinesMain = lu.entryMain && source_defines_main (*lu.entryMain );
18191851
1820- if ((lu.kind == LinkUnit::Binary || lu.kind == LinkUnit::TestBinary) && lu.entryMain ) {
1852+ // #622 A3: a `SharedLibrary` unit only ever carries `entryMain` when
1853+ // it is an `app` on the Android row (an ordinary `kind = "shared"`
1854+ // target never sets it) -- so this condition widens to synthesize
1855+ // that translation unit exactly as `Binary`'s is, without touching
1856+ // an ordinary shared library's compile set.
1857+ if ((lu.kind == LinkUnit::Binary || lu.kind == LinkUnit::TestBinary
1858+ || lu.kind == LinkUnit::SharedLibrary) && lu.entryMain ) {
18211859 // Synthesize the entry main's compile unit. Its object path is
18221860 // NOT computed here — it comes from the shared `object_for`
18231861 // disambiguator below, so the link input matches the compile edge
0 commit comments