fix: pmm self install/upgrade now takes effect immediately

- pdm_install_file: when the 'pmm' package is installed, copy the freshly
  built binary over the currently-running pmm path (e.g. /usr/local/bin/pmm)
  and clear the shell command hash, so 'pmm -v' reflects the new version
  without a manual 'hash -r'. Falls back to a hint when the target dir is
  not writable (non-root).
- pmm_add_to_path: prepend PMM dirs on Windows instead of appending, so
  ~/.pmm/root takes priority over /usr/local/bin (matches Linux .bashrc).
- release.yml: add -D_GNU_SOURCE so newer gcc exposes strdup/popen/realpath
  (fixes implicit-declaration build errors).
- CHANGELOG/0.3.7.md
这个提交包含在:
JGZYES
2026-09-05 14:39:29 +08:00
父节点 fcec803c16
当前提交 1b152e950b
修改 5 个文件,包含 84 行新增12 行删除
+1 -1
查看文件
@@ -53,7 +53,7 @@ jobs:
mkdir -p out
VER=${{ github.ref_type == 'tag' && github.ref_name || (github.event.inputs.version != '' && github.event.inputs.version || 'dev') }}
VER=${VER#v}
${{ matrix.cc }} -O2 -Wall -static -std=c11 -D_FORTIFY_SOURCE=0 -U_FORTIFY_SOURCE -DPMM_VERSION=\"$VER\" -o out/${{ matrix.bin }} \
${{ matrix.cc }} -O2 -Wall -static -std=c11 -D_GNU_SOURCE -D_FORTIFY_SOURCE=0 -U_FORTIFY_SOURCE -DPMM_VERSION=\"$VER\" -o out/${{ matrix.bin }} \
src/main.c src/json.c src/ini.c src/pmm.c src/http.c src/repo.c src/install.c \
src/sha256.c src/sha1.c src/mirrors.c src/pdm.c src/out.c src/i18n.c
+13
查看文件
@@ -0,0 +1,13 @@
# PMM v0.3.7
## 修复
- **安装/升级 `pmm` 自身后「不生效」**:之前新二进制只装进 `~/.pmm/root`,从不覆盖当前正在运行的 pmm 路径,
导致 apt 装的 `/usr/local/bin/pmm` 一直挡在前面、或升级后需 `hash -r` 才生效。现在安装 `pmm` 包时会把新二进制
**同步覆盖到当前运行的 pmm 路径**(如 `/usr/local/bin/pmm`)并自动执行 `hash -r``pmm -v` 立即显示新版本。
- Windows 上 `pmm_add_to_path`**append** 改为 **prepend**:PMM 目录排在 PATH 前面,避免被 `/usr/local/bin`
等系统目录覆盖(与 Linux 的 `~/.bashrc` 行为一致)。
- 构建命令增加 `-D_GNU_SOURCE`,修复新版 gcc 下 `strdup`/`popen`/`realpath` 的隐式声明编译错误。
## 说明
- 若运行用户无权限覆盖 `/usr/local/bin/pmm`(非 root),会打印黄色提示:新版本位于
`~/.pmm/root/usr/local/bin/pmm`,请将其加入 PATH 并在当前 shell 运行 `hash -r`
+1
查看文件
@@ -55,6 +55,7 @@ static const char *kBuiltinZh =
"\"msg.checksum-ok\":\"%s 校验 OK: %s\",\"msg.checksum-mismatch\":\"校验不匹配 (%s)!\","
"\"msg.resolving-dep\":\"正在解析依赖 %s\",\"msg.registry-ok\":\"注册表 sha256 OK: %s\","
"\"msg.path-exists\":\"PATH 已包含 PMM 目录\",\"msg.err.registry-not-found\":\"找不到软件包 '%s'\","
"\"msg.pmm-self-replaced\":\"pmm 已更新到 %s\",\"msg.pmm-self-hint\":\"无法覆盖 %s(需 root/管理员);新版本在 %s。请将其加入 PATH 并在当前 shell 运行 hash -r\","
"\"msg.err.usage\":\"用法\",\"msg.err.unknown-cmd\":\"未知命令 '%s' (试试 'pmm help')\","
"\"msg.err.download-failed\":\"下载语言包 %s 失败\",\"msg.err.invalid-locale\":\"无效语言 %s\","
"\"msg.err.not-found\":\"找不到软件包 '%s'\",\"msg.err.failed-install\":\"安装 %s 失败\","
+54
查看文件
@@ -393,6 +393,51 @@ static void restore_aside(const char *target) {
rename(old, target);
}
/* Locate the freshly-installed pmm binary under `root`. Fills `out` with the
* full path and returns 0 on success, 1 if it couldn't be found. */
static int find_pmm_binary(const char *root, char *out, size_t outsz) {
#ifdef _WIN32
const char *names[] = { "pmm.exe", "pmm" };
#else
const char *names[] = { "pmm" };
#endif
const char *dirms[] = { "/bin", "/usr/local/bin", "/", NULL };
for (int d = 0; dirms[d]; d++) {
for (size_t k = 0; k < sizeof(names) / sizeof(names[0]); k++) {
snprintf(out, outsz, "%s%s/%s", root, dirms[d], names[k]);
struct stat st;
if (stat(out, &st) == 0 && S_ISREG(st.st_mode)) return 0;
}
}
return 1;
}
/* After installing/upgrading the `pmm` tool itself, make the newly installed
* binary take effect immediately: copy it over the currently-running pmm
* (so an apt/system /usr/local/bin/pmm is replaced) and clear the shell's
* command hash so `pmm -v` reflects the new version without `hash -r`. */
static void pmm_sync_self(const char *newbin) {
const char *self = pmm_self_path();
if (!self || !*self) return;
if (path_eq_ci(newbin, self)) return; /* already the running image */
int moved = move_self_aside(self);
if (copy_file(newbin, self) != 0) {
if (moved) restore_aside(self); /* put the old image back */
pmm_warn("%s", pmm_tr_fmt("msg.pmm-self-hint", self, newbin, newbin));
return;
}
if (moved) {
char old[1500];
snprintf(old, sizeof(old), "%s.old", self);
remove(old); /* drop the old image */
}
pmm_success("%s", pmm_tr_fmt("msg.pmm-self-replaced", self));
#ifndef _WIN32
{ char cx[1600]; snprintf(cx, sizeof(cx), "chmod +x \"%s\" 2>/dev/null || true", self); system(cx); }
system("hash -r 2>/dev/null || true");
#endif
}
int pdm_info(const char *pdmfile) {
pmm_info("%s\n", base_name(pdmfile));
/* Copy into the pm dir + chdir, then use relative names (works with both
@@ -576,6 +621,15 @@ int pdm_install_file(const char *pdmfile) {
}
#endif
/* If we just installed/upgraded the pmm tool itself, make the new binary
* take effect immediately: replace the running image and clear the hash.
* (Otherwise an apt/system /usr/local/bin/pmm keeps shadowing it.) */
if (pkg && strcmp(pkg, "pmm") == 0) {
char nb[1400];
if (find_pmm_binary(root, nb, sizeof(nb)) == 0)
pmm_sync_self(nb);
}
free(pkg); if (ver) free(ver);
return 0;
}
+15 -11
查看文件
@@ -393,26 +393,30 @@ void pmm_add_to_path(void) {
}
#endif
/* append missing dirs (bounds-safe: never let size_t underflow) */
char next[17000] = "";
snprintf(next, sizeof(next), "%s", cur);
/* prepend any missing PMM dirs so they take priority over /usr/local/bin
* (an apt/system install could otherwise shadow a pmm-managed binary). */
char prefix[17000] = "";
size_t plen = 0;
int added = 0;
for (int i = 0; i < n; i++) {
if (!list[i][0]) continue;
if (path_has(next, list[i], ';') || path_has(next, list[i], ':')) continue;
size_t l = strlen(next);
/* ensure a separator between entries */
if (l && next[l-1] != ';' && next[l-1] != ':' && l + 1 < sizeof(next)) {
next[l] = ';'; next[l+1] = '\0'; l++;
}
size_t room = sizeof(next) - l - 1;
if (path_has(cur, list[i], ';') || path_has(cur, list[i], ':')
|| path_has(prefix, list[i], ';') || path_has(prefix, list[i], ':')) continue;
if (plen) { if (plen + 1 < sizeof(prefix)) prefix[plen++] = ';'; }
size_t room = sizeof(prefix) - plen - 1;
if (room <= 0) break; /* no space left: stop */
snprintf(next + l, room + 1, "%s", list[i]);
snprintf(prefix + plen, room + 1, "%s", list[i]);
plen += strlen(list[i]);
added++;
pmm_info("%s", pmm_tr_fmt("msg.path-adding", list[i]));
}
if (!added) { pmm_info(pmm_tr("msg.path-exists")); return; }
/* final value: PMM dirs (prefix) then the existing startup PATH (cur).
* Only Windows persists it (setx); Linux prepends via .bashrc below. */
char next[17000] = "";
snprintf(next, sizeof(next), "%s;%s", prefix, cur);
#ifdef _WIN32
{
char cmd[17001];