#!/usr/bin/env python # Copyright (C) 2013 Linaro # # Author: Milo Casagrande # # This file is part of Linaro Image Tools. It adds the feature # to append a debian package into the given hwpack. # We might need to change the manifest and Packages file in the # future to match the hardware pack v2 changes when available. # # Linaro Image Tools is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linaro Image Tools is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linaro Image Tools. If not, see . # import argparse import atexit import os import sys import tarfile import tempfile import shutil from debian.arfile import ArError from linaro_image_tools.hwpack.packages import ( get_packages_file, FetchedPackage ) from linaro_image_tools.utils import get_logger from linaro_image_tools.__version__ import __version__ logger = None def setup_args_parser(): """Setup the argument parsing. :return The parsed arguments. """ description = "Adds new packages inside a hardware pack." parser = argparse.ArgumentParser(version=__version__, description=description) parser.add_argument("-d", "--debug", action="store_true") parser.add_argument("-t", "--hwpack", required=True, help="The hardware pack to modify") parser.add_argument("-p", "--package", action="append", required=True, help="The debian package to append. Can be repeated " "multiple times.") parser.add_argument("-i", "--inplace", action="store_true", help="Add the packages in place, without creating a " "new hardware pack.") return parser.parse_args() def validate_args(args): """Verify the arguments passed on the command line. :param args: The arguments passed. """ hwpack_path = os.path.abspath(args.hwpack) if not os.path.isfile(hwpack_path): logger.error("Error: provided hardware pack file does not exists, or " "is not a file: {0}.".format(args.hwpack)) sys.exit(1) if not tarfile.is_tarfile(hwpack_path): logger.error("Error: cannot read hardware pack file. Make sure it " "is a supported tar archive.") sys.exit(1) for package in args.package: if not os.path.isfile(os.path.abspath(package)): logger.error("Error: provided package to add does not exists, or " "is not a file: {0}.".format(package)) sys.exit(1) def modify_manifest_file(debpackage_info, hwpack_dir): """Modify the manifest file to include a new package entry. :param debpackage_info: The info to write. :param hwpack_dir: Where the manifest file is located. """ debpack_manifest = os.path.join(hwpack_dir, "manifest") new_debpack_line = '{0}={1}\n'.format(debpackage_info.name, debpackage_info.version) logger.debug("Manifest line: {0}".format(new_debpack_line)) with open(debpack_manifest, "a") as manifest: manifest.write(new_debpack_line) def modify_packages_file(debpack_info, pkgs_dir): """Modify the Packages file to include a new package entry. :param debpack_info: The info to be written. :param pkgs_dir: The directory with the Packages file. """ debpack_Packages_fname = os.path.join(pkgs_dir, "Packages") package_info = get_packages_file([debpack_info]).strip() logger.debug("Packages line:\n{0}".format(package_info)) with open(debpack_Packages_fname, "a") as packages_file: packages_file.write("{0}\n\n".format(package_info)) def has_matching_package(pkg_to_search, dir_to_search): """Search for a matching file name in the provided directory. :param pkg_to_search: The package whose name will be matched. :param dir_to_search: Where to search for a matching name. """ logger.debug("Searching matching packages") package_found = False for pkg in os.listdir(dir_to_search): if os.path.basename(pkg_to_search) == os.path.basename(pkg): package_found = True break return package_found def add_packages_to_hwpack(hwpack, packages_to_add, inplace): """Add the provided packages to the hardware pack. Each package to add will be checked against the already available packages: if a similar one is found (it just need to have the same name), it will be skipped. :param hwpack: The hardware pack where to add the new files. :param packagess_to_add: List of package to add. """ hwpack = os.path.abspath(hwpack) tempdir = tempfile.mkdtemp() pkgs_dir = os.path.join(tempdir, 'pkgs') # Cleanup once done. atexit.register(shutil.rmtree, tempdir) # Unfortunately we cannot operate in memory, Python tar library does not # allow adding files with compressed tarballs. We have to extract it. logger.info("Opening hardware pack {0}...".format(hwpack)) logger.debug("Extracting hardware pack in {0}".format(tempdir)) with tarfile.open(hwpack, "r|gz") as tar_file: tar_file.extractall(tempdir) if not os.path.isdir(pkgs_dir): logger.error("Error: tar file does not include packages directory.") sys.exit(1) # Flag to check if we really need to save the new hwpack. save_hwpack = False for debpackage in packages_to_add: debpackage_path = os.path.abspath(debpackage) if has_matching_package(debpackage_path, pkgs_dir): logger.warning("Found similar package in the tar archive: file " "will not be added.") continue if os.path.isfile(debpackage_path): logger.info("Adding file {0}...".format(debpackage)) try: debpackage_info = FetchedPackage.from_deb(debpackage_path) except ArError: logger.warning("File {0} is invalid, skipping " "it.".format(debpackage)) continue if debpackage_info: logger.debug("Package info data:\n{0}".format(debpackage_info)) modify_manifest_file(debpackage_info, tempdir) modify_packages_file(debpackage_info, pkgs_dir) shutil.copy2(debpackage_path, pkgs_dir) save_hwpack |= True else: logger.warning("Unable to find valid info for package " "{0}.".format(debpackage)) else: logger.warning("File {0} does not exists, skipping " "it.".format(debpackage)) if save_hwpack: if inplace: logger.info("Saving hardware pack {0}...".format(hwpack)) with tarfile.open(hwpack, "w|gz") as tar_file: tar_file.add(tempdir, arcname="") else: save_dir = os.path.dirname(hwpack) # Retrieve the file name without the extensions, and create a new # file name. root_ext, ext1 = os.path.splitext(os.path.basename(hwpack)) root, ext2 = os.path.splitext(root_ext) root += "_new" new_file_name = root + ext2 + ext1 save_file = os.path.join(save_dir, new_file_name) logger.info("Saving new hardware pack {0}...".format(save_file)) with tarfile.open(save_file, "w|gz") as tar_file: tar_file.add(tempdir, arcname="") logger.info("New packages added successfully.") else: logger.info("No packages added. Exiting.") def hwpack_append(): args = setup_args_parser() global logger logger = get_logger(debug=args.debug) validate_args(args) add_packages_to_hwpack(args.hwpack, args.package, args.inplace) if __name__ == '__main__': hwpack_append()