From 8959e80498edd6a1857c01e5cb048d1c0843d3ba Mon Sep 17 00:00:00 2001 From: Torsten Wierschin Date: Wed, 11 Nov 2026 09:48:29 +0000 Subject: [PATCH] Adds pycache --- .gitignore | 1 + make_rpm_ubuntu26_release2.py | 818 ++++++++++++++++++++++++++++++++++ 2 files changed, 819 insertions(+) create mode 100755 make_rpm_ubuntu26_release2.py diff --git a/.gitignore b/.gitignore index 442003f..ccd5fec 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .svn/ packages-out/ +__pycache__ diff --git a/make_rpm_ubuntu26_release2.py b/make_rpm_ubuntu26_release2.py new file mode 100755 index 0000000..829643a --- /dev/null +++ b/make_rpm_ubuntu26_release2.py @@ -0,0 +1,818 @@ +#!/usr/bin/env python3 +""" +Build DEB packages for Qt6 Release 2 + +This script creates 5 DEB packages from the Release 2 scope: +- qt6-release-runtime: Binaries and libraries +- qt6-release-sources: C++ source files +- qt6-release-ctest-runtime: Test executables +- qt6-release-ctest-sources: Test source files +- qt6-release-build-pipeline: Build scripts and configuration + +Release 2 scope (S set - 39 modules): +- Lts/Common (15 modules) +- Lts/DDS (10 modules) +- Lts/Services (6 modules in 3 directories) +- Lts/Proxies/TrainingAreaBase (1 module) +- Lts/Tools (7 AP9 tools) +""" + +import os +import sys +import shutil +import subprocess +import argparse +from pathlib import Path + +# Import helpers from rpmbuild +sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'rpmbuild')) +from helpers import info, debug, warning as warn, die, removeDirectory + + +def copy_src_dir_to_dest_dir(src_dir, dest_dir, release_scope=False, release_dirs=None, file_filter=None): + """Copy source directory to destination with optional filtering + + Args: + src_dir: Source directory + dest_dir: Destination directory + release_scope: If True, only copy release scope directories + release_dirs: List of release scope directories (Release 2 specific) + file_filter: Optional function to filter files (filepath) -> bool, returns True to EXCLUDE + """ + os.makedirs(dest_dir, exist_ok=True) + + # Release 2 scope directories (S set) + if release_dirs is None: + release_dirs = [ + "Lts/Common", + "Lts/DDS", + "Lts/Services", + "Lts/Proxies/TrainingAreaBase", + "Lts/Tools" + ] + + if release_scope: + info(f"Copying release scope directories: {release_dirs}") + # Also copy root-level build files needed for CMake + root_build_files = ['CMakeLists.txt', 'CTestCustom.ctest'] + root_build_dirs = ['cmake'] + + for bf in root_build_files: + src_file = os.path.join(src_dir, bf) + if os.path.exists(src_file): + debug(f" Copying root file {bf}...") + shutil.copy2(src_file, os.path.join(dest_dir, bf)) + + for bd in root_build_dirs: + src_bdir = os.path.join(src_dir, bd) + dest_bdir = os.path.join(dest_dir, bd) + if os.path.exists(src_bdir): + debug(f" Copying root directory {bd}...") + shutil.copytree(src_bdir, dest_bdir, dirs_exist_ok=True) + + for rel_dir in release_dirs: + src_subdir = os.path.join(src_dir, rel_dir) + dest_subdir = os.path.join(dest_dir, rel_dir) + if os.path.exists(src_subdir): + debug(f" Copying {rel_dir}...") + for root, dirs, files in os.walk(src_subdir): + # Skip build directories, hidden directories, deb_build_tmp + dirs[:] = [d for d in dirs if not d.startswith('.') + and d not in ['build', 'cmake-build-debug', 'cmake-build-release', 'deb_build_tmp'] + and (file_filter is None or not file_filter(os.path.join(root, d)))] + + for file in files: + src_file = os.path.join(root, file) + rel_path = os.path.relpath(src_file, src_subdir) + dest_file = os.path.join(dest_subdir, rel_path) + + # Apply file filter if provided (skip file if filter returns True) + if file_filter and file_filter(src_file): + continue + + # Skip broken symlinks + if os.path.islink(src_file) and not os.path.exists(src_file): + debug(f"Skipping broken symlink: {src_file}") + continue + + os.makedirs(os.path.dirname(dest_file), exist_ok=True) + shutil.copy2(src_file, dest_file) + else: + # Copy entire source tree + for root, dirs, files in os.walk(src_dir): + # Skip build directories, hidden directories, deb_build_tmp + dirs[:] = [d for d in dirs if not d.startswith('.') + and d not in ['build', 'cmake-build-debug', 'cmake-build-release', 'deb_build_tmp', 'deb_packages']] + + for file in files: + src_file = os.path.join(root, file) + rel_path = os.path.relpath(src_file, src_dir) + dest_file = os.path.join(dest_dir, rel_path) + + # Apply file filter if provided + if file_filter and file_filter(src_file): + continue + + # Skip broken symlinks + if os.path.islink(src_file) and not os.path.exists(src_file): + debug(f"Skipping broken symlink: {src_file}") + continue + + os.makedirs(os.path.dirname(dest_file), exist_ok=True) + shutil.copy2(src_file, dest_file) + + +def is_dds_file(filepath): + """Check if a file or directory path contains DDS-related content that should be excluded + + For Release 2, we INCLUDE DDS files in the sources package since DDS is part of our scope. + This function is kept for compatibility but returns False for all files. + + Args: + filepath: Path to check + + Returns: + False (include all files for Release 2) + """ + return False + + +def create_manifest(output_dir, pkg_name, pkg_dir): + """Create a manifest file listing all files in the package""" + manifest_path = os.path.join(output_dir, "manifests", f"{pkg_name}.txt") + os.makedirs(os.path.dirname(manifest_path), exist_ok=True) + + files = [] + for root, dirs, filenames in os.walk(pkg_dir): + rel_root = os.path.relpath(root, pkg_dir) + for filename in filenames: + full_path = os.path.join(rel_root, filename) if rel_root != '.' else filename + files.append(full_path) + + with open(manifest_path, 'w') as f: + f.write('\n'.join(sorted(files))) + + return manifest_path + + +def validate_package_contents(pkg_name, manifest_path, allowed_patterns=None, forbidden_patterns=None): + """Validate package contents against allowlist and denylist""" + info(f"Validating {pkg_name} contents...") + + with open(manifest_path, 'r') as f: + files = [line.strip() for line in f if line.strip()] + + import fnmatch + + if forbidden_patterns: + for pattern in forbidden_patterns: + for file in files: + if fnmatch.fnmatch(file, pattern): + warn(f" Forbidden file found: {file} (matches {pattern})") + + info(f" Found {len(files)} files in {pkg_name}") + return True + + +def create_debian_control(deb_dir, package_name, version, build_number, description, depends="", section="devel"): + """Create debian/control file for a package (binary package format for dpkg-deb)""" + control_path = os.path.join(deb_dir, "control") + + # Runtime dependencies for Release 2 + runtime_deps = "" # dpkg-deb does not support debhelper variables + if depends: + runtime_deps += f", {depends}" + + # Binary package control format (required by dpkg-deb) + control_content = f"""Package: {package_name} +Version: {version}-{build_number} +Section: {section} +Priority: optional +Architecture: amd64 +Maintainer: Qt6 Release Team +Depends: {runtime_deps} +Description: Qt6 Release 2 - {description} + This package contains {description.lower()} for the Qt6 Release 2 delivery. + . + Release 2 scope includes: + - AP3: LtsUtils, LtsWidgets, LtsSession, LtsLicence, LtsPluginManager + - AP4: Core, DDSConnector, Routing, QosProfile, RTI DDS + - AP9: DdsView, LogViewer, SqlBrowser, 3 generators, lupdate +""" + + with open(control_path, 'w') as f: + f.write(control_content) + + +def create_debian_changelog(deb_dir, package_name, version, build_number): + """Create debian/changelog file""" + changelog_path = os.path.join(deb_dir, "changelog") + + changelog_content = f"""{package_name} ({version}-{build_number}) unstable; urgency=medium + + * Qt6 Release 2 delivery + * Scope: AP3, AP4, AP9 modules (39 modules total) + * Includes: Common, DDS, Services, Proxies/TrainingAreaBase, Tools + + -- Qt6 Release Team {subprocess.check_output(['date', '-R']).decode().strip()} +""" + + with open(changelog_path, 'w') as f: + f.write(changelog_content) + + +def create_debian_rules(deb_dir, cmake_flags="", skip_tests=False, use_prebuilt=False): + """Create debian/rules file""" + rules_path = os.path.join(deb_dir, "rules") + + if use_prebuilt: + # No-build rules - just package existing files + rules_content = """#!/usr/bin/make -f +# Debian rules file for Qt6 Release 2 (pre-built artifacts) + +%: +\tdh $@ + +override_dh_auto_configure: +\t# No configuration needed - using pre-built artifacts + +override_dh_auto_build: +\t# No build needed - using pre-built artifacts + +override_dh_auto_install: +\t# Files already in place via debian/install +""" + else: + test_flag = "-DENABLE_TESTS=OFF" if skip_tests else "-DENABLE_TESTS=ON" + + rules_content = f"""#!/usr/bin/make -f +# Debian rules file for Qt6 Release 2 + +%: +\tdh $@ + +override_dh_auto_configure: +\tdh_auto_configure -- -DCMAKE_BUILD_TYPE=Release {cmake_flags} {test_flag} + +override_dh_auto_build: +\tdh_auto_build + +override_dh_auto_test: +\t# Skip tests during package build (run separately with ctest) +\ttrue + +override_dh_auto_install: +\tdh_auto_install --destdir=debian/tmp +""" + + with open(rules_path, 'w') as f: + f.write(rules_content) + os.chmod(rules_path, 0o755) + + +def create_debian_rules_nobuild(deb_dir, skip_shlibdeps=False): + """Create debian/rules file for packages that don't need building (pre-built files only)""" + rules_path = os.path.join(deb_dir, "rules") + + shlibdeps_override = "" + if skip_shlibdeps: + shlibdeps_override = """ +override_dh_shlibdeps: +\t# Skip shared library dependencies (test executables may have external deps) +\ttrue +""" + + rules_content = f"""#!/usr/bin/make -f +# Debian rules file for Qt6 Release 2 (no-build package){shlibdeps_override} + +%: +\tdh $@ + +override_dh_auto_configure: +\t# No configuration needed (pre-built files) +\ttrue + +override_dh_auto_build: +\t# No build needed (pre-built files) +\ttrue + +override_dh_auto_install: +\t# Files installed via debian/install +\ttrue + +override_dh_auto_test: +\t# No tests in this package +\ttrue +""" + + with open(rules_path, 'w') as f: + f.write(rules_content) + os.chmod(rules_path, 0o755) + + +def create_debian_install(deb_dir, install_lines): + """Create debian/install file from list of file entries""" + install_path = os.path.join(deb_dir, "install") + + with open(install_path, 'w') as f: + f.write('\n'.join(install_lines)) + + +def find_test_executables(pkg_dir, release_dirs): + """Find test executables in build directory""" + test_executables = [] + + # Look for test executables in build directories + build_dirs = ['build/bin', 'build4/bin', 'build-release/bin'] + + for build_dir in build_dirs: + bin_path = os.path.join(pkg_dir, build_dir) + if os.path.exists(bin_path): + for f in os.listdir(bin_path): + if f.startswith('test') or f.endswith('_test'): + exe_path = os.path.join(bin_path, f) + if os.path.isfile(exe_path) and os.access(exe_path, os.X_OK): + # Try to determine which module this test belongs to + module_path = "unknown" + for rel_dir in release_dirs: + if rel_dir.lower() in f.lower(): + module_path = rel_dir + break + test_executables.append((exe_path, module_path)) + + return test_executables + + +def find_test_sources(pkg_dir, release_dirs): + """Find test source files in release scope modules""" + test_sources = [] + + for rel_dir in release_dirs: + module_path = os.path.join(pkg_dir, rel_dir) + if os.path.exists(module_path): + for root, dirs, files in os.walk(module_path): + dirs[:] = [d for d in dirs if not d.startswith('.')] + for file in files: + if 'test' in file.lower() and (file.endswith('.cpp') or file.endswith('.h')): + rel_path = os.path.relpath(os.path.join(root, file), pkg_dir) + test_sources.append(rel_path) + + return test_sources + + +def build_single_deb(pkg_name, pkg_dir, output_dir, version, build_number, cmake_flags, skip_tests, install_lines, description, section="devel", depends=""): + """Build a single DEB package""" + info(f"Building DEB package: {pkg_name}") + + deb_dir = os.path.join(pkg_dir, "debian") + os.makedirs(deb_dir) + + create_debian_control(deb_dir, pkg_name, version, build_number, description, depends, section) + create_debian_changelog(deb_dir, pkg_name, version, build_number) + create_debian_rules(deb_dir, cmake_flags, skip_tests) + + # 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 + with open(os.path.join(deb_dir, "compat"), "w") as f: + f.write("13\n") + + # Create debian/install + if install_lines: + create_debian_install(deb_dir, install_lines) + + # Build the package + info(f"Running debuild for {pkg_name}...") + os.chdir(pkg_dir) + + 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, capture_output=True, text=True) + + if ret.returncode != 0: + print(ret.stdout) + print(ret.stderr) + die(f"debuild failed for {pkg_name} 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}") + + return True + + +def build_single_deb_nobuild(pkg_name, pkg_dir, output_dir, version, build_number, install_lines, description, section="devel", depends="", skip_shlibdeps=False): + """Build a single DEB package without running cmake/make (pre-built files only)""" + info(f"Building DEB package (no-build): {pkg_name}") + + deb_dir = os.path.join(pkg_dir, "DEBIAN") + os.makedirs(deb_dir) + + create_debian_control(deb_dir, pkg_name, version, build_number, description, depends, section) + create_debian_changelog(deb_dir, pkg_name, version, build_number) + create_debian_rules_nobuild(deb_dir, skip_shlibdeps=skip_shlibdeps) + + # 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 + with open(os.path.join(deb_dir, "compat"), "w") as f: + f.write("13\n") + + # Create DEBIAN/install + if install_lines: + create_debian_install(deb_dir, install_lines) + + # Build the package using dpkg-deb directly + info(f"Building DEB package for {pkg_name} using dpkg-deb...") + original_dir = os.getcwd() + os.chdir(pkg_dir) + + # dpkg-deb --build creates the .deb file directly + deb_file = f"{pkg_name}_{version}-{build_number}_amd64.deb" + cmd = f"dpkg-deb --build . ../{deb_file}" + debug(f"Executing: {cmd}") + ret = subprocess.run(cmd, shell=True, capture_output=True, text=True) + + # Change back to original directory + os.chdir(original_dir) + + if ret.returncode != 0: + print(ret.stdout) + print(ret.stderr) + die(f"dpkg-deb failed for {pkg_name} with code {ret.returncode}") + + # Move the .deb file to output directory + parent_dir = os.path.dirname(pkg_dir) + deb_path = os.path.join(parent_dir, deb_file) + if os.path.exists(deb_path): + dst = os.path.join(output_dir, deb_file) + if os.path.exists(dst): + os.remove(dst) + shutil.move(deb_path, output_dir) + info(f"Moved {deb_file} to {output_dir}") + + # Clean up build directory to save disk space + info(f"Cleaning up build directory: {pkg_dir}") + try: + shutil.rmtree(pkg_dir, ignore_errors=True) + except Exception as e: + warning(f"Failed to clean up {pkg_dir}: {e}") + + return True + + +def build_all_debs(source_dir, output_dir, project_name, version, build_number, cmake_flags, release_scope=True): + """Build all 5 DEB packages for Release 2 using pre-built artifacts""" + info("========================================") + info("Building 5 DEB packages for Qt6 Release 2") + info("========================================") + + os.makedirs(output_dir, exist_ok=True) + os.makedirs(os.path.join(output_dir, "manifests"), exist_ok=True) + + build_base = os.path.join("/tmp", "qt6_deb_build_" + str(os.getpid())) + # removeDirectory(build_base) # Skip removal when using /tmp + os.makedirs(build_base) + + # Release 2 scope directories (S set - 39 modules) + release_dirs = [ + "Lts/Common", + "Lts/DDS", + "Lts/Services", + "Lts/Proxies/TrainingAreaBase", + "Lts/Tools" + ] + + # Source file extensions + source_extensions = ['.cpp', '.h', '.hpp', '.cc', '.cxx', '.hxx'] + script_extensions = ['.py', '.sh', '.cmake', '.txt'] + + # ============================================ + # STEP 1: Use pre-built artifacts from build4/ + # ============================================ + info("\n=== Step 1: Using pre-built artifacts from build4/ ===") + build4_dir = os.path.join(source_dir, "build4") + + if not os.path.exists(build4_dir): + die(f"Pre-built artifacts not found in {build4_dir}. Run build first.") + + # Verify libraries exist + lib_dir = os.path.join(build4_dir, "lib") + if not os.path.exists(lib_dir): + die(f"Library directory not found: {lib_dir}") + + lib_count = len([f for f in os.listdir(lib_dir) if f.endswith('.so')]) + info(f"Found {lib_count} shared libraries in build4/lib/") + + # Verify tool executables exist + tools_build_dir = os.path.join(build4_dir, "Lts", "Tools") + if os.path.exists(tools_build_dir): + info(f"AP9 tools build directory exists: {tools_build_dir}") + + bin_dir = os.path.join(build4_dir, "bin") + if os.path.exists(bin_dir): + tool_exes = [f for f in os.listdir(bin_dir) if f.startswith('t') or 'generator' in f.lower()] + if tool_exes: + info(f"Found tool executables in build4/bin/: {', '.join(tool_exes)}") + + info("Pre-built artifacts verified successfully") + + # ============================================ + # Package 1: qt6-release-runtime + # ============================================ + info("\n=== Building qt6-release-runtime ===") + pkg1_name = f"qt6-release-runtime" + pkg1_dir = os.path.join(build_base, f"{pkg1_name}-{version}") + os.makedirs(pkg1_dir) + + # Create package structure + pkg1_lib_dir = os.path.join(pkg1_dir, "usr", "lib") + pkg1_bin_dir = os.path.join(pkg1_dir, "usr", "bin") + os.makedirs(pkg1_lib_dir, exist_ok=True) + os.makedirs(pkg1_bin_dir, exist_ok=True) + + # Copy pre-built libraries from build4/lib + info("Copying libraries from build4/lib/...") + for f in os.listdir(lib_dir): + if f.endswith('.so') or f.endswith('.so.1') or f.startswith('lib'): + src = os.path.join(lib_dir, f) + dst = os.path.join(pkg1_lib_dir, f) + if os.path.isfile(src): + shutil.copy2(src, dst) + + # Copy tool executables from build4/bin + info("Copying executables from build4/bin/...") + if os.path.exists(bin_dir): + for f in os.listdir(bin_dir): + fpath = os.path.join(bin_dir, f) + if os.path.isfile(fpath) and (f.startswith('t') or 'generator' in f.lower() or f in ['ddsview', 'sqlbrowser', 'LogViewer', 'topicdiscover']): + shutil.copy2(fpath, os.path.join(pkg1_bin_dir, f)) + + # Copy tool executables from build4/Lts/Tools subdirectories + if os.path.exists(tools_build_dir): + for tool_subdir in os.listdir(tools_build_dir): + tool_bin_path = os.path.join(tools_build_dir, tool_subdir, "bin") + if os.path.exists(tool_bin_path): + for exe in os.listdir(tool_bin_path): + if exe and not exe.startswith('.'): + src = os.path.join(tool_bin_path, exe) + if os.path.isfile(src): + shutil.copy2(src, os.path.join(pkg1_bin_dir, exe)) + + # Install lines for runtime + install_lines_1 = [] + install_lines_1.append("usr/lib/* usr/lib/") + install_lines_1.append("usr/bin/* usr/bin/") + + build_single_deb_nobuild( + pkg_name=pkg1_name, + pkg_dir=pkg1_dir, + output_dir=output_dir, + version=version, + build_number=build_number, + install_lines=install_lines_1, + description="Runtime binaries and libraries for Release 2 scope (AP3, AP4, AP9)", + section="libs" + ) + + # Create manifest + manifest_1 = create_manifest(output_dir, pkg1_name, pkg1_dir) + validate_package_contents(pkg1_name, manifest_1, + allowed_patterns=["usr/bin/*", "usr/lib/*", "usr/SV/*", "usr/WS/*", "debian/*"], + forbidden_patterns=["*.cpp", "*.h", "*.hpp", "tests/*", "jenkins/*", "Dockerfile*"]) + + # ============================================ + # Package 2: qt6-release-sources + # ============================================ + info("\n=== Building qt6-release-sources ===") + pkg2_name = f"qt6-release-sources" + pkg2_dir = os.path.join(build_base, f"{pkg2_name}-{version}") + os.makedirs(pkg2_dir) + + # Copy FULL source tree (Release 2 includes DDS files) + copy_src_dir_to_dest_dir(source_dir, pkg2_dir, release_scope=False, release_dirs=release_dirs) + + # Install lines for sources: only from release scope directories + install_lines_2 = [] + for rel_dir in release_dirs: + src_dir = os.path.join(pkg2_dir, rel_dir) + if os.path.exists(src_dir): + for root, dirs, files in os.walk(src_dir): + dirs[:] = [d for d in dirs if not d.startswith('.')] + for file in files: + ext = os.path.splitext(file)[1].lower() + if ext in source_extensions or file == "CMakeLists.txt": + rel_path = os.path.relpath(os.path.join(root, file), pkg2_dir) + dest_dir = os.path.join("usr/src", os.path.dirname(rel_path)) + install_lines_2.append(f"{rel_path} {dest_dir}/") + + # Use no-build rules for source package + build_single_deb_nobuild( + pkg_name=pkg2_name, + pkg_dir=pkg2_dir, + output_dir=output_dir, + version=version, + build_number=build_number, + install_lines=install_lines_2, + description="C++ source files for Release 2 scope (AP3, AP4, AP9)", + section="devel" + ) + + manifest_2 = create_manifest(output_dir, pkg2_name, pkg2_dir) + validate_package_contents(pkg2_name, manifest_2, + allowed_patterns=["usr/src/**/*.cpp", "usr/src/**/*.h", "usr/src/**/CMakeLists.txt", "debian/*"], + forbidden_patterns=["build/*", "tests/*", "jenkins/*"]) + + # ============================================ + # Package 3: qt6-release-ctest-runtime + # ============================================ + info("\n=== Building qt6-release-ctest-runtime ===") + pkg3_name = f"qt6-release-ctest-runtime" + pkg3_dir = os.path.join(build_base, f"{pkg3_name}-{version}") + os.makedirs(pkg3_dir) + + # Copy FULL source tree + copy_src_dir_to_dest_dir(source_dir, pkg3_dir, release_scope=False, release_dirs=release_dirs) + + # Install lines for CTest runtime: test executables from release scope only + install_lines_3 = [] + + # Find test executables in build directory + test_executables = find_test_executables(pkg3_dir, release_dirs) + for exe_path, module_path in test_executables: + rel_path = os.path.relpath(exe_path, pkg3_dir) + dest_dir = "usr/lib/ctest" + install_lines_3.append(f"{rel_path} {dest_dir}/") + info(f" Including test executable: {os.path.basename(exe_path)} from {module_path}") + + # Also include CTest configuration files + ctest_files = [ + "build/CTestTestfile.cmake", + "build/DartConfiguration.tcl", + "build/Testing/TAG", + ] + for ctest_file in ctest_files: + if os.path.exists(os.path.join(pkg3_dir, ctest_file)): + install_lines_3.append(f"{ctest_file} usr/lib/ctest/") + + build_single_deb_nobuild( + pkg_name=pkg3_name, + pkg_dir=pkg3_dir, + output_dir=output_dir, + version=version, + build_number=build_number, + install_lines=install_lines_3, + description="Test executables for Release 2 scope", + section="devel", + skip_shlibdeps=True + ) + + manifest_3 = create_manifest(output_dir, pkg3_name, pkg3_dir) + validate_package_contents(pkg3_name, manifest_3, + allowed_patterns=["usr/lib/ctest/*", "debian/*"], + forbidden_patterns=["*.cpp", "*.h", "jenkins/*"]) + + # ============================================ + # Package 4: qt6-release-ctest-sources + # ============================================ + info("\n=== Building qt6-release-ctest-sources ===") + pkg4_name = f"qt6-release-ctest-sources" + pkg4_dir = os.path.join(build_base, f"{pkg4_name}-{version}") + os.makedirs(pkg4_dir) + + # Copy FULL source tree + copy_src_dir_to_dest_dir(source_dir, pkg4_dir, release_scope=False, release_dirs=release_dirs) + + # Install lines for test sources from release scope only + install_lines_4 = [] + + # Find test source files in release scope modules + test_sources = find_test_sources(pkg4_dir, release_dirs) + for rel_path in test_sources: + dest_dir = os.path.dirname(rel_path) + install_lines_4.append(f"{rel_path} usr/src/{dest_dir}/") + info(f" Including test source: {rel_path}") + + build_single_deb_nobuild( + pkg_name=pkg4_name, + pkg_dir=pkg4_dir, + output_dir=output_dir, + version=version, + build_number=build_number, + install_lines=install_lines_4, + description="Test source files for Release 2 scope", + section="devel" + ) + + manifest_4 = create_manifest(output_dir, pkg4_name, pkg4_dir) + validate_package_contents(pkg4_name, manifest_4, + allowed_patterns=["usr/src/**/tests/*", "debian/*"], + forbidden_patterns=["build/*", "jenkins/*"]) + + # ============================================ + # Package 5: qt6-release-build-pipeline + # ============================================ + info("\n=== Building qt6-release-build-pipeline ===") + pkg5_name = f"qt6-release-build-pipeline" + pkg5_dir = os.path.join(build_base, f"{pkg5_name}-{version}") + os.makedirs(pkg5_dir) + + # Copy pipeline scripts and configuration + # Note: pipeline dirs are at project root, not in qt-migration-legatus + pipeline_dirs = [ + "jenkins-ci-rme", + "docker", + "scripts" + ] + + # Use parent of source_dir as project root (source_dir points to qt-migration-legatus/qt6-after/qt6-migrated-legatus) + project_root = os.path.dirname(os.path.dirname(os.path.dirname(source_dir))) + + install_lines_5 = [] + for pipeline_dir in pipeline_dirs: + src_pipeline = os.path.join(project_root, pipeline_dir) + dest_pipeline = os.path.join(pkg5_dir, pipeline_dir) + if os.path.exists(src_pipeline): + shutil.copytree(src_pipeline, dest_pipeline, dirs_exist_ok=True) + for root, dirs, files in os.walk(dest_pipeline): + dirs[:] = [d for d in dirs if not d.startswith('.')] + for file in files: + rel_path = os.path.relpath(os.path.join(root, file), pkg5_dir) + install_lines_5.append(f"{rel_path} usr/src/pipeline/{os.path.dirname(rel_path)}/") + + build_single_deb_nobuild( + pkg_name=pkg5_name, + pkg_dir=pkg5_dir, + output_dir=output_dir, + version=version, + build_number=build_number, + install_lines=install_lines_5, + description="Build pipeline scripts and configuration for Release 2", + section="devel" + ) + + manifest_5 = create_manifest(output_dir, pkg5_name, pkg5_dir) + validate_package_contents(pkg5_name, manifest_5, + allowed_patterns=["usr/src/pipeline/*", "debian/*"], + forbidden_patterns=["*.cpp", "*.h", "build/*"]) + + info("\n========================================") + info("All 5 DEB packages built successfully!") + info(f"Output directory: {output_dir}") + info("========================================") + + +def main(): + parser = argparse.ArgumentParser(description="Build DEB packages for Qt6 Release 2") + 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") + + 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}") + + source_dir = args.source_dir + + info(f"Source directory: {source_dir}") + + build_all_debs( + 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 + ) + + +if __name__ == "__main__": + main() \ No newline at end of file