mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
toolchain clean old packs
This commit is contained in:
@@ -26,6 +26,8 @@ static int cmake_path_runs(const char* cmake_path);
|
||||
static int toolchain_bin_is_healthy(const char* bin);
|
||||
static void heal_broken_toolchain_pointers(void);
|
||||
static void clear_project_toolchain_stamp(void);
|
||||
static int host_paths_same_file(const char* a, const char* b);
|
||||
static void prune_old_toolchain_tags(const char* keep_pack);
|
||||
#if defined(_WIN32)
|
||||
static int junction_dir(const char* link_path, const char* target_path);
|
||||
static int run_cmdline_wait(const char* cmdline, DWORD* out_code);
|
||||
@@ -2006,6 +2008,125 @@ static int link_or_stamp_project_toolchain(const char* pack_root) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* True when *child* is *parent* or a path under *parent* (dir prefix). */
|
||||
static int path_is_under_dir(const char* child, const char* parent) {
|
||||
char ca[1400], pa[1400];
|
||||
size_t n;
|
||||
if (!child || !child[0] || !parent || !parent[0])
|
||||
return 0;
|
||||
#if defined(_WIN32)
|
||||
{
|
||||
DWORD nc = GetFullPathNameA(child, (DWORD)sizeof(ca), ca, NULL);
|
||||
DWORD np = GetFullPathNameA(parent, (DWORD)sizeof(pa), pa, NULL);
|
||||
if (nc == 0 || nc >= (DWORD)sizeof(ca) || np == 0 ||
|
||||
np >= (DWORD)sizeof(pa))
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
{
|
||||
char* rc = realpath(child, NULL);
|
||||
char* rp = realpath(parent, NULL);
|
||||
if (!rc || !rp) {
|
||||
free(rc);
|
||||
free(rp);
|
||||
return 0;
|
||||
}
|
||||
snprintf(ca, sizeof(ca), "%s", rc);
|
||||
snprintf(pa, sizeof(pa), "%s", rp);
|
||||
free(rc);
|
||||
free(rp);
|
||||
}
|
||||
#endif
|
||||
n = strlen(pa);
|
||||
#if defined(_WIN32)
|
||||
if (_strnicmp(ca, pa, (int)n) != 0)
|
||||
return 0;
|
||||
#else
|
||||
if (strncmp(ca, pa, n) != 0)
|
||||
return 0;
|
||||
#endif
|
||||
if (ca[n] == '\0')
|
||||
return 1;
|
||||
return ca[n] == '/' || ca[n] == '\\';
|
||||
}
|
||||
|
||||
/* After a successful install into the managed cache, drop other versioned
|
||||
* <tag>/ siblings (and *.broken quarantines). Keeps *keep_pack*, latest/, and
|
||||
* dot-directories (staging). Does not touch RETCOMM_TOOLCHAIN_DIR overrides
|
||||
* outside the preferred install root. */
|
||||
static void prune_old_toolchain_tags(const char* keep_pack) {
|
||||
char cache_root[1400];
|
||||
int removed = 0;
|
||||
if (!keep_pack || !keep_pack[0])
|
||||
return;
|
||||
if (!preferred_toolchain_cache_root(cache_root, sizeof(cache_root)))
|
||||
return;
|
||||
if (!path_is_dir(cache_root))
|
||||
return;
|
||||
if (!path_is_under_dir(keep_pack, cache_root) &&
|
||||
!host_paths_same_file(keep_pack, cache_root))
|
||||
return;
|
||||
|
||||
#if defined(_WIN32)
|
||||
{
|
||||
WIN32_FIND_DATAA fd;
|
||||
HANDLE h;
|
||||
char pattern[1400];
|
||||
snprintf(pattern, sizeof(pattern), "%s\\*", cache_root);
|
||||
h = FindFirstFileA(pattern, &fd);
|
||||
if (h == INVALID_HANDLE_VALUE)
|
||||
return;
|
||||
do {
|
||||
char child[1400];
|
||||
if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
||||
continue;
|
||||
if (fd.cFileName[0] == '.')
|
||||
continue;
|
||||
if (_stricmp(fd.cFileName, "latest") == 0)
|
||||
continue;
|
||||
if (!join_path(child, sizeof(child), cache_root, fd.cFileName))
|
||||
continue;
|
||||
if (path_is_under_dir(keep_pack, child) ||
|
||||
host_paths_same_file(keep_pack, child) ||
|
||||
path_is_under_dir(child, keep_pack))
|
||||
continue;
|
||||
if (rmtree_path(child))
|
||||
removed = 1;
|
||||
} while (FindNextFileA(h, &fd));
|
||||
FindClose(h);
|
||||
}
|
||||
#else
|
||||
{
|
||||
DIR* d = opendir(cache_root);
|
||||
struct dirent* ent;
|
||||
if (!d)
|
||||
return;
|
||||
while ((ent = readdir(d)) != NULL) {
|
||||
char child[1400];
|
||||
struct stat st;
|
||||
if (ent->d_name[0] == '.')
|
||||
continue;
|
||||
if (strcmp(ent->d_name, "latest") == 0)
|
||||
continue;
|
||||
if (!join_path(child, sizeof(child), cache_root, ent->d_name))
|
||||
continue;
|
||||
if (lstat(child, &st) != 0)
|
||||
continue;
|
||||
if (!S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode))
|
||||
continue;
|
||||
if (path_is_under_dir(keep_pack, child) ||
|
||||
host_paths_same_file(keep_pack, child) ||
|
||||
path_is_under_dir(child, keep_pack))
|
||||
continue;
|
||||
if (rmtree_path(child))
|
||||
removed = 1;
|
||||
}
|
||||
closedir(d);
|
||||
}
|
||||
#endif
|
||||
(void)removed;
|
||||
}
|
||||
|
||||
static int host_install_toolchain_from_zip(
|
||||
const char* zip_path, RecompLauncherCPrepareProgressFn on_progress,
|
||||
void* progress_ctx, char* err_msg, size_t err_cap) {
|
||||
@@ -2108,11 +2229,15 @@ static int host_install_toolchain_from_zip(
|
||||
if (on_progress)
|
||||
on_progress(progress_ctx, 0.85f, "Activating toolchain…");
|
||||
link_or_stamp_project_toolchain(pack);
|
||||
if (activate_installed_pack_root(pack))
|
||||
return 1;
|
||||
if (join_path(latest, sizeof(latest), cache_root, "latest") &&
|
||||
activate_installed_pack_root(latest))
|
||||
if (activate_installed_pack_root(pack) ||
|
||||
(join_path(latest, sizeof(latest), cache_root, "latest") &&
|
||||
activate_installed_pack_root(latest))) {
|
||||
if (on_progress)
|
||||
on_progress(progress_ctx, 0.92f,
|
||||
"Removing older toolchain installs…");
|
||||
prune_old_toolchain_tags(pack);
|
||||
return 1;
|
||||
}
|
||||
snprintf(err_msg, err_cap,
|
||||
"Extracted toolchain but cmake.exe is missing or will not run "
|
||||
"(bin\\cmake.exe --version failed).");
|
||||
@@ -2392,7 +2517,8 @@ static int path_is_toolchain_latest_leaf(const char* path) {
|
||||
|
||||
/* Remove unusable latest/ under shared cache roots (heal before re-download).
|
||||
* Also rejects packs where cmake runs but clang/ld.lld cannot link (Linux ICU
|
||||
* / missing libxml2). Leaves versioned <tag>/ packs intact. */
|
||||
* / missing libxml2). Versioned <tag>/ siblings are pruned after a successful
|
||||
* install/update (see prune_old_toolchain_tags). */
|
||||
static void heal_broken_toolchain_pointers(void) {
|
||||
char bases[12][1400];
|
||||
int n = collect_toolchain_cache_bases(bases, 12);
|
||||
|
||||
+54
-1
@@ -96,11 +96,62 @@ def clear_project_toolchain_stamp(project_root: Optional[Path]) -> None:
|
||||
pass
|
||||
|
||||
|
||||
def prune_old_toolchain_tags(keep_pack: Path, log=None) -> int:
|
||||
"""Delete sibling versioned ``<tag>/`` dirs under the managed install root.
|
||||
|
||||
Keeps *keep_pack* (and anything under it), ``latest/``, and dot-dirs
|
||||
(staging). Returns the number of removed entries. No-op when *keep_pack*
|
||||
is outside ``preferred_install_root()`` (e.g. ``RETCOMM_TOOLCHAIN_DIR``).
|
||||
"""
|
||||
keep = unwrap_pack_root(Path(keep_pack).expanduser())
|
||||
try:
|
||||
cache_root = preferred_install_root()
|
||||
except OSError:
|
||||
return 0
|
||||
if not cache_root.is_dir():
|
||||
return 0
|
||||
if not (_paths_equal(keep, cache_root) or _is_under(keep, cache_root)):
|
||||
return 0
|
||||
removed = 0
|
||||
try:
|
||||
children = list(cache_root.iterdir())
|
||||
except OSError:
|
||||
return 0
|
||||
for child in children:
|
||||
name = child.name
|
||||
if name.startswith(".") or name == "latest":
|
||||
continue
|
||||
try:
|
||||
if not (child.is_dir() or child.is_symlink()):
|
||||
continue
|
||||
except OSError:
|
||||
continue
|
||||
if (
|
||||
_paths_equal(keep, child)
|
||||
or _is_under(keep, child)
|
||||
or _is_under(child, keep)
|
||||
):
|
||||
continue
|
||||
if log:
|
||||
log(f"Removing old toolchain install: {child}")
|
||||
try:
|
||||
if child.is_symlink() or child.is_file():
|
||||
child.unlink(missing_ok=True)
|
||||
else:
|
||||
shutil.rmtree(child, ignore_errors=True)
|
||||
removed += 1
|
||||
except OSError as exc:
|
||||
if log:
|
||||
log(f"Could not remove old toolchain {child}: {exc}")
|
||||
return removed
|
||||
|
||||
|
||||
def heal_broken_toolchain_pointers(log=None) -> None:
|
||||
"""Remove unusable ``latest`` pointers so ensure can reinstall cleanly.
|
||||
|
||||
Does not delete versioned ``<tag>/`` packs — only broken ``latest``
|
||||
symlinks/directories that lack a runnable bin/cmake.
|
||||
symlinks/directories that lack a runnable bin/cmake. Sibling tags are
|
||||
pruned after a successful install via ``prune_old_toolchain_tags``.
|
||||
"""
|
||||
for base in shared_cache_roots():
|
||||
latest = base / "latest"
|
||||
@@ -736,6 +787,8 @@ def install_from_zip(
|
||||
shutil.rmtree(staging, ignore_errors=True)
|
||||
# latest pointer + idempotent user login PATH (same as zip install.sh).
|
||||
register_toolchain_user_env(root, log=log)
|
||||
# Drop prior versioned <tag>/ installs now that the new pack is active.
|
||||
prune_old_toolchain_tags(root, log=log)
|
||||
if project_root is not None:
|
||||
return materialize_into_project(project_root, root, log=log)
|
||||
return root
|
||||
|
||||
Reference in New Issue
Block a user