Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fdb38817 authored by Daniel Norman's avatar Daniel Norman
Browse files

Adds output-dir and output-item-list for copying only certain files.

This provides the ability to run merge_target_files without the end goal
of a target files zip. This is useful for users that only want the IMAGES
folder, for example.

Bug: 130304869
Test: python -m unittest test_merge_target_files
Change-Id: If0412b8e1eb85fe09d7b689fd7f56ce84067faea
parent bbfcef3f
Loading
Loading
Loading
Loading
+65 −4
Original line number Diff line number Diff line
@@ -42,7 +42,17 @@ Usage: merge_target_files.py [args]
      contents of default_other_item_list if provided.

  --output-target-files output-target-files-package
      The output merged target files package. Also a zip archive.
      If provided, the output merged target files package. Also a zip archive.

  --output-dir output-directory
      If provided, the destination directory for saving merged files. Requires
      the --output-item-list flag.
      Can be provided alongside --output-target-files, or by itself.

  --output-item-list output-item-list-file.
      The optional path to a newline-separated config file that specifies the
      file patterns to copy into the --output-dir. Required if providing
      the --output-dir flag.

  --rebuild_recovery
      Rebuild the recovery patch used by non-A/B devices and write it to the
@@ -57,6 +67,7 @@ from __future__ import print_function
import fnmatch
import logging
import os
import shutil
import sys
import zipfile

@@ -72,6 +83,8 @@ OPTIONS.system_misc_info_keys = None
OPTIONS.other_target_files = None
OPTIONS.other_item_list = None
OPTIONS.output_target_files = None
OPTIONS.output_dir = None
OPTIONS.output_item_list = None
OPTIONS.rebuild_recovery = False
OPTIONS.keep_tmp = False

@@ -195,6 +208,29 @@ def extract_items(target_files, target_files_temp_dir, extract_item_list):
      filtered_extract_item_list)


def copy_items(from_dir, to_dir, patterns):
  """Similar to extract_items() except uses an input dir instead of zip."""
  file_paths = []
  for dirpath, _, filenames in os.walk(from_dir):
    file_paths.extend(os.path.relpath(path=os.path.join(dirpath, filename),
                                      start=from_dir) for filename in filenames)

  filtered_file_paths = set()
  for pattern in patterns:
    filtered_file_paths.update(fnmatch.filter(file_paths, pattern))

  for file_path in filtered_file_paths:
    original_file_path = os.path.join(from_dir, file_path)
    copied_file_path = os.path.join(to_dir, file_path)
    copied_file_dir = os.path.dirname(copied_file_path)
    if not os.path.exists(copied_file_dir):
      os.makedirs(copied_file_dir)
    if os.path.islink(original_file_path):
      os.symlink(os.readlink(original_file_path), copied_file_path)
    else:
      shutil.copyfile(original_file_path, copied_file_path)


def read_config_list(config_file_path):
  """Reads a config file into a list of strings.

@@ -546,6 +582,8 @@ def merge_target_files(
    other_target_files,
    other_item_list,
    output_target_files,
    output_dir,
    output_item_list,
    rebuild_recovery):
  """Merge two target files packages together.

@@ -655,7 +693,14 @@ def merge_target_files(

  add_img_to_target_files.main(add_img_args)

  # Finally, create the output target files zip archive.
  # Finally, create the output target files zip archive and/or copy the
  # output items to the output target files directory.

  if output_dir:
    copy_items(output_target_files_temp_dir, output_dir, output_item_list)

  if not output_target_files:
    return

  output_zip = os.path.abspath(output_target_files)
  output_target_files_list = os.path.join(temp_dir, 'output.list')
@@ -747,6 +792,10 @@ def main():
      OPTIONS.other_item_list = a
    elif o == '--output-target-files':
      OPTIONS.output_target_files = a
    elif o == '--output-dir':
      OPTIONS.output_dir = a
    elif o == '--output-item-list':
      OPTIONS.output_item_list = a
    elif o == '--rebuild_recovery':
      OPTIONS.rebuild_recovery = True
    elif o == '--keep-tmp':
@@ -764,6 +813,8 @@ def main():
          'other-target-files=',
          'other-item-list=',
          'output-target-files=',
          'output-dir=',
          'output-item-list=',
          'rebuild_recovery',
          'keep-tmp',
      ],
