19 Commits

Author SHA1 Message Date
Simone cdb61af358 feat(linux): per-config + total wall-clock build timing
Print a timing summary (each config status+seconds and total wall
clock) for both parallel and sequential paths, mirroring the Windows
build output. Helps compare Linux vs Windows and track perf changes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 22:05:14 +02:00
Simone 8155d78230 fix(build): require VC C++ tools in vswhere query
Two 18.x installs coexisted: VS2026 Community (full IDE, has VC v180)
and a BuildTools at (x86)\Microsoft Visual Studio\18\BuildTools that
has MSBuild but NO VC v180 targets. The vswhere query only required
Microsoft.Component.MSBuild, so -latest could (and did) return the
C++-less BuildTools -> MSB4019. Also require
Microsoft.VisualStudio.Component.VC.Tools.x86.x64 so only installs
with the C++ toolset match; the broken BuildTools is excluded and
2026\Community is selected. CI VM BuildTools has the C++ workload so
it still matches.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 22:00:10 +02:00
Simone b052497701 fix(build): pass -prerelease to vswhere (find VS2026)
VS2026 ships on the prerelease channel; vswhere omits it unless
-prerelease is given. Without it _find_msbuild_via_vswhere returned
nothing, so the path fallback picked an incomplete
(x86)\Microsoft Visual Studio\18\BuildTools install (MSBuild present
but no VC v180 targets) -> MSB4019 on hosts that have VS2026 IDE but
not BuildTools. -prerelease is harmless on the CI VM (BuildTools is
found regardless).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 21:57:13 +02:00
Simone ea71e0d8b2 perf(linux): full make -j per parallel config (no //nconfigs split)
Splitting make -j across configs (jobs // nconfigs) made each parallel
config single-threaded on a 4-vCPU VM, so 3 concurrent serial builds
were as slow as the old sequential path (~7min). Windows (MSBuild)
runs the 3 configs in parallel each using all cores and finishes in
~2min on the same vCPU/RAM. Mirror that: give every parallel config
the full make -j; the scheduler absorbs the mild oversubscription.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 21:26:23 +02:00
Simone c6e74909b4 perf(linux): build configs in parallel (default)
Linux built the 3 configs sequentially (~3x a single config); Windows
already parallelizes. Run them concurrently via ThreadPoolExecutor,
splitting make -j across configs to avoid CPU oversubscription.
Symlink setup serialized with a lock. --no-parallel restores
sequential. Brings Linux wall time close to Windows.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 21:07:02 +02:00
Simone a9643c78e7 feat(build): add --dist to all remaining legacy Windows build scripts
Same --dist redirect (output to <repo>/dist/<config> instead of
plugins/<config>) applied to every per-version Windows script so the
flag works regardless of --7zip-version/--toolset the dispatcher
selects. Mirrors build_plugin_2601_vs2026.py + the Linux path.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 20:35:47 +02:00
Simone 57133b71ee feat(build): add --dist to redirect output to dist/ instead of plugins/
When --dist is passed (forwarded by build_plugin.py to the per-version
script), built nsis7z.dll files are copied to <repo>/dist/<config>
instead of <repo>/plugins/<config>. Lets CI collect a clean dist/
tree without touching the committed plugins/ dir. Windows
(2601_vs2026) and Linux build paths both supported.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 20:33:37 +02:00
Simone 00ddcbd9ff ci: remove [skip ci] from dist pushback commit message 2026-05-05 00:39:40 +02:00
github-actions[bot] eb29dfe1f9 chore: update dist/ DLLs for v2.3.1 [skip ci] 2026-05-04 22:38:30 +00:00
Simone a731bedad0 fix: handle empty 204 response body in Gitea API calls 2026-05-05 00:34:33 +02:00
Simone 1aa358a78f fix: correct Gitea release asset DELETE endpoint path 2026-05-05 00:28:27 +02:00
Simone 505c9a609f ci: push back dist/ DLLs to Gitea after release build 2026-05-05 00:21:40 +02:00
Simone bfd4cff9ff ci: append empty line and -- to GitHub release body 2026-05-05 00:17:32 +02:00
Simone 002b7b4d54 ci: remove Gitea notice footer from GitHub release body 2026-05-05 00:16:37 +02:00
Simone e0dc59bb7d chore: release v2.3.1 2026-05-05 00:12:28 +02:00
Simone d3ab0cf488 chore: update release notes for version 2.3.0 and add GitHub release synchronization script
Co-authored-by: Copilot <copilot@github.com>
2026-05-05 00:08:59 +02:00
Simone 919395a423 fix: remove obsolete nsis7z.dll binaries for amd64-unicode, x86-ansi, and x86-unicode plugins 2026-05-04 19:33:42 +02:00
Simone c2c91b9e48 fix: use #include_next in Linux overlay shims to prevent infinite recursion
All 6 case-sensitivity shim headers in tools/linux/overlay/include/
now use #include_next instead of #include to forward to the real
MinGW system header, avoiding infinite preprocessor recursion on
Linux (Debian/WSL) where overlay/include is first in the search path.

Fixes build failures on Debian WSL (GCC 12) for all configurations.
Also update nsis7z.dll binaries built with the fixed overlay.
2026-05-03 04:04:20 +02:00
Simone f4cd9b9bf2 fix: update nsis7z.dll binaries for amd64, x86-ansi, and x86-unicode plugins 2026-05-03 03:20:13 +02:00
33 changed files with 372 additions and 61 deletions
+25 -8
View File
@@ -139,11 +139,8 @@ jobs:
/^## / && index($0, ver) { found=1; next }
found && /^## / { exit }
found { print }
' CHANGELOG.md > release_body.md
# Append Gitea notice
echo "" >> release_body.md
echo "---" >> release_body.md
echo "_Repository primario: vedi link Gitea nel README. Il mirror GitHub è generato automaticamente._" >> release_body.md
' CHANGELOG.md > release_body.md echo "" >> release_body.md
echo "--" >> release_body.md
- name: Package plugins ZIP
id: pkg
@@ -211,7 +208,8 @@ jobs:
},
)
with urllib.request.urlopen(req) as resp:
return json.loads(resp.read().decode("utf-8"))
raw = resp.read()
return json.loads(raw) if raw else {}
def upload_asset(rid, path, auth_scheme):
p = Path(path)
@@ -234,7 +232,8 @@ jobs:
},
)
with urllib.request.urlopen(req) as resp:
return json.loads(resp.read().decode("utf-8"))
raw = resp.read()
return json.loads(raw) if raw else {}
auth_scheme = None
last_error = None
@@ -291,7 +290,7 @@ jobs:
upload_names = {zip_name, "SHA256SUMS.txt"}
for asset in existing:
if asset["name"] in upload_names:
request("DELETE", f"/repos/{repo}/releases/assets/{asset['id']}", auth_scheme=auth_scheme)
request("DELETE", f"/repos/{repo}/releases/{rid}/assets/{asset['id']}", auth_scheme=auth_scheme)
print(f"Deleted existing asset: {asset['name']}")
# Carica ZIP e SHA256SUMS
@@ -303,3 +302,21 @@ jobs:
print(f"WARNING: {f} not found, skipping upload")
PY
- name: Push back dist/ DLLs to Gitea
env:
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
run: |
tag="${{ steps.tag.outputs.tag }}"
# Copia le DLL compilate in dist/
for dir in amd64-unicode x86-ansi x86-unicode; do
mkdir -p "dist/$dir"
cp "dll-bundle/$dir/nsis7z.dll" "dist/$dir/nsis7z.dll"
done
# Configura git con le credenziali Gitea
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git remote add gitea "https://x-token:${GITEA_TOKEN}@gitea.emulab.it/Simone/nsis-plugin-ns7zip.git"
git add dist/
git diff --cached --quiet || git commit -m "chore: update dist/ DLLs for ${tag}"
git push gitea HEAD:main
+12 -1
View File
@@ -7,6 +7,15 @@ e il progetto aderisce al [Semantic Versioning](https://semver.org/spec/v2.0.0.h
## [Unreleased]
## [2.3.1] — 2026-05-05
### Added
- `tools/update_github_releases.py`: one-shot script to sync GitHub Release bodies via API using local `tools/release-notes/` snippets
### Fixed
- Linux overlay shims (`tools/linux/overlay/include/`) now use `#include_next` instead of `#include` to forward to the real MinGW system header, avoiding infinite preprocessor recursion on Linux/WSL (GCC 12+)
- Removed stale prebuilt `plugins/*/nsis7z.dll` binaries from the repository
## [2.3.0] — 2026-05-03
### Added
@@ -83,7 +92,9 @@ e il progetto aderisce al [Semantic Versioning](https://semver.org/spec/v2.0.0.h
- CI/CD via GitHub Actions (mirror automatico Gitea → GitHub)
- Documentazione completa (README, CONTRIBUTING, SECURITY)
[Unreleased]: https://gitea.emulab.it/Simone/nsis-plugin-ns7zip/compare/v2.2.2...HEAD
[Unreleased]: https://gitea.emulab.it/Simone/nsis-plugin-ns7zip/compare/v2.3.1...HEAD
[2.3.1]: https://gitea.emulab.it/Simone/nsis-plugin-ns7zip/compare/v2.3.0...v2.3.1
[2.3.0]: https://gitea.emulab.it/Simone/nsis-plugin-ns7zip/compare/v2.2.2...v2.3.0
[2.2.2]: https://gitea.emulab.it/Simone/nsis-plugin-ns7zip/compare/v2.2.1...v2.2.2
[2.2.1]: https://gitea.emulab.it/Simone/nsis-plugin-ns7zip/compare/v2.2.0...v2.2.1
[2.2.0]: https://gitea.emulab.it/Simone/nsis-plugin-ns7zip/compare/v2.1.0...v2.2.0
+1 -1
View File
@@ -1 +1 @@
2.3.0
2.3.1
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+7
View File
@@ -141,7 +141,9 @@ def _find_msbuild_via_vswhere(vs_version: str) -> 'Optional[Tuple[Path, str, str
str(_VSWHERE_PATH),
'-version', _VS_VERSION_RANGE[ver],
'-latest',
'-prerelease',
'-requires', 'Microsoft.Component.MSBuild',
'Microsoft.VisualStudio.Component.VC.Tools.x86.x64',
'-find', r'MSBuild\**\Bin\MSBuild.exe',
],
capture_output=True, text=True, timeout=15,
@@ -747,10 +749,15 @@ Examples:
help='Visual Studio version to use (default: auto - tries 2026 then 2022)'
)
parser.add_argument('--dist', action='store_true',
help='Copy built DLLs to <repo>/dist/<config> instead of <repo>/plugins/<config>')
args = parser.parse_args()
# Get project paths early for list-project
project_dir, project_file, plugins_dir = get_project_paths()
# --dist: redirect output root from <repo>/plugins to <repo>/dist.
if args.dist:
plugins_dir = plugins_dir.parent / 'dist'
# List project configurations
if args.list_project:
+7
View File
@@ -150,7 +150,9 @@ def _find_msbuild_via_vswhere(vs_version: str) -> 'Optional[Tuple[Path, str, str
'-products', '*',
'-version', _VS_VERSION_RANGE[ver],
'-latest',
'-prerelease',
'-requires', 'Microsoft.Component.MSBuild',
'Microsoft.VisualStudio.Component.VC.Tools.x86.x64',
'-find', r'MSBuild\**\Bin\MSBuild.exe',
],
capture_output=True, text=True, timeout=15,
@@ -763,10 +765,15 @@ Examples:
help='Visual Studio version to use (default: auto - tries 2026 then 2022)'
)
parser.add_argument('--dist', action='store_true',
help='Copy built DLLs to <repo>/dist/<config> instead of <repo>/plugins/<config>')
args = parser.parse_args()
# Get project paths early for list-project
project_dir, project_file, plugins_dir = get_project_paths()
# --dist: redirect output root from <repo>/plugins to <repo>/dist.
if args.dist:
plugins_dir = plugins_dir.parent / 'dist'
# List project configurations
if args.list_project:
+7
View File
@@ -150,7 +150,9 @@ def _find_msbuild_via_vswhere(vs_version: str) -> 'Optional[Tuple[Path, str, str
'-products', '*',
'-version', _VS_VERSION_RANGE[ver],
'-latest',
'-prerelease',
'-requires', 'Microsoft.Component.MSBuild',
'Microsoft.VisualStudio.Component.VC.Tools.x86.x64',
'-find', r'MSBuild\**\Bin\MSBuild.exe',
],
capture_output=True, text=True, timeout=15,
@@ -766,6 +768,8 @@ Examples:
help='Visual Studio version to use (default: auto - tries 2026 then 2022)'
)
parser.add_argument('--dist', action='store_true',
help='Copy built DLLs to <repo>/dist/<config> instead of <repo>/plugins/<config>')
args = parser.parse_args()
# Find MSBuild early (needed for project path selection)
@@ -777,6 +781,9 @@ Examples:
# Get project paths (project file depends on toolset)
project_dir, project_file, plugins_dir = get_project_paths(platform_toolset)
# --dist: redirect output root from <repo>/plugins to <repo>/dist.
if args.dist:
plugins_dir = plugins_dir.parent / 'dist'
# List project configurations
if args.list_project:
+8
View File
@@ -150,7 +150,9 @@ def _find_msbuild_via_vswhere(vs_version: str) -> 'Optional[Tuple[Path, str, str
'-products', '*',
'-version', _VS_VERSION_RANGE[ver],
'-latest',
'-prerelease',
'-requires', 'Microsoft.Component.MSBuild',
'Microsoft.VisualStudio.Component.VC.Tools.x86.x64',
'-find', r'MSBuild\**\Bin\MSBuild.exe',
],
capture_output=True, text=True, timeout=15,
@@ -630,6 +632,9 @@ Examples:
help='Disable additional build optimizations')
parser.add_argument('--vs-version', choices=['auto', '2026', '2022'], default='auto',
help='Visual Studio version to use (default: auto)')
parser.add_argument('--dist', action='store_true',
help='Copy built DLLs to <repo>/dist/<config> instead '
'of <repo>/plugins/<config>')
args = parser.parse_args()
@@ -642,6 +647,9 @@ Examples:
# Get project paths
project_dir, project_file, plugins_dir = get_project_paths(platform_toolset)
# --dist: redirect the output root from <repo>/plugins to <repo>/dist.
if args.dist:
plugins_dir = plugins_dir.parent / 'dist'
if args.list_project:
if not project_file.exists():
+7
View File
@@ -146,7 +146,9 @@ def _find_msbuild_via_vswhere(vs_version: str) -> 'Optional[Tuple[Path, str, str
str(_VSWHERE_PATH),
'-version', _VS_VERSION_RANGE[ver],
'-latest',
'-prerelease',
'-requires', 'Microsoft.Component.MSBuild',
'Microsoft.VisualStudio.Component.VC.Tools.x86.x64',
'-find', r'MSBuild\**\Bin\MSBuild.exe',
],
capture_output=True, text=True, timeout=15,
@@ -747,10 +749,15 @@ Examples:
help='Visual Studio version to use (default: auto - tries 2026 then 2022)'
)
parser.add_argument('--dist', action='store_true',
help='Copy built DLLs to <repo>/dist/<config> instead of <repo>/plugins/<config>')
args = parser.parse_args()
# Get project paths early for list-project
project_dir, project_file, plugins_dir = get_project_paths()
# --dist: redirect output root from <repo>/plugins to <repo>/dist.
if args.dist:
plugins_dir = plugins_dir.parent / 'dist'
# List project configurations
if args.list_project:
+7
View File
@@ -150,7 +150,9 @@ def _find_msbuild_via_vswhere(vs_version: str) -> 'Optional[Tuple[Path, str, str
'-products', '*',
'-version', _VS_VERSION_RANGE[ver],
'-latest',
'-prerelease',
'-requires', 'Microsoft.Component.MSBuild',
'Microsoft.VisualStudio.Component.VC.Tools.x86.x64',
'-find', r'MSBuild\**\Bin\MSBuild.exe',
],
capture_output=True, text=True, timeout=15,
@@ -763,10 +765,15 @@ Examples:
help='Visual Studio version to use (default: auto - tries 2026 then 2022)'
)
parser.add_argument('--dist', action='store_true',
help='Copy built DLLs to <repo>/dist/<config> instead of <repo>/plugins/<config>')
args = parser.parse_args()
# Get project paths early for list-project
project_dir, project_file, plugins_dir = get_project_paths()
# --dist: redirect output root from <repo>/plugins to <repo>/dist.
if args.dist:
plugins_dir = plugins_dir.parent / 'dist'
# List project configurations
if args.list_project:
+7
View File
@@ -150,7 +150,9 @@ def _find_msbuild_via_vswhere(vs_version: str) -> 'Optional[Tuple[Path, str, str
'-products', '*',
'-version', _VS_VERSION_RANGE[ver],
'-latest',
'-prerelease',
'-requires', 'Microsoft.Component.MSBuild',
'Microsoft.VisualStudio.Component.VC.Tools.x86.x64',
'-find', r'MSBuild\**\Bin\MSBuild.exe',
],
capture_output=True, text=True, timeout=15,
@@ -631,6 +633,8 @@ Examples:
parser.add_argument('--vs-version', choices=['auto', '2026', '2022'], default='auto',
help='Visual Studio version to use (default: auto)')
parser.add_argument('--dist', action='store_true',
help='Copy built DLLs to <repo>/dist/<config> instead of <repo>/plugins/<config>')
args = parser.parse_args()
# Find MSBuild
@@ -642,6 +646,9 @@ Examples:
# Get project paths
project_dir, project_file, plugins_dir = get_project_paths(platform_toolset)
# --dist: redirect output root from <repo>/plugins to <repo>/dist.
if args.dist:
plugins_dir = plugins_dir.parent / 'dist'
if args.list_project:
if not project_file.exists():
+107 -12
View File
@@ -15,8 +15,15 @@ import os
import shutil
import subprocess
import sys
import threading
import time
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path
# Symlink creation is shared across configs; serialize it so parallel
# per-config builds don't race recreating the bundle symlinks.
_SYMLINK_LOCK = threading.Lock()
ROOT = Path(__file__).resolve().parents[2]
OVERLAY = ROOT / "tools" / "linux" / "overlay"
@@ -151,14 +158,17 @@ def _build_one(
jobs: int,
clean: bool,
cleanup_artifacts: bool,
dist: bool = False,
) -> int:
layout = VERSION_LAYOUT[zip_version]
vendor_7zip = layout["vendor_7zip"]
bundle_dir = layout["bundle_dir"]
# Symlinks are not committed to git; recreate them on Linux if missing.
# Serialized: concurrent per-config builds would race here.
if zip_version in VERSION_LAYOUT:
_ensure_bundle_symlinks(zip_version, bundle_dir, vendor_7zip)
with _SYMLINK_LOCK:
_ensure_bundle_symlinks(zip_version, bundle_dir, vendor_7zip)
cfg = CONFIGS[cfg_name]
triplet = cfg["triplet"]
@@ -243,7 +253,10 @@ def _build_one(
print(f"ERROR: expected artifact not found: {built}", file=sys.stderr)
return 1
dest = ROOT / cfg["dest"]
rel_dest = cfg["dest"]
if dist and rel_dest.startswith("plugins/"):
rel_dest = "dist/" + rel_dest[len("plugins/"):]
dest = ROOT / rel_dest
dest.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(built, dest)
@@ -257,6 +270,26 @@ def _build_one(
return 0
def _print_timing(
order: list[str],
codes: dict[str, int],
durations: dict[str, float],
wall: float,
) -> None:
"""Per-config + total wall-clock timing summary."""
print("\n" + "=" * 50)
print("[linux] Build timing")
print("-" * 50)
for cfg in order:
status = "OK " if codes.get(cfg) == 0 else "FAIL"
secs = durations.get(cfg)
secs_s = f"{secs:6.1f}s" if secs is not None else " N/A"
print(f" {status} {cfg:<13} {secs_s}")
print("-" * 50)
print(f" TOTAL wall clock {wall:6.1f}s")
print("=" * 50)
def main() -> int:
parser = argparse.ArgumentParser(description="Linux build path for nsis7z")
parser.add_argument(
@@ -303,6 +336,24 @@ def main() -> int:
help="Keep per-config build artifact directories (_o_*)",
)
parser.add_argument("--verbose", action="store_true", help="Verbose output")
parser.add_argument(
"--dist",
action="store_true",
help="Copy built DLLs to <repo>/dist/<config> instead of "
"<repo>/plugins/<config>",
)
parser.add_argument(
"--parallel",
action="store_true",
default=True,
help="Build configs concurrently (default: True)",
)
parser.add_argument(
"--no-parallel",
action="store_false",
dest="parallel",
help="Build configs one at a time",
)
args = parser.parse_args()
_require_tool("make")
@@ -319,17 +370,61 @@ def main() -> int:
wanted = list(CONFIGS.keys()) if "all" in args.configs else args.configs
for cfg_name in wanted:
code = _build_one(
args.zip_version,
cfg_name,
args.verbose,
jobs,
args.clean,
args.cleanup_artifacts,
if args.parallel and len(wanted) > 1:
# Each config gets the FULL make -j (no //nconfigs split). The
# configs are independent; mild CPU oversubscription is handled
# by the scheduler and is still far faster than serial — this is
# exactly what the Windows (MSBuild) parallel build does, which
# finishes in ~2min on the same vCPU/RAM. Splitting -j across
# configs neutered intra-config parallelism (on a 4-vCPU VM it
# became make -j1) and gave no speedup.
per_jobs = jobs
print(
f"[linux] Building {len(wanted)} configs in parallel "
f"(make -j{per_jobs} each)"
)
if code != 0:
return code
codes: dict[str, int] = {}
durations: dict[str, float] = {}
def _timed(cfg: str, jb: int) -> None:
t0 = time.perf_counter()
codes[cfg] = _build_one(
args.zip_version, cfg, args.verbose, jb,
args.clean, args.cleanup_artifacts, args.dist,
)
durations[cfg] = time.perf_counter() - t0
wall0 = time.perf_counter()
with ThreadPoolExecutor(max_workers=len(wanted)) as ex:
futs = [ex.submit(_timed, c, per_jobs) for c in wanted]
for fut in futs:
fut.result()
wall = time.perf_counter() - wall0
_print_timing(wanted, codes, durations, wall)
for name in wanted:
if codes[name] != 0:
return codes[name]
else:
codes = {}
durations = {}
wall0 = time.perf_counter()
for cfg_name in wanted:
t0 = time.perf_counter()
codes[cfg_name] = _build_one(
args.zip_version,
cfg_name,
args.verbose,
jobs,
args.clean,
args.cleanup_artifacts,
args.dist,
)
durations[cfg_name] = time.perf_counter() - t0
if codes[cfg_name] != 0:
_print_timing(wanted, codes, durations,
time.perf_counter() - wall0)
return codes[cfg_name]
_print_timing(wanted, codes, durations, time.perf_counter() - wall0)
print("[linux] Build completed")
return 0
+4 -2
View File
@@ -1,3 +1,5 @@
/* Linux/MinGW cross-build shim: CommCtrl.h → commctrl.h */
/* Linux/MinGW cross-build shim: CommCtrl.h → commctrl.h
* Uses #include_next so this shim is skipped on the recursive search,
* forwarding to the real MinGW system commctrl.h without infinite recursion. */
#pragma GCC system_header
#include <commctrl.h>
#include_next <commctrl.h>
+4 -2
View File
@@ -1,3 +1,5 @@
/* Linux/MinGW cross-build shim: MAPI.h → mapi.h */
/* Linux/MinGW cross-build shim: MAPI.h → mapi.h
* Uses #include_next so this shim is skipped on the recursive search,
* forwarding to the real MinGW system mapi.h without infinite recursion. */
#pragma GCC system_header
#include <mapi.h>
#include_next <mapi.h>
+4 -2
View File
@@ -1,3 +1,5 @@
/* Linux/MinGW cross-build shim: NTSecAPI.h → ntsecapi.h */
/* Linux/MinGW cross-build shim: NTSecAPI.h → ntsecapi.h
* Uses #include_next so this shim is skipped on the recursive search,
* forwarding to the real MinGW system ntsecapi.h without infinite recursion. */
#pragma GCC system_header
#include <ntsecapi.h>
#include_next <ntsecapi.h>
+4 -2
View File
@@ -1,3 +1,5 @@
/* Linux/MinGW cross-build shim: Psapi.h → psapi.h */
/* Linux/MinGW cross-build shim: Psapi.h → psapi.h
* Uses #include_next so this shim is skipped on the recursive search,
* forwarding to the real MinGW system psapi.h without infinite recursion. */
#pragma GCC system_header
#include <psapi.h>
#include_next <psapi.h>
+4 -2
View File
@@ -1,3 +1,5 @@
/* Linux/MinGW cross-build shim: ShlObj.h → shlobj.h */
/* Linux/MinGW cross-build shim: ShlObj.h → shlobj.h
* Uses #include_next so this shim is skipped on the recursive search,
* forwarding to the real MinGW system shlobj.h without infinite recursion. */
#pragma GCC system_header
#include <shlobj.h>
#include_next <shlobj.h>
+4 -2
View File
@@ -1,3 +1,5 @@
/* Linux/MinGW cross-build shim: Windows.h → windows.h */
/* Linux/MinGW cross-build shim: Windows.h → windows.h
* Uses #include_next so this shim is skipped on the recursive search,
* forwarding to the real MinGW system windows.h without infinite recursion. */
#pragma GCC system_header
#include <windows.h>
#include_next <windows.h>
+1 -2
View File
@@ -4,5 +4,4 @@
- CI/CD via GitHub Actions (mirror automatico Gitea → GitHub)
- Documentazione completa (README, CONTRIBUTING, SECURITY)
---
_Repository primario: vedi link Gitea nel README. Il mirror GitHub è generato automaticamente._
--
+1 -2
View File
@@ -4,5 +4,4 @@
- Fixed MSB8020 toolset error on GitHub Actions: `build_plugin_2600_vs2026.py` now detects the installed VS toolset dynamically (falls back to v143 on VS2022 runners)
- Build script for 26.00 selects `Nsis7z_vs2026.vcxproj` (v145) or `Nsis7z.vcxproj` (v143) based on detected toolset
---
_Repository primario: vedi link Gitea nel README. Il mirror GitHub è generato automaticamente._
--
+1 -2
View File
@@ -9,5 +9,4 @@
- `versions/7-zip-zstd` submodule added
- `build_zstd.cmd` top-level build script for the zstd variant
---
_Repository primario: vedi link Gitea nel README. Il mirror GitHub è generato automaticamente._
--
+1 -2
View File
@@ -7,5 +7,4 @@
- `build_plugin.py` now supports both Windows (MSBuild) and Linux (MinGW-w64) targets
- Windows build-script f-string fixes
---
_Repository primario: vedi link Gitea nel README. Il mirror GitHub è generato automaticamente._
--
+1 -2
View File
@@ -4,5 +4,4 @@
### Changed
- GitHub Actions workflows now opt into Node 24 for JavaScript-based actions to avoid Node 20 deprecation warnings
---
_Repository primario: vedi link Gitea nel README. Il mirror GitHub è generato automaticamente._
--
+8
View File
@@ -0,0 +1,8 @@
### Added
- `tools/update_gitea_releases.py`: one-shot script to backfill Gitea release bodies with CHANGELOG content via the Gitea API
- `tools/release-notes/`: per-version Markdown snippets used by the backfill script
### Changed
- Release workflow now extracts the relevant CHANGELOG section and uses it as the GitHub Release body (`body_path`)
- Release workflow adds a step to update the corresponding Gitea release body via API (`GITEA_TOKEN` secret)
- All GitHub Actions upgraded to node24-native major versions (no more Node 20 deprecation warnings)
+14
View File
@@ -0,0 +1,14 @@
### Added
- Auto-recreation of bundle POSIX symlinks on Linux if missing after a fresh clone (`_ensure_bundle_symlinks()` in `tools/linux/build_plugin_linux.py`)
### Changed
- Default 7-zip version changed from `26.00` to `26.01` in `build_plugin.py` and `tools/linux/build_plugin_linux.py`
- Renamed `versions/zstd` → `versions/zstd-bundle` for naming consistency with the other bundle directories
- Auto-generated bundle symlinks are now excluded from git tracking (`.gitignore`)
### Fixed
- Windows build error `C1083: Cannot open include file` for `Common/Common.h`, `IPassword.h`, `C/Alloc.h` and other vendor headers: added `versions/VER/CPP/7zip/UI/Common/` to `AdditionalIncludeDirectories` in all five bundle vcxproj files (25.01, 26.00, 26.01)
- f-string nested-quote syntax errors in all `tools/legacy/` build scripts (patterns `{"="*60}`, `{"-"*50}`, `{'Rebuild' if ...}`): replaced with Python 3.10/3.11-compatible equivalents
### Removed
- One-shot `tools/fix_bundle_vcxproj_paths.py` helper (no longer needed)
+6
View File
@@ -0,0 +1,6 @@
### Added
- `tools/update_github_releases.py`: one-shot script to sync GitHub Release bodies via API using local `tools/release-notes/` snippets
### Fixed
- Linux overlay shims (`tools/linux/overlay/include/`) now use `#include_next` instead of `#include` to forward to the real MinGW system header, avoiding infinite preprocessor recursion on Linux/WSL (GCC 12+)
- Removed stale prebuilt `plugins/*/nsis7z.dll` binaries from the repository
+33 -17
View File
@@ -1,7 +1,8 @@
#!/usr/bin/env python3
"""
One-shot script: aggiorna il body delle release Gitea esistenti
con i file in tools/release-notes/.
Script: sincronizza le release Gitea con i file in tools/release-notes/.
- Se la release esiste → aggiorna il body
- Se la release non esiste → la crea (richiede che il tag git esista già)
Uso:
GITEA_TOKEN=<token> python3 tools/update_gitea_releases.py
@@ -51,28 +52,43 @@ def main() -> None:
if not args.token:
sys.exit("Errore: token mancante. Usa --token oppure imposta GITEA_TOKEN.")
print("Recupero lista release da Gitea...", flush=True)
try:
releases = api("GET", f"/repos/{REPO}/releases?limit=50", args.token)
except urllib.error.HTTPError:
sys.exit("Errore nel recupero delle release.")
existing = {r["tag_name"]: r["id"] for r in releases}
note_files = sorted(NOTES_DIR.glob("v*.md"))
if not note_files:
sys.exit(f"Nessun file trovato in {NOTES_DIR}")
for note_path in note_files:
tag = note_path.stem # es. v2.2.1
tag = note_path.stem
body = note_path.read_text(encoding="utf-8").strip()
print(f"[{tag}] recupero release...", end=" ", flush=True)
try:
release = api("GET", f"/repos/{REPO}/releases/tags/{tag}", args.token)
except urllib.error.HTTPError:
print("SALTATO (release non trovata)")
continue
release_id = release["id"]
print(f"id={release_id}, aggiorno body...", end=" ", flush=True)
try:
api("PATCH", f"/repos/{REPO}/releases/{release_id}", args.token, {"body": body})
print("OK")
except urllib.error.HTTPError:
print("FALLITO")
if tag in existing:
release_id = existing[tag]
print(f"[{tag}] id={release_id}, aggiorno body...", end=" ", flush=True)
try:
api("PATCH", f"/repos/{REPO}/releases/{release_id}", args.token, {"body": body})
print("OK")
except urllib.error.HTTPError:
print("FALLITO")
else:
print(f"[{tag}] release non trovata, creo...", end=" ", flush=True)
try:
result = api("POST", f"/repos/{REPO}/releases", args.token, {
"tag_name": tag,
"name": tag,
"body": body,
"draft": False,
"prerelease": False,
})
print(f"OK (id={result['id']})")
except urllib.error.HTTPError:
print("FALLITO (il tag git esiste su Gitea?)")
if __name__ == "__main__":
+87
View File
@@ -0,0 +1,87 @@
#!/usr/bin/env python3
"""
Script: sincronizza i body delle release GitHub con i file in tools/release-notes/.
- Se la release esiste → aggiorna il body
- Se la release non esiste → la salta (le release GitHub vengono create dal workflow CI)
Uso:
GITHUB_TOKEN=<token> python3 tools/update_github_releases.py
oppure:
python3 tools/update_github_releases.py --token <token>
Richiede un PAT GitHub con permesso 'contents: write' sul repo.
"""
import argparse
import json
import os
import sys
import urllib.request
import urllib.error
from pathlib import Path
GITHUB_BASE = "https://api.github.com"
REPO = "systempal/nsis-plugin-ns7zip"
NOTES_DIR = Path(__file__).parent / "release-notes"
def api(method: str, path: str, token: str, data: dict | None = None) -> dict:
url = f"{GITHUB_BASE}{path}"
body = json.dumps(data).encode() if data else None
req = urllib.request.Request(
url,
data=body,
method=method,
headers={
"Authorization": f"token {token}",
"Content-Type": "application/json",
"Accept": "application/vnd.github+json",
"X-GitHub-Api-Version": "2022-11-28",
"User-Agent": "update_github_releases/1.0",
},
)
try:
with urllib.request.urlopen(req) as resp:
return json.loads(resp.read())
except urllib.error.HTTPError as e:
print(f" HTTP {e.code} {e.reason} — {e.read().decode()}", file=sys.stderr)
raise
def main() -> None:
parser = argparse.ArgumentParser(description="Update GitHub release bodies")
parser.add_argument("--token", default=os.environ.get("GITHUB_TOKEN"), help="GitHub API token")
args = parser.parse_args()
if not args.token:
sys.exit("Errore: token mancante. Usa --token oppure imposta GITHUB_TOKEN.")
note_files = sorted(NOTES_DIR.glob("v*.md"))
if not note_files:
sys.exit(f"Nessun file trovato in {NOTES_DIR}")
for note_path in note_files:
tag = note_path.stem
body = note_path.read_text(encoding="utf-8").strip()
print(f"[{tag}] recupero release...", end=" ", flush=True)
try:
release = api("GET", f"/repos/{REPO}/releases/tags/{tag}", args.token)
except urllib.error.HTTPError as e:
if e.code == 404:
print("SALTATO (release non trovata su GitHub)")
else:
print("ERRORE")
continue
release_id = release["id"]
print(f"id={release_id}, aggiorno body...", end=" ", flush=True)
try:
api("PATCH", f"/repos/{REPO}/releases/{release_id}", args.token, {"body": body})
print("OK")
except urllib.error.HTTPError:
print("FALLITO")
if __name__ == "__main__":
main()