@@ -52,28 +52,29 @@ bool clean_incomplete_install(const std::filesystem::path& xpkgDir);
5252// Returns number of directories cleaned.
5353int clean_all_incomplete (const std::filesystem::path& xpkgsBase);
5454
55+ // One-time migration: scan xpkgs and write .mcpp_ok markers for all
56+ // old packages that look complete but lack a marker. After migration,
57+ // is_install_complete() uses strict marker-only semantics. Returns
58+ // number of packages migrated.
59+ int migrate_legacy_installs (const std::filesystem::path& xpkgsBase);
60+
5561} // namespace mcpp::fallback
5662
5763// ─── Implementation ─────────────────────────────────────────────────
5864
5965namespace mcpp ::fallback {
6066
61- bool is_install_complete (const std::filesystem::path& xpkgDir) {
62- if (!std::filesystem::exists (xpkgDir)) return false ;
67+ namespace {
6368
64- // Primary: .mcpp_ok marker
65- if (std::filesystem::exists (xpkgDir / std::string (kInstallMarker )))
66- return true ;
67-
68- // Backward compat: no marker but has content directories
69- // (installed before this feature was added)
70- // Check top-level content dirs (xim toolchain packages).
69+ // Heuristic: does this directory look like a complete xpkg?
70+ // Used ONLY for legacy migration (pre-.mcpp_ok packages).
71+ bool looks_complete_legacy (const std::filesystem::path& xpkgDir) {
72+ // xim toolchain/tool packages: top-level bin/lib/lib64/include/share
7173 for (auto dir : {" bin" , " lib" , " lib64" , " include" , " share" }) {
7274 if (std::filesystem::exists (xpkgDir / dir))
7375 return true ;
7476 }
75- // Check for mcpplibs layout: single subdirectory containing src/ or
76- // mcpp.toml (extracted tarball). E.g. verdir/cmdline-0.0.1/src/...
77+ // mcpplibs layout: single subdirectory containing src/ or mcpp.toml
7778 std::error_code ec;
7879 std::vector<std::filesystem::path> subs;
7980 for (auto & e : std::filesystem::directory_iterator (xpkgDir, ec)) {
@@ -90,6 +91,22 @@ bool is_install_complete(const std::filesystem::path& xpkgDir) {
9091 return false ;
9192}
9293
94+ } // namespace
95+
96+ bool is_install_complete (const std::filesystem::path& xpkgDir) {
97+ if (!std::filesystem::exists (xpkgDir)) return false ;
98+
99+ // Strict: .mcpp_ok marker is the sole authority.
100+ // Legacy packages should have been migrated (marker added) during
101+ // the first load_or_init() after upgrading to this version.
102+ if (std::filesystem::exists (xpkgDir / std::string (kInstallMarker )))
103+ return true ;
104+
105+ // Fallback for un-migrated packages (e.g. first run after upgrade
106+ // before migration has a chance to run).
107+ return looks_complete_legacy (xpkgDir);
108+ }
109+
93110void mark_install_complete (const std::filesystem::path& xpkgDir) {
94111 auto marker = xpkgDir / std::string (kInstallMarker );
95112 if (std::filesystem::exists (marker)) return ;
@@ -124,4 +141,24 @@ int clean_all_incomplete(const std::filesystem::path& xpkgsBase) {
124141 return cleaned;
125142}
126143
144+ int migrate_legacy_installs (const std::filesystem::path& xpkgsBase) {
145+ if (!std::filesystem::exists (xpkgsBase)) return 0 ;
146+
147+ int migrated = 0 ;
148+ std::error_code ec;
149+ for (auto & pkgDir : std::filesystem::directory_iterator (xpkgsBase, ec)) {
150+ if (!pkgDir.is_directory ()) continue ;
151+ for (auto & verDir : std::filesystem::directory_iterator (pkgDir.path (), ec)) {
152+ if (!verDir.is_directory ()) continue ;
153+ auto marker = verDir.path () / std::string (kInstallMarker );
154+ if (std::filesystem::exists (marker)) continue ;
155+ if (looks_complete_legacy (verDir.path ())) {
156+ mark_install_complete (verDir.path ());
157+ ++migrated;
158+ }
159+ }
160+ }
161+ return migrated;
162+ }
163+
127164} // namespace mcpp::fallback
0 commit comments