- Add lts-cc.conf to release_items list - Change TetraIF check from exact match to startswith to include subdirectories - Fixes CMake errors for missing lts-cc.conf, Console, LastTest, LastTestAuswertung Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
379 lines
13 KiB
Python
Executable File
379 lines
13 KiB
Python
Executable File
#!/usr/bin/python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
DEB package builder for Ubuntu 26.04
|
|
Migrated from make_rpm.py (openSUSE) to Ubuntu/DEB ecosystem
|
|
"""
|
|
|
|
import argparse
|
|
import configparser
|
|
import getpass
|
|
import os
|
|
import re
|
|
import shutil
|
|
import stat
|
|
import subprocess
|
|
import sys
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
|
|
def verbose():
|
|
verb = os.getenv("EMERGE_VERBOSE")
|
|
if verb is not None and verb.isdigit() and int(verb) > 0:
|
|
return int(verb)
|
|
return 0
|
|
|
|
|
|
def debug(message):
|
|
if verbose() >= 1:
|
|
print(f"debug: {message}")
|
|
return True
|
|
|
|
|
|
def info(message):
|
|
if verbose() >= 0:
|
|
print(f"info: {message}")
|
|
return True
|
|
|
|
|
|
def warning(message):
|
|
print(f"warning: {message}")
|
|
return True
|
|
|
|
|
|
def error(message):
|
|
die(message)
|
|
|
|
|
|
def die(message):
|
|
print(f"emerge fatal error: {message}", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
|
|
def system(cmdstring, file=None):
|
|
debug(f"Executing '{cmdstring}'")
|
|
stdout = subprocess.PIPE
|
|
if file is not None:
|
|
stdout = file
|
|
p = subprocess.Popen(cmdstring, shell=True, stdout=stdout, stderr=sys.stderr)
|
|
ret = p.wait()
|
|
if ret != 0:
|
|
die(f"Error executing {cmdstring} ({ret})")
|
|
ret = ""
|
|
if file is None:
|
|
ret = p.stdout.read().strip()
|
|
return ret
|
|
|
|
|
|
def get_os_name():
|
|
"""Return Ubuntu version name"""
|
|
if os.path.exists("/etc/os-release"):
|
|
with open("/etc/os-release", "r") as f:
|
|
for line in f.readlines():
|
|
if line.startswith("PRETTY_NAME="):
|
|
name = line.split("=")[1].strip().strip('"')
|
|
return name.replace(" ", "_")
|
|
return "Ubuntu_unknown"
|
|
|
|
|
|
def remove_directory(dest_dir):
|
|
info("--------------------------------")
|
|
info(f"Removing {dest_dir}")
|
|
info("--------------------------------")
|
|
if os.path.exists(dest_dir) and os.path.isdir(dest_dir):
|
|
shutil.rmtree(dest_dir)
|
|
|
|
|
|
def copy_src_dir_to_dest_dir(srcdir, dstdir, perm=None, with_subdirs=True, release_scope=False):
|
|
"""Copy source directory to destination directory.
|
|
|
|
Args:
|
|
srcdir: Source directory path
|
|
dstdir: Destination directory path
|
|
perm: Optional permissions to set
|
|
with_subdirs: Whether to copy subdirectories
|
|
release_scope: If True, only copy release scope files (TetraIF, Z/src, Lts/Common)
|
|
"""
|
|
info("--------------------------------")
|
|
info(f"Copying {srcdir} to {dstdir}")
|
|
if release_scope:
|
|
info("Release scope mode: only copying TetraIF, Z/src, Lts/Common")
|
|
info("--------------------------------")
|
|
|
|
while srcdir.endswith("/"):
|
|
srcdir = srcdir[:-1]
|
|
|
|
# Release scope directories and files to include
|
|
release_items = [
|
|
'TetraIF',
|
|
'Z/src',
|
|
'Lts/Common',
|
|
'CMakeLists.txt',
|
|
'cmake',
|
|
'tests',
|
|
'new_docker_build.sh',
|
|
'specs',
|
|
'include',
|
|
'translations',
|
|
'CTestCustom.ctest',
|
|
'.gitignore',
|
|
'.envrc',
|
|
'doxygen',
|
|
'external',
|
|
'src',
|
|
'lts-cc.conf'
|
|
]
|
|
|
|
if with_subdirs:
|
|
for root, dirs, files in os.walk(srcdir):
|
|
# Filter directories for release scope
|
|
if release_scope:
|
|
rel_root = os.path.relpath(root, srcdir)
|
|
if rel_root == '.':
|
|
# At root level, only keep release scope directories
|
|
dirs[:] = [d for d in dirs if d in release_items or any(d == item.split('/')[0] for item in release_items if '/' in item)]
|
|
elif rel_root == 'Z':
|
|
# In Z directory, only keep src
|
|
dirs[:] = [d for d in dirs if d == 'src']
|
|
elif rel_root == 'Lts':
|
|
# In Lts directory, only keep Common
|
|
dirs[:] = [d for d in dirs if d == 'Common']
|
|
elif rel_root.startswith('Lts/Common'):
|
|
# Keep all subdirectories under Lts/Common
|
|
pass
|
|
elif rel_root.startswith('Z/src'):
|
|
# Keep all subdirectories under Z/src
|
|
pass
|
|
elif rel_root.startswith('TetraIF'):
|
|
# Keep all subdirectories under TetraIF
|
|
pass
|
|
else:
|
|
# Skip other directories
|
|
dirs[:] = []
|
|
|
|
tmpdir = root.replace(srcdir, dstdir)
|
|
debug(f"dstdir = '{tmpdir}'")
|
|
if not os.path.exists(tmpdir):
|
|
debug(f"Creating dir '{tmpdir}'")
|
|
os.makedirs(tmpdir)
|
|
if perm:
|
|
os.chmod(tmpdir, perm | stat.S_IREAD | stat.S_IRGRP | stat.S_IROTH)
|
|
for file in files:
|
|
# Skip non-release files at root level
|
|
if release_scope and rel_root == '.':
|
|
if file not in release_items and not any(file == item.split('/')[0] for item in release_items if '/' in item):
|
|
continue
|
|
src_path = os.path.join(root, file)
|
|
# Skip broken symlinks
|
|
if os.path.islink(src_path) and not os.path.exists(src_path):
|
|
debug(f"Skipping broken symlink: {src_path}")
|
|
continue
|
|
if os.path.isfile(tmpdir) and os.path.exists(tmpdir):
|
|
os.remove(tmpdir)
|
|
shutil.copy(src_path, tmpdir, follow_symlinks=True)
|
|
if perm:
|
|
f = os.path.join(tmpdir, file)
|
|
add_perm = 0
|
|
if os.path.isdir(f):
|
|
add_perm = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
|
|
else:
|
|
mode = os.stat(f).st_mode
|
|
if (stat.S_IXUSR & mode) == stat.S_IXUSR:
|
|
add_perm = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
|
|
os.chmod(f, perm | add_perm)
|
|
else:
|
|
for f in os.listdir(srcdir):
|
|
if os.path.isdir(f):
|
|
continue
|
|
if release_scope and f not in release_items:
|
|
continue
|
|
shutil.copy(os.path.join(srcdir, f), os.path.join(dstdir, f))
|
|
|
|
|
|
def create_debian_control(deb_dir, project_name, version, build_number, cmake_flags):
|
|
"""Create debian/control file for DEB package"""
|
|
control_content = f"""Source: legatus-{project_name.lower()}
|
|
Section: devel
|
|
Priority: optional
|
|
Maintainer: Legatus Team <legatus@example.com>
|
|
Build-Depends: debhelper (>= 13), cmake, ninja-build, qt6-base-dev, qt6-tools-dev,
|
|
libboost-all-dev, libjson-glib-dev, libgeographiclib-dev
|
|
|
|
Package: legatus-{project_name.lower()}
|
|
Architecture: any
|
|
Depends: ${{shlibs:Depends}}, ${{misc:Depends}}
|
|
Description: Legatus {project_name} LTS package
|
|
Legatus {project_name} component built with Qt6/C++23
|
|
Version: {version}-{build_number}
|
|
Build flags: {cmake_flags}
|
|
"""
|
|
|
|
control_file = os.path.join(deb_dir, "control")
|
|
with open(control_file, "w") as f:
|
|
f.write(control_content)
|
|
info(f"Created {control_file}")
|
|
|
|
|
|
def create_debian_changelog(deb_dir, project_name, version, build_number):
|
|
"""Create debian/changelog file"""
|
|
timestamp = datetime.now().strftime("%a, %d %b %Y %H:%M:%S +0000")
|
|
changelog_content = f"""legatus-{project_name.lower()} ({version}-{build_number}) unstable; urgency=medium
|
|
|
|
* Automated build for Ubuntu 26.04
|
|
* Qt6/C++23 migration
|
|
|
|
-- Legatus Team <legatus@example.com> {timestamp}
|
|
"""
|
|
|
|
changelog_file = os.path.join(deb_dir, "changelog")
|
|
with open(changelog_file, "w") as f:
|
|
f.write(changelog_content)
|
|
info(f"Created {changelog_file}")
|
|
|
|
|
|
def create_debian_rules(deb_dir):
|
|
"""Create debian/rules file"""
|
|
rules_content = """#!/usr/bin/make -f
|
|
%:
|
|
dh $@ --buildsystem=cmake --builddirectory=build
|
|
|
|
override_dh_auto_configure:
|
|
dh_auto_configure -- -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
|
|
|
override_dh_auto_build:
|
|
dh_auto_build --parallel
|
|
|
|
override_dh_auto_install:
|
|
dh_auto_install
|
|
"""
|
|
|
|
rules_file = os.path.join(deb_dir, "rules")
|
|
with open(rules_file, "w") as f:
|
|
f.write(rules_content)
|
|
os.chmod(rules_file, stat.S_IREAD | stat.S_IWRITE | stat.S_IXUSR | stat.S_IRGRP | stat.S_IROTH | stat.S_IXGRP | stat.S_IXOTH)
|
|
info(f"Created {rules_file}")
|
|
|
|
|
|
def build_deb(source_dir, output_dir, project_name, version, build_number, cmake_flags, release_scope=False):
|
|
"""Build DEB package using debuild
|
|
|
|
Args:
|
|
source_dir: Source directory path
|
|
output_dir: Output directory for packages
|
|
project_name: Project name (qatar|ahtc|guez)
|
|
version: Version string
|
|
build_number: Build number
|
|
cmake_flags: CMake build flags
|
|
release_scope: If True, only include release scope files (TetraIF, Z/src, Lts/Common)
|
|
"""
|
|
info("--------------------------------")
|
|
info(f"Building DEB package for {project_name} {version}-{build_number}")
|
|
if release_scope:
|
|
info("Release scope mode: only packaging TetraIF, Z/src, Lts/Common")
|
|
info("--------------------------------")
|
|
|
|
# Create output directory
|
|
os.makedirs(output_dir, exist_ok=True)
|
|
|
|
# Create temporary build directory
|
|
build_base = os.path.join(source_dir, "deb_build_tmp")
|
|
remove_directory(build_base)
|
|
os.makedirs(build_base)
|
|
|
|
# Copy source to build directory
|
|
pkg_name = f"legatus-{project_name.lower()}"
|
|
pkg_dir = os.path.join(build_base, f"{pkg_name}-{version}")
|
|
copy_src_dir_to_dest_dir(source_dir, pkg_dir, release_scope=release_scope)
|
|
|
|
# Create debian directory
|
|
deb_dir = os.path.join(pkg_dir, "debian")
|
|
os.makedirs(deb_dir)
|
|
|
|
# Create debian control files
|
|
create_debian_control(deb_dir, project_name, version, build_number, cmake_flags)
|
|
create_debian_changelog(deb_dir, project_name, version, build_number)
|
|
create_debian_rules(deb_dir)
|
|
|
|
# Create debian/source/format
|
|
source_dir_path = os.path.join(deb_dir, "source")
|
|
os.makedirs(source_dir_path)
|
|
with open(os.path.join(source_dir_path, "format"), "w") as f:
|
|
f.write("3.0 (quilt)\n")
|
|
|
|
# Create debian/compat (for older debhelper compatibility)
|
|
with open(os.path.join(deb_dir, "compat"), "w") as f:
|
|
f.write("13\n")
|
|
|
|
# Build the package
|
|
info("Running debuild...")
|
|
os.chdir(pkg_dir)
|
|
|
|
# Set environment for debuild
|
|
env = os.environ.copy()
|
|
env["DEB_BUILD_OPTIONS"] = "parallel=$(nproc)"
|
|
|
|
cmd = f"debuild -us -uc -b"
|
|
debug(f"Executing: {cmd}")
|
|
ret = subprocess.run(cmd, shell=True, env=env)
|
|
|
|
if ret.returncode != 0:
|
|
die(f"debuild failed with code {ret.returncode}")
|
|
|
|
# Move generated packages to output directory
|
|
parent_dir = os.path.dirname(pkg_dir)
|
|
for f in os.listdir(parent_dir):
|
|
if f.endswith(".deb") or f.endswith(".dsc") or f.endswith(".changes") or f.endswith(".buildinfo"):
|
|
src = os.path.join(parent_dir, f)
|
|
dst = os.path.join(output_dir, f)
|
|
if os.path.exists(dst):
|
|
os.remove(dst)
|
|
shutil.move(src, output_dir)
|
|
info(f"Moved {f} to {output_dir}")
|
|
|
|
# Cleanup
|
|
os.chdir(source_dir)
|
|
remove_directory(build_base)
|
|
|
|
info(f"DEB package build completed: {output_dir}")
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Build DEB packages for Legatus LTS")
|
|
parser.add_argument("-c", "--config", required=True, help="Config type (LTS)")
|
|
parser.add_argument("--project_name", required=True, help="Project name (qatar|ahtc|guez)")
|
|
parser.add_argument("--version", required=True, help="Version (Major.minor)")
|
|
parser.add_argument("--output_dir", required=True, help="Output directory for packages")
|
|
parser.add_argument("--source_dir", required=True, help="Source directory path")
|
|
parser.add_argument("--cmake_build_flags", default="", help="CMake build flags")
|
|
parser.add_argument("--build_number", default="1", help="Build number")
|
|
parser.add_argument("--release_scope", action="store_true", help="Only include release scope files (TetraIF, Z/src, Lts/Common)")
|
|
|
|
args = parser.parse_args()
|
|
|
|
info(f"Building {args.config} package for {args.project_name} v{args.version}")
|
|
info(f"Output directory: {args.output_dir}")
|
|
info(f"Source directory: {args.source_dir}")
|
|
info(f"CMake flags: {args.cmake_build_flags}")
|
|
info(f"Release scope: {args.release_scope}")
|
|
|
|
# Use provided source directory
|
|
source_dir = args.source_dir
|
|
|
|
info(f"Source directory: {source_dir}")
|
|
|
|
build_deb(
|
|
source_dir=source_dir,
|
|
output_dir=args.output_dir,
|
|
project_name=args.project_name,
|
|
version=args.version,
|
|
build_number=args.build_number,
|
|
cmake_flags=args.cmake_build_flags,
|
|
release_scope=args.release_scope
|
|
)
|
|
|
|
info("Build completed successfully!")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main() |