Files
nsis-plugin-ns7zip/tools/linux/build_plugin_linux.py
T
Simone 68f5101c64 build: redirect 26.00 builds to bundle directory
- tools/linux/build_plugin_linux.py: point VERSION_LAYOUT['26.00'].bundle_dir
  to versions/26.00-bundle/ (was versions/26.00/); update module docstring.
- tools/legacy/build_plugin_2600_vs2026.py: point project_dir to
  versions/26.00-bundle/ (was versions/26.00/).
- tools/linux/setup_26_01_bundle_symlinks.py: refactor to use bundle_root /
  vendor_root variables and add C/Asm root-level symlink creation, matching
  the new setup_26_00_bundle_symlinks.py convention.
2026-05-03 01:31:30 +02:00

297 lines
10 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""Linux build path for nsis7z plugin.
Builds Windows DLL artifacts from Linux using MinGW-w64 cross compilers.
Supported versions: 7-Zip 25.01, 26.00 and zstd.
All project-owned build-infrastructure files (makefile.gcc, Nsis7z.def) live
under tools/linux/overlay/ and are never written into the vendor source trees.
Build artifacts go to _linux_build/<version>/<cfg>/ (gitignored).
"""
from __future__ import annotations
import argparse
import os
import shutil
import subprocess
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
OVERLAY = ROOT / "tools" / "linux" / "overlay"
BUILD_DIR = ROOT / "_linux_build"
SUPPORTED = {"25.01", "26.00", "26.01", "zstd"}
# For 25.01 the vendor bundle dir already contains our wrapper .cpp files
# alongside the upstream 7-zip sources. make is run with -C pointing there so
# relative paths (../../UI/…) resolve correctly, but the makefile itself comes
# from OVERLAY and the output objects go to BUILD_DIR.
#
# For 26.00, 26.01: the upstream vendor does NOT ship a Nsis7z bundle, so a
# *-bundle wrapper directory mirrors the vendor tree via symlinks and provides
# project-owned sources (NSIS UI, wrapper cpp files, vcxproj).
#
# For zstd the 7-zip C++ sources live in the 7-zip-zstd submodule while our
# NSIS wrapper sits in versions/zstd/. make is run with -C pointing to the
# wrapper dir and VENDOR_7ZIP overridden to the submodule tree so that
# include/source rules resolve correctly without touching the submodule.
VERSION_LAYOUT = {
"25.01": {
"vendor_7zip": ROOT / "versions" / "25.01" / "CPP" / "7zip",
"bundle_dir": ROOT / "versions" / "25.01" / "CPP" / "7zip" / "Bundles" / "Nsis7z",
},
"26.00": {
"vendor_7zip": ROOT / "versions" / "26.00" / "CPP" / "7zip",
"bundle_dir": ROOT / "versions" / "26.00-bundle" / "CPP" / "7zip" / "Bundles" / "Nsis7z",
},
"26.01": {
"vendor_7zip": ROOT / "versions" / "26.01" / "CPP" / "7zip",
"bundle_dir": ROOT / "versions" / "26.01-bundle" / "CPP" / "7zip" / "Bundles" / "Nsis7z",
},
# zstd: make runs in our wrapper dir; vendor_7zip points to submodule tree.
"zstd": {
"vendor_7zip": ROOT / "versions" / "7-zip-zstd" / "CPP" / "7zip",
"bundle_dir": ROOT / "versions" / "zstd" / "CPP" / "7zip" / "Bundles" / "Nsis7z",
},
}
# Per-version C++-only extra flags for GCC/MinGW cross-compilation.
# These compensate for vendor code written exclusively for MSVC.
# Passed via LOCAL_CXXFLAGS_EXTRA → appended to CXXFLAGS only (not CFLAGS).
#
# If flag-based suppression is insufficient (e.g. the code must be modified),
# place a patched copy at tools/linux/overlay/src/<version>/<relative-path>
# and add a rule override in makefile.gcc using the OVERLAY_SRC variable.
VERSION_EXTRA_CXXFLAGS: dict[str, str] = {
# 7-zip 25.01 NSIS UI code was never compiled with GCC:
# - STDMETHODIMP implementations lack `noexcept`/`throw()` while the COM
# interface macros (Z7_COM7F_IMP) declare the virtuals with `throw()`
# (= noexcept in C++17). Fixed per-TU via -include z7_idecl_noexcept_strip.h
# in the specific makefile rule for NsisExtractCallbackConsole.o.
# - Several unused parameters and sign-compare on UInt64 vs. literal -1.
"25.01": "-Wno-sign-compare -Wno-unused-parameter",
}
CONFIGS = {
"x86-ansi": {
"triplet": "i686-w64-mingw32",
"is_x64": "0",
"unicode": False,
"dest": "plugins/x86-ansi/nsis7z.dll",
},
"x86-unicode": {
"triplet": "i686-w64-mingw32",
"is_x64": "0",
"unicode": True,
"dest": "plugins/x86-unicode/nsis7z.dll",
},
"x64-unicode": {
"triplet": "x86_64-w64-mingw32",
"is_x64": "1",
"unicode": True,
"dest": "plugins/amd64-unicode/nsis7z.dll",
},
}
def _require_tool(name: str) -> None:
if shutil.which(name) is None:
print(f"ERROR: required tool not found: {name}", file=sys.stderr)
sys.exit(2)
def _resolve_jobs(value: str) -> int:
if value == "auto":
return max(1, os.cpu_count() or 1)
jobs = int(value)
if jobs < 1:
raise ValueError("jobs must be >= 1")
return jobs
def _build_one(
zip_version: str,
cfg_name: str,
verbose: bool,
jobs: int,
clean: bool,
cleanup_artifacts: bool,
) -> int:
layout = VERSION_LAYOUT[zip_version]
vendor_7zip = layout["vendor_7zip"]
bundle_dir = layout["bundle_dir"]
cfg = CONFIGS[cfg_name]
triplet = cfg["triplet"]
# Output objects go to an absolute path under _linux_build/ so nothing
# temporary is ever written into the vendor source trees.
out_dir = BUILD_DIR / zip_version / cfg_name
out_dir.mkdir(parents=True, exist_ok=True)
local_flags = "-DNDEBUG"
if cfg["unicode"]:
local_flags += " -DUNICODE -D_UNICODE"
else:
# Precomp.h forces UNICODE=1 unconditionally; suppress it for the ansi
# build so that LPTSTR resolves to char* consistently in all TUs.
local_flags += " -DZ7_NO_UNICODE"
# Version-specific workarounds for vendor code that was MSVC-only.
# Flags that are C++-only (e.g. -fpermissive) must go into LOCAL_CXXFLAGS_EXTRA
# which the makefile appends only to CXXFLAGS, not to CFLAGS.
extra_cxx = VERSION_EXTRA_CXXFLAGS.get(zip_version, "")
# CXX_INCLUDE_FLAGS:
# 1. overlay/include case-sensitivity shims (Windows.h → windows.h, …)
# searched first so they shadow the actual MinGW system headers.
# 2. CPP root so vendor #include paths resolve from any working dir.
cpp_root = vendor_7zip.parent # …/CPP
overlay_inc = OVERLAY / "include"
# For bundle-based builds (zstd, 26.01) the UI/NSIS wrapper files live in
# our bundle dir, not in the vendor tree. Override NSIS_DIR accordingly.
bundle_nsis = bundle_dir.parent.parent / "UI" / "NSIS"
base_cmd = [
"make",
"-C", str(bundle_dir),
"-f", str(OVERLAY / "makefile.gcc"),
f"O={out_dir}",
f"DEF_FILE={OVERLAY / 'Nsis7z.def'}",
f"VENDOR_7ZIP={vendor_7zip}",
*([ f"NSIS_DIR={bundle_nsis}" ] if bundle_nsis.is_dir() else []),
"SystemDrive=1",
"IS_MINGW=1",
"MSYSTEM=LINUX",
f"IS_X64={cfg['is_x64']}",
f"CC={triplet}-gcc",
f"CXX={triplet}-g++",
f"RC={triplet}-windres",
f"LOCAL_FLAGS_EXTRA={local_flags} -Wno-unknown-pragmas",
f"LOCAL_CXXFLAGS_EXTRA={extra_cxx}",
# Linux ld is case-sensitive; vendor makefile uses mixed-case lib names
# (lUser32, lOle32, …) that don't exist on disk. Override LIB2 with
# fully-expanded lowercase equivalents.
"LIB2=-loleaut32 -luuid -ladvapi32 -luser32 -lole32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32 -lhtmlhelp",
f"CXX_INCLUDE_FLAGS=-I{overlay_inc} -I{cpp_root}",
]
build_mode = "clean build" if clean else "incremental build"
print(f"[linux] Building {cfg_name} ({zip_version}, {build_mode}, -j{jobs})")
# Don't run 'clean' and 'all' in the same parallel make invocation:
# with -j, GNU make may execute them concurrently and remove objects while
# the linker is already consuming them.
if clean:
proc = subprocess.run(base_cmd + ["clean"], cwd=bundle_dir)
if proc.returncode != 0:
return proc.returncode
proc = subprocess.run(base_cmd + [f"-j{jobs}", "all"], cwd=bundle_dir)
if proc.returncode != 0:
return proc.returncode
built = out_dir / "nsis7z.dll"
if not built.exists():
print(f"ERROR: expected artifact not found: {built}", file=sys.stderr)
return 1
dest = ROOT / cfg["dest"]
dest.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(built, dest)
if cleanup_artifacts:
shutil.rmtree(out_dir, ignore_errors=True)
if verbose:
print(f"[linux] Copied -> {dest}")
if cleanup_artifacts:
print(f"[linux] Cleaned build artifacts -> {out_dir}")
return 0
def main() -> int:
parser = argparse.ArgumentParser(description="Linux build path for nsis7z")
parser.add_argument(
"--7zip-version",
dest="zip_version",
choices=sorted(SUPPORTED),
default="26.00",
help="7-Zip version to build",
)
parser.add_argument(
"--configs",
nargs="+",
choices=list(CONFIGS.keys()) + ["all"],
default=["all"],
help="Build configuration(s): x86-ansi, x86-unicode, x64-unicode, or all",
)
parser.add_argument(
"--jobs",
default="auto",
help="Parallel make jobs. Use an integer or 'auto' (default: auto)",
)
parser.add_argument(
"--clean",
action="store_true",
default=True,
help="Run 'make clean' before building (default: True)",
)
parser.add_argument(
"--no-clean",
action="store_false",
dest="clean",
help="Skip 'make clean' to enable incremental rebuilds",
)
parser.add_argument(
"--cleanup-artifacts",
action="store_true",
default=True,
help="Remove per-config build artifact directories after successful copy (default: True)",
)
parser.add_argument(
"--no-cleanup-artifacts",
action="store_false",
dest="cleanup_artifacts",
help="Keep per-config build artifact directories (_o_*)",
)
parser.add_argument("--verbose", action="store_true", help="Verbose output")
args = parser.parse_args()
_require_tool("make")
for cfg in CONFIGS.values():
_require_tool(f"{cfg['triplet']}-gcc")
_require_tool(f"{cfg['triplet']}-g++")
_require_tool(f"{cfg['triplet']}-windres")
try:
jobs = _resolve_jobs(args.jobs)
except ValueError as exc:
print(f"ERROR: invalid --jobs value: {exc}", file=sys.stderr)
return 2
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 code != 0:
return code
print("[linux] Build completed")
return 0
if __name__ == "__main__":
sys.exit(main())