#!/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()