@@ -771,8 +822,11 @@ def main():

  if (len(args) != 0 or
      OPTIONS.system_target_files is None or
      OPTIONS.other_target_files is None or
      OPTIONS.output_target_files is None):
      OPTIONS.other_target_files is None or (
        OPTIONS.output_target_files is None and
        OPTIONS.output_dir is None) or (
        OPTIONS.output_dir is not None and
        OPTIONS.output_item_list is None)):
    common.Usage(__doc__)
    sys.exit(1)

@@ -791,6 +845,11 @@ def main():
  else:
    other_item_list = default_other_item_list

  if OPTIONS.output_item_list:
    output_item_list = read_config_list(OPTIONS.output_item_list)
  else:
    output_item_list = None

  if not validate_config_lists(
      system_item_list=system_item_list,
      system_misc_info_keys=system_misc_info_keys,
@@ -806,6 +865,8 @@ def main():
          other_target_files=OPTIONS.other_target_files,
          other_item_list=other_item_list,
          output_target_files=OPTIONS.output_target_files,
          output_dir=OPTIONS.output_dir,
          output_item_list=output_item_list,
          rebuild_recovery=OPTIONS.rebuild_recovery),
      OPTIONS.keep_tmp)

+57 −3
Original line number Diff line number Diff line
@@ -16,10 +16,12 @@

import os.path

import common
import test_utils
from merge_target_files import (
    read_config_list, validate_config_lists, default_system_item_list,
    default_other_item_list, default_system_misc_info_keys)
from merge_target_files import (read_config_list, validate_config_lists,
                                default_system_item_list,
                                default_other_item_list,
                                default_system_misc_info_keys, copy_items)


class MergeTargetFilesTest(test_utils.ReleaseToolsTestCase):
@@ -27,6 +29,58 @@ class MergeTargetFilesTest(test_utils.ReleaseToolsTestCase):
  def setUp(self):
    self.testdata_dir = test_utils.get_testdata_dir()

  def test_copy_items_CopiesItemsMatchingPatterns(self):

    def createEmptyFile(path):
      if not os.path.exists(os.path.dirname(path)):
        os.makedirs(os.path.dirname(path))
      open(path, 'a').close()
      return path

    def createSymLink(source, dest):
      os.symlink(source, dest)
      return dest

    def getRelPaths(start, filepaths):
      return set(
          os.path.relpath(path=filepath, start=start) for filepath in filepaths)

    input_dir = common.MakeTempDir()
    output_dir = common.MakeTempDir()
    expected_copied_items = []
    actual_copied_items = []
    patterns = ['*.cpp', 'subdir/*.txt']

    # Create various files that we expect to get copied because they
    # match one of the patterns.
    expected_copied_items.extend([
        createEmptyFile(os.path.join(input_dir, 'a.cpp')),
        createEmptyFile(os.path.join(input_dir, 'b.cpp')),
        createEmptyFile(os.path.join(input_dir, 'subdir', 'c.txt')),
        createEmptyFile(os.path.join(input_dir, 'subdir', 'd.txt')),
        createEmptyFile(
            os.path.join(input_dir, 'subdir', 'subsubdir', 'e.txt')),
        createSymLink('a.cpp', os.path.join(input_dir, 'a_link.cpp')),
    ])
    # Create some more files that we expect to not get copied.
    createEmptyFile(os.path.join(input_dir, 'a.h'))
    createEmptyFile(os.path.join(input_dir, 'b.h'))
    createEmptyFile(os.path.join(input_dir, 'subdir', 'subsubdir', 'f.gif'))
    createSymLink('a.h', os.path.join(input_dir, 'a_link.h'))

    # Copy items.
    copy_items(input_dir, output_dir, patterns)

    # Assert the actual copied items match the ones we expected.
    for dirpath, _, filenames in os.walk(output_dir):
      actual_copied_items.extend(
          os.path.join(dirpath, filename) for filename in filenames)
    self.assertEqual(
        getRelPaths(output_dir, actual_copied_items),
        getRelPaths(input_dir, expected_copied_items))
    self.assertEqual(
        os.readlink(os.path.join(output_dir, 'a_link.cpp')), 'a.cpp')

  def test_read_config_list(self):
    system_item_list_file = os.path.join(self.testdata_dir,
                                         'merge_config_system_item_list')