Bug 971265 - Add --no-remove-empty-directories to process_install_manifest.py. r=gps
authorNick Alexander <nalexander@mozilla.com>
Thu, 13 Feb 2014 09:09:08 -0800
changeset 168598 fa1585a40c130a1b37d4871d22f2cf31c545a579
parent 168597 7812cc0ea7954ab1ab7ec7ab73a38ea55693bebf
child 168599 a808e19f51c1dd7c93ffa4bd455783837ba8bae9
push id26210
push userkwierso@gmail.com
push dateFri, 14 Feb 2014 00:52:58 +0000
treeherdermozilla-central@0f18070eb5d6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgps
bugs971265
milestone30.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 971265 - Add --no-remove-empty-directories to process_install_manifest.py. r=gps
python/mozbuild/mozbuild/action/process_install_manifest.py
python/mozbuild/mozpack/copier.py
python/mozbuild/mozpack/test/test_copier.py
--- a/python/mozbuild/mozbuild/action/process_install_manifest.py
+++ b/python/mozbuild/mozbuild/action/process_install_manifest.py
@@ -9,39 +9,46 @@ import sys
 from mozpack.copier import FileCopier
 from mozpack.manifests import InstallManifest
 
 
 COMPLETE = 'From {dest}: Kept {existing} existing; Added/updated {updated}; ' \
     'Removed {rm_files} files and {rm_dirs} directories.'
 
 
-def process_manifest(destdir, paths, remove_unaccounted=True):
+def process_manifest(destdir, paths,
+        remove_unaccounted=True,
+        remove_empty_directories=True):
     manifest = InstallManifest()
     for path in paths:
         manifest |= InstallManifest(path=path)
 
     copier = FileCopier()
     manifest.populate_registry(copier)
-    return copier.copy(destdir, remove_unaccounted=remove_unaccounted)
+    return copier.copy(destdir,
+        remove_unaccounted=remove_unaccounted,
+        remove_empty_directories=remove_empty_directories)
 
 
 def main(argv):
     parser = argparse.ArgumentParser(
         description='Process install manifest files.')
 
     parser.add_argument('destdir', help='Destination directory.')
     parser.add_argument('manifests', nargs='+', help='Path to manifest file(s).')
     parser.add_argument('--no-remove', action='store_true',
         help='Do not remove unaccounted files from destination.')
+    parser.add_argument('--no-remove-empty-directories', action='store_true',
+        help='Do not remove empty directories from destination.')
 
     args = parser.parse_args(argv)
 
     result = process_manifest(args.destdir, args.manifests,
-        remove_unaccounted=not args.no_remove)
+        remove_unaccounted=not args.no_remove,
+        remove_empty_directories=not args.no_remove_empty_directories)
 
     print(COMPLETE.format(dest=args.destdir,
         existing=result.existing_files_count,
         updated=result.updated_files_count,
         rm_files=result.removed_files_count,
         rm_dirs=result.removed_directories_count))
 
 if __name__ == '__main__':
--- a/python/mozbuild/mozpack/copier.py
+++ b/python/mozbuild/mozpack/copier.py
@@ -143,27 +143,28 @@ class FileCopyResult(object):
         return len(self.removed_directories)
 
 
 class FileCopier(FileRegistry):
     '''
     FileRegistry with the ability to copy the registered files to a separate
     directory.
     '''
-    def copy(self, destination, skip_if_older=True, remove_unaccounted=True):
+    def copy(self, destination, skip_if_older=True, remove_unaccounted=True, remove_empty_directories=True):
         '''
         Copy all registered files to the given destination path. The given
         destination can be an existing directory, or not exist at all. It
         can't be e.g. a file.
         The copy process acts a bit like rsync: files are not copied when they
         don't need to (see mozpack.files for details on file.copy).
 
         By default, files in the destination directory that aren't registered
         are removed and empty directories are deleted. To disable removing of
-        unregistered files, pass remove_unaccounted=False.
+        unregistered files, pass remove_unaccounted=False. To disable removing
+        empty directories, pass remove_empty_directories=False.
 
         Returns a FileCopyResult that details what changed.
         '''
         assert isinstance(destination, basestring)
         assert not os.path.exists(destination) or os.path.isdir(destination)
 
         result = FileCopyResult()
         have_symlinks = hasattr(os, 'symlink')
@@ -290,16 +291,19 @@ class FileCopier(FileRegistry):
         # Install files.
         for p, f in self:
             destfile = os.path.normpath(os.path.join(destination, p))
             if f.copy(destfile, skip_if_older):
                 result.updated_files.add(destfile)
             else:
                 result.existing_files.add(destfile)
 
+        if not remove_empty_directories:
+            return result
+
         # Figure out which directories can be removed. This is complicated
         # by the fact we optionally remove existing files. This would be easy
         # if we walked the directory tree after installing files. But, we're
         # trying to minimize system calls.
 
         # Start with the ideal set.
         remove_dirs = existing_dirs - required_dirs
 
--- a/python/mozbuild/mozpack/test/test_copier.py
+++ b/python/mozbuild/mozpack/test/test_copier.py
@@ -194,20 +194,45 @@ class TestFileCopier(TestWithTmpDir):
 
         with open(self.tmppath('populateddir/foo'), 'a'):
             pass
 
         result = copier.copy(self.tmpdir, remove_unaccounted=False)
 
         self.assertEqual(self.all_files(self.tmpdir), set(['foo', 'bar',
             'populateddir/foo']))
+        self.assertEqual(self.all_dirs(self.tmpdir), set(['populateddir']))
         self.assertEqual(result.removed_files, set())
         self.assertEqual(result.removed_directories,
             set([self.tmppath('emptydir')]))
 
+    def test_no_remove_empty_directories(self):
+        copier = FileCopier()
+        copier.add('foo', GeneratedFile('foo'))
+
+        with open(self.tmppath('bar'), 'a'):
+            pass
+
+        os.mkdir(self.tmppath('emptydir'))
+        d = self.tmppath('populateddir')
+        os.mkdir(d)
+
+        with open(self.tmppath('populateddir/foo'), 'a'):
+            pass
+
+        result = copier.copy(self.tmpdir, remove_unaccounted=False,
+            remove_empty_directories=False)
+
+        self.assertEqual(self.all_files(self.tmpdir), set(['foo', 'bar',
+            'populateddir/foo']))
+        self.assertEqual(self.all_dirs(self.tmpdir), set(['emptydir',
+            'populateddir']))
+        self.assertEqual(result.removed_files, set())
+        self.assertEqual(result.removed_directories, set())
+
 
 class TestFilePurger(TestWithTmpDir):
     def test_file_purger(self):
         existing = os.path.join(self.tmpdir, 'existing')
         extra = os.path.join(self.tmpdir, 'extra')
         empty_dir = os.path.join(self.tmpdir, 'dir')
 
         with open(existing, 'a'):