def test_serialization(self): m = self._get_test_manifest() p = self.tmppath("m") m.write(path=p) self.assertTrue(os.path.isfile(p)) with open(p, "rb") as fh: c = fh.read() self.assertEqual(c.count("\n"), 4) lines = c.splitlines() self.assertEqual(len(lines), 4) self.assertEqual(lines[0], "1") self.assertEqual(lines[1], "2\x1fc_dest\x1f%s" % self.tmppath("c_source")) self.assertEqual(lines[2], "3\x1fe_dest") self.assertEqual(lines[3], "1\x1fs_dest\x1f%s" % self.tmppath("s_source")) m2 = InstallManifest(path=p) self.assertEqual(m, m2) p2 = self.tmppath("m2") m2.write(path=p2) with open(p2, "rb") as fh: c2 = fh.read() self.assertEqual(c, c2)
def test_serialization(self): m = self._get_test_manifest() p = self.tmppath('m') m.write(path=p) self.assertTrue(os.path.isfile(p)) with open(p, 'rb') as fh: c = fh.read() self.assertEqual(c.count('\n'), 9) lines = c.splitlines() self.assertEqual(len(lines), 9) self.assertEqual(lines[0], '5') m2 = InstallManifest(path=p) self.assertEqual(m, m2) p2 = self.tmppath('m2') m2.write(path=p2) with open(p2, 'rb') as fh: c2 = fh.read() self.assertEqual(c, c2)
def test_process_manifest(self): source = self.tmppath('source') os.mkdir(source) os.mkdir('%s/base' % source) os.mkdir('%s/base/foo' % source) os.mkdir('%s/base2' % source) with open('%s/base/foo/file1' % source, 'a'): pass with open('%s/base/foo/file2' % source, 'a'): pass with open('%s/base2/file3' % source, 'a'): pass m = InstallManifest() m.add_pattern_link('%s/base' % source, '**', '') m.add_link('%s/base2/file3' % source, 'foo/file3') p = self.tmppath('m') m.write(path=p) dest = self.tmppath('dest') track = self.tmppath('track') for i in range(2): process_install_manifest.process_manifest(dest, [p], track) self.assertTrue(os.path.exists(self.tmppath('dest/foo/file1'))) self.assertTrue(os.path.exists(self.tmppath('dest/foo/file2'))) self.assertTrue(os.path.exists(self.tmppath('dest/foo/file3')))
def test_serialization(self): m = self._get_test_manifest() p = self.tmppath("m") m.write(path=p) self.assertTrue(os.path.isfile(p)) with open(p, "rb") as fh: c = fh.read() self.assertEqual(c.count("\n"), 8) lines = c.splitlines() self.assertEqual(len(lines), 8) self.assertEqual(lines[0], "4") m2 = InstallManifest(path=p) self.assertEqual(m, m2) p2 = self.tmppath("m2") m2.write(path=p2) with open(p2, "rb") as fh: c2 = fh.read() self.assertEqual(c, c2)
def test_serialization(self): m = self._get_test_manifest() p = self.tmppath('m') m.write(path=p) self.assertTrue(os.path.isfile(p)) with open(p, 'rb') as fh: c = fh.read() self.assertEqual(c.count('\n'), 5) lines = c.splitlines() self.assertEqual(len(lines), 5) self.assertEqual(lines[0], '2') self.assertEqual(lines[1], '2\x1fc_dest\x1f%s' % self.tmppath('c_source')) self.assertEqual(lines[2], '3\x1fe_dest') self.assertEqual(lines[3], '4\x1fo_dest') self.assertEqual(lines[4], '1\x1fs_dest\x1f%s' % self.tmppath('s_source')) m2 = InstallManifest(path=p) self.assertEqual(m, m2) p2 = self.tmppath('m2') m2.write(path=p2) with open(p2, 'rb') as fh: c2 = fh.read() self.assertEqual(c, c2)
def test_add_entries_from(self): source = self.tmppath("source") os.mkdir(source) os.mkdir("%s/base" % source) os.mkdir("%s/base/foo" % source) with open("%s/base/foo/file1" % source, "a"): pass with open("%s/base/foo/file2" % source, "a"): pass m = InstallManifest() m.add_pattern_link("%s/base" % source, "**", "dest") p = InstallManifest() p.add_entries_from(m) self.assertEqual(len(p), 1) c = FileCopier() p.populate_registry(c) self.assertEqual(c.paths(), ["dest/foo/file1", "dest/foo/file2"]) q = InstallManifest() q.add_entries_from(m, base="target") self.assertEqual(len(q), 1) d = FileCopier() q.populate_registry(d) self.assertEqual(d.paths(), ["target/dest/foo/file1", "target/dest/foo/file2"]) # Some of the values in an InstallManifest include destination # information that is present in the keys. Verify that we can # round-trip serialization. r = InstallManifest() r.add_entries_from(m) r.add_entries_from(m, base="target") self.assertEqual(len(r), 2) temp_path = self.tmppath("temp_path") r.write(path=temp_path) s = InstallManifest(path=temp_path) e = FileCopier() s.populate_registry(e) self.assertEqual( e.paths(), [ "dest/foo/file1", "dest/foo/file2", "target/dest/foo/file1", "target/dest/foo/file2", ], )
def test_old_install_manifest_deleted(self): # Simulate an install manifest from a previous backend version. Ensure # it is deleted. env = self._get_environment('stub0') purge_dir = mozpath.join(env.topobjdir, '_build_manifests', 'install') manifest_path = mozpath.join(purge_dir, 'old_manifest') os.makedirs(purge_dir) m = InstallManifest() m.write(path=manifest_path) self.assertTrue(os.path.exists(manifest_path)) self._consume('stub0', RecursiveMakeBackend, env) self.assertFalse(os.path.exists(manifest_path))
def test_add_entries_from(self): source = self.tmppath('source') os.mkdir(source) os.mkdir('%s/base' % source) os.mkdir('%s/base/foo' % source) with open('%s/base/foo/file1' % source, 'a'): pass with open('%s/base/foo/file2' % source, 'a'): pass m = InstallManifest() m.add_pattern_link('%s/base' % source, '**', 'dest') p = InstallManifest() p.add_entries_from(m) self.assertEqual(len(p), 1) c = FileCopier() p.populate_registry(c) self.assertEqual(c.paths(), ['dest/foo/file1', 'dest/foo/file2']) q = InstallManifest() q.add_entries_from(m, base='target') self.assertEqual(len(q), 1) d = FileCopier() q.populate_registry(d) self.assertEqual(d.paths(), ['target/dest/foo/file1', 'target/dest/foo/file2']) # Some of the values in an InstallManifest include destination # information that is present in the keys. Verify that we can # round-trip serialization. r = InstallManifest() r.add_entries_from(m) r.add_entries_from(m, base='target') self.assertEqual(len(r), 2) temp_path = self.tmppath('temp_path') r.write(path=temp_path) s = InstallManifest(path=temp_path) e = FileCopier() s.populate_registry(e) self.assertEqual(e.paths(), [ 'dest/foo/file1', 'dest/foo/file2', 'target/dest/foo/file1', 'target/dest/foo/file2' ])
def test_add_entries_from(self): source = self.tmppath('source') os.mkdir(source) os.mkdir('%s/base' % source) os.mkdir('%s/base/foo' % source) with open('%s/base/foo/file1' % source, 'a'): pass with open('%s/base/foo/file2' % source, 'a'): pass m = InstallManifest() m.add_pattern_link('%s/base' % source, '**', 'dest') p = InstallManifest() p.add_entries_from(m) self.assertEqual(len(p), 1) c = FileCopier() p.populate_registry(c) self.assertEqual(c.paths(), ['dest/foo/file1', 'dest/foo/file2']) q = InstallManifest() q.add_entries_from(m, base='target') self.assertEqual(len(q), 1) d = FileCopier() q.populate_registry(d) self.assertEqual(d.paths(), ['target/dest/foo/file1', 'target/dest/foo/file2']) # Some of the values in an InstallManifest include destination # information that is present in the keys. Verify that we can # round-trip serialization. r = InstallManifest() r.add_entries_from(m) r.add_entries_from(m, base='target') self.assertEqual(len(r), 2) temp_path = self.tmppath('temp_path') r.write(path=temp_path) s = InstallManifest(path=temp_path) e = FileCopier() s.populate_registry(e) self.assertEqual(e.paths(), ['dest/foo/file1', 'dest/foo/file2', 'target/dest/foo/file1', 'target/dest/foo/file2'])
def test_process_manifest2(self): self.test_process_manifest() m = InstallManifest() p = self.tmppath('m') m.write(path=p) dest = self.tmppath('dest') track = self.tmppath('track') for i in range(2): process_install_manifest.process_manifest(dest, [p], track) self.assertFalse(os.path.exists(self.tmppath('dest/foo/file1'))) self.assertFalse(os.path.exists(self.tmppath('dest/foo/file2'))) self.assertFalse(os.path.exists(self.tmppath('dest/foo/file3')))
def process_manifest(destdir, paths, track=None, remove_unaccounted=True, remove_all_directory_symlinks=True, remove_empty_directories=True, defines={}): if track: if os.path.exists(track): # We use the same format as install manifests for the tracking # data. manifest = InstallManifest(path=track) remove_unaccounted = FileRegistry() dummy_file = BaseFile() finder = FileFinder(destdir, find_executables=False, find_dotfiles=True) for dest in manifest._dests: if '*' in dest: for p, f in finder.find(dest): remove_unaccounted.add(p, dummy_file) else: remove_unaccounted.add(dest, dummy_file) else: # If tracking is enabled and there is no file, we don't want to # be removing anything. remove_unaccounted = False remove_empty_directories = False remove_all_directory_symlinks = False manifest = InstallManifest() for path in paths: manifest |= InstallManifest(path=path) copier = FileCopier() manifest.populate_registry(copier, defines_override=defines) result = copier.copy( destdir, remove_unaccounted=remove_unaccounted, remove_all_directory_symlinks=remove_all_directory_symlinks, remove_empty_directories=remove_empty_directories) if track: manifest.write(path=track) return result
def test_old_install_manifest_deleted(self): # Simulate an install manifest from a previous backend version. Ensure # it is deleted. env = self._get_environment("stub0") purge_dir = mozpath.join(env.topobjdir, "_build_manifests", "install") manifest_path = mozpath.join(purge_dir, "old_manifest") os.makedirs(purge_dir) m = InstallManifest() m.write(path=manifest_path) with open(mozpath.join(env.topobjdir, "backend.RecursiveMakeBackend"), "w") as f: f.write("%s\n" % manifest_path) self.assertTrue(os.path.exists(manifest_path)) self._consume("stub0", RecursiveMakeBackend, env) self.assertFalse(os.path.exists(manifest_path))
def process_manifest( destdir, paths, track=None, remove_unaccounted=True, remove_all_directory_symlinks=True, remove_empty_directories=True, defines={}, ): if track: if os.path.exists(track): # We use the same format as install manifests for the tracking # data. manifest = InstallManifest(path=track) remove_unaccounted = FileRegistry() dummy_file = BaseFile() finder = FileFinder(destdir, find_executables=False, find_dotfiles=True) for dest in manifest._dests: for p, f in finder.find(dest): remove_unaccounted.add(p, dummy_file) else: # If tracking is enabled and there is no file, we don't want to # be removing anything. remove_unaccounted = False remove_empty_directories = False remove_all_directory_symlinks = False manifest = InstallManifest() for path in paths: manifest |= InstallManifest(path=path) copier = FileCopier() manifest.populate_registry(copier, defines_override=defines) result = copier.copy( destdir, remove_unaccounted=remove_unaccounted, remove_all_directory_symlinks=remove_all_directory_symlinks, remove_empty_directories=remove_empty_directories, ) if track: manifest.write(path=track) return result
def process_manifest(destdir, paths, track, no_symlinks=False, defines={}): if os.path.exists(track): # We use the same format as install manifests for the tracking # data. manifest = InstallManifest(path=track) remove_unaccounted = FileRegistry() dummy_file = BaseFile() finder = FileFinder(destdir, find_dotfiles=True) for dest in manifest._dests: for p, f in finder.find(dest): remove_unaccounted.add(p, dummy_file) remove_empty_directories = True remove_all_directory_symlinks = True else: # If tracking is enabled and there is no file, we don't want to # be removing anything. remove_unaccounted = False remove_empty_directories = False remove_all_directory_symlinks = False manifest = InstallManifest() for path in paths: manifest |= InstallManifest(path=path) copier = FileCopier() link_policy = "copy" if no_symlinks else "symlink" manifest.populate_registry( copier, defines_override=defines, link_policy=link_policy ) result = copier.copy(destdir, remove_unaccounted=remove_unaccounted, remove_all_directory_symlinks=remove_all_directory_symlinks, remove_empty_directories=remove_empty_directories) if track: # We should record files that we actually copied. # It is too late to expand wildcards when the track file is read. manifest.write(path=track, expand_pattern=True) return result
def test_process_manifest(self): source = self.tmppath('source') os.mkdir(source) os.mkdir('%s/base' % source) os.mkdir('%s/base/foo' % source) os.mkdir('%s/base2' % source) with open('%s/base/foo/file1' % source, 'a'): pass with open('%s/base/foo/file2' % source, 'a'): pass with open('%s/base2/file3' % source, 'a'): pass m = InstallManifest() m.add_pattern_link('%s/base' % source, '**', '') m.add_link('%s/base2/file3' % source, 'foo/file3') p = self.tmppath('m') m.write(path=p) dest = self.tmppath('dest') track = self.tmppath('track') for i in range(2): process_install_manifest.process_manifest(dest, [p], track) self.assertTrue(os.path.exists(self.tmppath('dest/foo/file1'))) self.assertTrue(os.path.exists(self.tmppath('dest/foo/file2'))) self.assertTrue(os.path.exists(self.tmppath('dest/foo/file3'))) m = InstallManifest() m.write(path=p) for i in range(2): process_install_manifest.process_manifest(dest, [p], track) self.assertFalse(os.path.exists(self.tmppath('dest/foo/file1'))) self.assertFalse(os.path.exists(self.tmppath('dest/foo/file2'))) self.assertFalse(os.path.exists(self.tmppath('dest/foo/file3')))
def test_process_manifest(self): source = self.tmppath("source") os.mkdir(source) os.mkdir("%s/base" % source) os.mkdir("%s/base/foo" % source) os.mkdir("%s/base2" % source) with open("%s/base/foo/file1" % source, "a"): pass with open("%s/base/foo/file2" % source, "a"): pass with open("%s/base2/file3" % source, "a"): pass m = InstallManifest() m.add_pattern_link("%s/base" % source, "**", "") m.add_link("%s/base2/file3" % source, "foo/file3") p = self.tmppath("m") m.write(path=p) dest = self.tmppath("dest") track = self.tmppath("track") for i in range(2): process_install_manifest.process_manifest(dest, [p], track) self.assertTrue(os.path.exists(self.tmppath("dest/foo/file1"))) self.assertTrue(os.path.exists(self.tmppath("dest/foo/file2"))) self.assertTrue(os.path.exists(self.tmppath("dest/foo/file3"))) m = InstallManifest() m.write(path=p) for i in range(2): process_install_manifest.process_manifest(dest, [p], track) self.assertFalse(os.path.exists(self.tmppath("dest/foo/file1"))) self.assertFalse(os.path.exists(self.tmppath("dest/foo/file2"))) self.assertFalse(os.path.exists(self.tmppath("dest/foo/file3")))
def test_write_expand_pattern(self): source = self.tmppath('source') os.mkdir(source) os.mkdir('%s/base' % source) os.mkdir('%s/base/foo' % source) with open('%s/base/foo/file1' % source, 'a'): pass with open('%s/base/foo/file2' % source, 'a'): pass m = InstallManifest() m.add_pattern_link('%s/base' % source, '**', 'dest') track = self.tmppath('track') m.write(path=track, expand_pattern=True) m = InstallManifest(path=track) self.assertEqual([dest for dest in m._dests], ['dest/foo/file1', 'dest/foo/file2'])
def test_write_expand_pattern(self): source = self.tmppath("source") os.mkdir(source) os.mkdir("%s/base" % source) os.mkdir("%s/base/foo" % source) with open("%s/base/foo/file1" % source, "a"): pass with open("%s/base/foo/file2" % source, "a"): pass m = InstallManifest() m.add_pattern_link("%s/base" % source, "**", "dest") track = self.tmppath("track") m.write(path=track, expand_pattern=True) m = InstallManifest(path=track) self.assertEqual(sorted(dest for dest in m._dests), ["dest/foo/file1", "dest/foo/file2"])
class TestInstallManifest(HelperMixin, unittest.TestCase): def setUp(self): HelperMixin.setUp(self) self.srcdir = os.path.join(self.test_dir, 'src') os.mkdir(self.srcdir) self.objdir = os.path.join(self.test_dir, 'obj') os.mkdir(self.objdir) self.manifest = InstallManifest() self.canonical_mapping = {} for s in ['src1', 'src2']: srcfile = normpath(os.path.join(self.srcdir, s)) objfile = normpath(os.path.join(self.objdir, s)) self.canonical_mapping[objfile] = srcfile self.manifest.add_copy(srcfile, s) self.manifest_file = os.path.join(self.test_dir, 'install-manifest') self.manifest.write(self.manifest_file) def testMakeFileMapping(self): ''' Test that valid arguments are validated. ''' arg = '%s,%s' % (self.manifest_file, self.objdir) ret = symbolstore.validate_install_manifests([arg]) self.assertEqual(len(ret), 1) manifest, dest = ret[0] self.assertTrue(isinstance(manifest, InstallManifest)) self.assertEqual(dest, self.objdir) file_mapping = symbolstore.make_file_mapping(ret) for obj, src in self.canonical_mapping.iteritems(): self.assertTrue(obj in file_mapping) self.assertEqual(file_mapping[obj], src) def testMissingFiles(self): ''' Test that missing manifest files or install directories give errors. ''' missing_manifest = os.path.join(self.test_dir, 'missing-manifest') arg = '%s,%s' % (missing_manifest, self.objdir) with self.assertRaises(IOError) as e: symbolstore.validate_install_manifests([arg]) self.assertEqual(e.filename, missing_manifest) missing_install_dir = os.path.join(self.test_dir, 'missing-dir') arg = '%s,%s' % (self.manifest_file, missing_install_dir) with self.assertRaises(IOError) as e: symbolstore.validate_install_manifests([arg]) self.assertEqual(e.filename, missing_install_dir) def testBadManifest(self): ''' Test that a bad manifest file give errors. ''' bad_manifest = os.path.join(self.test_dir, 'bad-manifest') with open(bad_manifest, 'wb') as f: f.write('junk\n') arg = '%s,%s' % (bad_manifest, self.objdir) with self.assertRaises(IOError) as e: symbolstore.validate_install_manifests([arg]) self.assertEqual(e.filename, bad_manifest) def testBadArgument(self): ''' Test that a bad manifest argument gives an error. ''' with self.assertRaises(ValueError) as e: symbolstore.validate_install_manifests(['foo'])
class TestInstallManifest(HelperMixin, unittest.TestCase): def setUp(self): HelperMixin.setUp(self) self.srcdir = os.path.join(self.test_dir, 'src') os.mkdir(self.srcdir) self.objdir = os.path.join(self.test_dir, 'obj') os.mkdir(self.objdir) self.manifest = InstallManifest() self.canonical_mapping = {} for s in ['src1', 'src2']: srcfile = os.path.join(self.srcdir, s) objfile = os.path.join(self.objdir, s) self.canonical_mapping[objfile] = srcfile self.manifest.add_copy(srcfile, s) self.manifest_file = os.path.join(self.test_dir, 'install-manifest') self.manifest.write(self.manifest_file) def testMakeFileMapping(self): ''' Test that valid arguments are validated. ''' arg = '%s,%s' % (self.manifest_file, self.objdir) ret = symbolstore.validate_install_manifests([arg]) self.assertEqual(len(ret), 1) manifest, dest = ret[0] self.assertTrue(isinstance(manifest, InstallManifest)) self.assertEqual(dest, self.objdir) file_mapping = symbolstore.make_file_mapping(ret) for obj, src in self.canonical_mapping.iteritems(): self.assertTrue(obj in file_mapping) self.assertEqual(file_mapping[obj], src) def testMissingFiles(self): ''' Test that missing manifest files or install directories give errors. ''' missing_manifest = os.path.join(self.test_dir, 'missing-manifest') arg = '%s,%s' % (missing_manifest, self.objdir) with self.assertRaises(IOError) as e: symbolstore.validate_install_manifests([arg]) self.assertEqual(e.filename, missing_manifest) missing_install_dir = os.path.join(self.test_dir, 'missing-dir') arg = '%s,%s' % (self.manifest_file, missing_install_dir) with self.assertRaises(IOError) as e: symbolstore.validate_install_manifests([arg]) self.assertEqual(e.filename, missing_install_dir) def testBadManifest(self): ''' Test that a bad manifest file give errors. ''' bad_manifest = os.path.join(self.test_dir, 'bad-manifest') with open(bad_manifest, 'wb') as f: f.write('junk\n') arg = '%s,%s' % (bad_manifest, self.objdir) with self.assertRaises(IOError) as e: symbolstore.validate_install_manifests([arg]) self.assertEqual(e.filename, bad_manifest) def testBadArgument(self): ''' Test that a bad manifest argument gives an error. ''' with self.assertRaises(ValueError) as e: symbolstore.validate_install_manifests(['foo'])
def test_preprocessor_dependencies(self): manifest = self.tmppath('m') deps = self.tmppath('m.pp') dest = self.tmppath('dest') source = self.tmppath('p_source') destfile = self.tmppath('dest/p_dest') include = self.tmppath('p_incl') os.mkdir(dest) with open(source, 'wt') as fh: fh.write('#define SRC\nSOURCE\n') time = os.path.getmtime(source) - 3 os.utime(source, (time, time)) with open(include, 'wt') as fh: fh.write('INCLUDE\n') time = os.path.getmtime(source) - 3 os.utime(include, (time, time)) # Create and write a manifest with the preprocessed file. m = InstallManifest() m.add_preprocess(source, 'p_dest', deps, '#', { 'FOO': 'BAR', 'BAZ': 'QUX' }) m.write(path=manifest) time = os.path.getmtime(source) - 5 os.utime(manifest, (time, time)) # Now read the manifest back in, and apply it. This should write out # our preprocessed file. m = InstallManifest(path=manifest) c = FileCopier() m.populate_registry(c) self.assertTrue(c.copy(dest)) with open(destfile, 'rt') as fh: self.assertEqual(fh.read(), 'SOURCE\n') # Next, modify the source to #INCLUDE another file. with open(source, 'wt') as fh: fh.write('SOURCE\n#include p_incl\n') time = os.path.getmtime(source) - 1 os.utime(destfile, (time, time)) # Apply the manifest, and confirm that it also reads the newly included # file. m = InstallManifest(path=manifest) c = FileCopier() m.populate_registry(c) c.copy(dest) with open(destfile, 'rt') as fh: self.assertEqual(fh.read(), 'SOURCE\nINCLUDE\n') # Set the time on the source file back, so it won't be picked up as # modified in the next test. time = os.path.getmtime(source) - 1 os.utime(source, (time, time)) # Now, modify the include file (but not the original source). with open(include, 'wt') as fh: fh.write('INCLUDE MODIFIED\n') time = os.path.getmtime(include) - 1 os.utime(destfile, (time, time)) # Apply the manifest, and confirm that the change to the include file # is detected. That should cause the preprocessor to run again. m = InstallManifest(path=manifest) c = FileCopier() m.populate_registry(c) c.copy(dest) with open(destfile, 'rt') as fh: self.assertEqual(fh.read(), 'SOURCE\nINCLUDE MODIFIED\n') # ORing an InstallManifest should copy file dependencies m = InstallManifest() m |= InstallManifest(path=manifest) c = FileCopier() m.populate_registry(c) e = c._files['p_dest'] self.assertEqual(e.extra_depends, [manifest])
def consume_finished(self): mk = Makefile() # Add the default rule at the very beginning. mk.create_rule(['default']) mk.add_statement('TOPSRCDIR = %s' % self.environment.topsrcdir) mk.add_statement('TOPOBJDIR = %s' % self.environment.topobjdir) if not self._has_xpidl: mk.add_statement('NO_XPIDL = 1') # Add a few necessary variables inherited from configure for var in ( 'PYTHON', 'ACDEFINES', 'MOZ_BUILD_APP', 'MOZ_WIDGET_TOOLKIT', ): value = self.environment.substs.get(var) if value is not None: mk.add_statement('%s = %s' % (var, value)) install_manifests_bases = self._install_manifests.keys() # Add information for chrome manifest generation manifest_targets = [] for target, entries in self._manifest_entries.iteritems(): manifest_targets.append(target) install_target = mozpath.basedir(target, install_manifests_bases) self._install_manifests[install_target].add_content( ''.join('%s\n' % e for e in sorted(entries)), mozpath.relpath(target, install_target)) # Add information for install manifests. mk.add_statement('INSTALL_MANIFESTS = %s' % ' '.join(self._install_manifests.keys())) # Add dependencies we infered: for target, deps in self._dependencies.iteritems(): mk.create_rule([target]).add_dependencies( '$(TOPOBJDIR)/%s' % d for d in deps) mk.add_statement('include $(TOPSRCDIR)/config/faster/rules.mk') for base, install_manifest in self._install_manifests.iteritems(): with self._write_file( mozpath.join(self.environment.topobjdir, 'faster', 'install_%s' % base.replace('/', '_'))) as fh: install_manifest.write(fileobj=fh) # For artifact builds only, write a single unified manifest for consumption by |mach watch|. if self.environment.is_artifact_build: unified_manifest = InstallManifest() for base, install_manifest in self._install_manifests.iteritems(): # Expect 'dist/bin/**', which includes 'dist/bin' with no trailing slash. assert base.startswith('dist/bin') base = base[len('dist/bin'):] if base and base[0] == '/': base = base[1:] unified_manifest.add_entries_from(install_manifest, base=base) with self._write_file( mozpath.join(self.environment.topobjdir, 'faster', 'unified_install_dist_bin')) as fh: unified_manifest.write(fileobj=fh) with self._write_file( mozpath.join(self.environment.topobjdir, 'faster', 'Makefile')) as fh: mk.dump(fh, removal_guard=False)
def test_preprocessor_dependencies(self): manifest = self.tmppath('m') deps = self.tmppath('m.pp') dest = self.tmppath('dest') source = self.tmppath('p_source') destfile = self.tmppath('dest/p_dest') include = self.tmppath('p_incl') os.mkdir(dest) with open(source, 'wt') as fh: fh.write('#define SRC\nSOURCE\n') time = os.path.getmtime(source) - 3 os.utime(source, (time, time)) with open(include, 'wt') as fh: fh.write('INCLUDE\n') time = os.path.getmtime(source) - 3 os.utime(include, (time, time)) # Create and write a manifest with the preprocessed file. m = InstallManifest() m.add_preprocess(source, 'p_dest', deps, '#', {'FOO':'BAR', 'BAZ':'QUX'}) m.write(path=manifest) time = os.path.getmtime(source) - 5 os.utime(manifest, (time, time)) # Now read the manifest back in, and apply it. This should write out # our preprocessed file. m = InstallManifest(path=manifest) c = FileCopier() m.populate_registry(c) self.assertTrue(c.copy(dest)) with open(destfile, 'rt') as fh: self.assertEqual(fh.read(), 'SOURCE\n') # Next, modify the source to #INCLUDE another file. with open(source, 'wt') as fh: fh.write('SOURCE\n#include p_incl\n') time = os.path.getmtime(source) - 1 os.utime(destfile, (time, time)) # Apply the manifest, and confirm that it also reads the newly included # file. m = InstallManifest(path=manifest) c = FileCopier() m.populate_registry(c) c.copy(dest) with open(destfile, 'rt') as fh: self.assertEqual(fh.read(), 'SOURCE\nINCLUDE\n') # Set the time on the source file back, so it won't be picked up as # modified in the next test. time = os.path.getmtime(source) - 1 os.utime(source, (time, time)) # Now, modify the include file (but not the original source). with open(include, 'wt') as fh: fh.write('INCLUDE MODIFIED\n') time = os.path.getmtime(include) - 1 os.utime(destfile, (time, time)) # Apply the manifest, and confirm that the change to the include file # is detected. That should cause the preprocessor to run again. m = InstallManifest(path=manifest) c = FileCopier() m.populate_registry(c) c.copy(dest) with open(destfile, 'rt') as fh: self.assertEqual(fh.read(), 'SOURCE\nINCLUDE MODIFIED\n') # ORing an InstallManifest should copy file dependencies m = InstallManifest() m |= InstallManifest(path=manifest) c = FileCopier() m.populate_registry(c) e = c._files['p_dest'] self.assertEqual(e.extra_depends, [manifest])
def test_preprocessor(self): manifest = self.tmppath('m') deps = self.tmppath('m.pp') dest = self.tmppath('dest') include = self.tmppath('p_incl') with open(include, 'wt') as fh: fh.write('#define INCL\n') time = os.path.getmtime(include) - 3 os.utime(include, (time, time)) with open(self.tmppath('p_source'), 'wt') as fh: fh.write('#ifdef FOO\n#if BAZ == QUX\nPASS1\n#endif\n#endif\n') fh.write('#ifdef DEPTEST\nPASS2\n#endif\n') fh.write('#include p_incl\n#ifdef INCLTEST\nPASS3\n#endif\n') time = os.path.getmtime(self.tmppath('p_source')) - 3 os.utime(self.tmppath('p_source'), (time, time)) # Create and write a manifest with the preprocessed file, then apply it. # This should write out our preprocessed file. m = InstallManifest() m.add_preprocess(self.tmppath('p_source'), 'p_dest', deps, '#', {'FOO':'BAR', 'BAZ':'QUX'}) m.write(path=manifest) m = InstallManifest(path=manifest) c = FileCopier() m.populate_registry(c) c.copy(dest) self.assertTrue(os.path.exists(self.tmppath('dest/p_dest'))) with open(self.tmppath('dest/p_dest'), 'rt') as fh: self.assertEqual(fh.read(), 'PASS1\n') # Create a second manifest with the preprocessed file, then apply it. # Since this manifest does not exist on the disk, there should not be a # dependency on it, and the preprocessed file should not be modified. m2 = InstallManifest() m2.add_preprocess(self.tmppath('p_source'), 'p_dest', deps, '#', {'DEPTEST':True}) c = FileCopier() m2.populate_registry(c) result = c.copy(dest) self.assertFalse(self.tmppath('dest/p_dest') in result.updated_files) self.assertTrue(self.tmppath('dest/p_dest') in result.existing_files) # Write out the second manifest, then load it back in from the disk. # This should add the dependency on the manifest file, so our # preprocessed file should be regenerated with the new defines. # We also set the mtime on the destination file back, so it will be # older than the manifest file. m2.write(path=manifest) time = os.path.getmtime(manifest) - 1 os.utime(self.tmppath('dest/p_dest'), (time, time)) m2 = InstallManifest(path=manifest) c = FileCopier() m2.populate_registry(c) self.assertTrue(c.copy(dest)) with open(self.tmppath('dest/p_dest'), 'rt') as fh: self.assertEqual(fh.read(), 'PASS2\n') # Set the time on the manifest back, so it won't be picked up as # modified in the next test time = os.path.getmtime(manifest) - 1 os.utime(manifest, (time, time)) # Update the contents of a file included by the source file. This should # cause the destination to be regenerated. with open(include, 'wt') as fh: fh.write('#define INCLTEST\n') time = os.path.getmtime(include) - 1 os.utime(self.tmppath('dest/p_dest'), (time, time)) c = FileCopier() m2.populate_registry(c) self.assertTrue(c.copy(dest)) with open(self.tmppath('dest/p_dest'), 'rt') as fh: self.assertEqual(fh.read(), 'PASS2\nPASS3\n')
class TestInstallManifest(HelperMixin, unittest.TestCase): def setUp(self): HelperMixin.setUp(self) self.srcdir = os.path.join(self.test_dir, "src") os.mkdir(self.srcdir) self.objdir = os.path.join(self.test_dir, "obj") os.mkdir(self.objdir) self.manifest = InstallManifest() self.canonical_mapping = {} for s in ["src1", "src2"]: srcfile = realpath(os.path.join(self.srcdir, s)) objfile = realpath(os.path.join(self.objdir, s)) self.canonical_mapping[objfile] = srcfile self.manifest.add_copy(srcfile, s) self.manifest_file = os.path.join(self.test_dir, "install-manifest") self.manifest.write(self.manifest_file) def testMakeFileMapping(self): """ Test that valid arguments are validated. """ arg = "%s,%s" % (self.manifest_file, self.objdir) ret = symbolstore.validate_install_manifests([arg]) self.assertEqual(len(ret), 1) manifest, dest = ret[0] self.assertTrue(isinstance(manifest, InstallManifest)) self.assertEqual(dest, self.objdir) file_mapping = symbolstore.make_file_mapping(ret) for obj, src in self.canonical_mapping.items(): self.assertTrue(obj in file_mapping) self.assertEqual(file_mapping[obj], src) def testMissingFiles(self): """ Test that missing manifest files or install directories give errors. """ missing_manifest = os.path.join(self.test_dir, "missing-manifest") arg = "%s,%s" % (missing_manifest, self.objdir) with self.assertRaises(IOError) as e: symbolstore.validate_install_manifests([arg]) self.assertEqual(e.filename, missing_manifest) missing_install_dir = os.path.join(self.test_dir, "missing-dir") arg = "%s,%s" % (self.manifest_file, missing_install_dir) with self.assertRaises(IOError) as e: symbolstore.validate_install_manifests([arg]) self.assertEqual(e.filename, missing_install_dir) def testBadManifest(self): """ Test that a bad manifest file give errors. """ bad_manifest = os.path.join(self.test_dir, "bad-manifest") with open(bad_manifest, "w") as f: f.write("junk\n") arg = "%s,%s" % (bad_manifest, self.objdir) with self.assertRaises(IOError) as e: symbolstore.validate_install_manifests([arg]) self.assertEqual(e.filename, bad_manifest) def testBadArgument(self): """ Test that a bad manifest argument gives an error. """ with self.assertRaises(ValueError): symbolstore.validate_install_manifests(["foo"])
def consume_finished(self): mk = Makefile() # Add the default rule at the very beginning. mk.create_rule(['default']) mk.add_statement('TOPSRCDIR = %s' % self.environment.topsrcdir) mk.add_statement('TOPOBJDIR = %s' % self.environment.topobjdir) if not self._has_xpidl: mk.add_statement('NO_XPIDL = 1') # Add a few necessary variables inherited from configure for var in ( 'PYTHON', 'ACDEFINES', 'MOZ_BUILD_APP', 'MOZ_WIDGET_TOOLKIT', ): value = self.environment.substs.get(var) if value is not None: mk.add_statement('%s = %s' % (var, value)) install_manifests_bases = self._install_manifests.keys() # Add information for chrome manifest generation manifest_targets = [] for target, entries in self._manifest_entries.iteritems(): manifest_targets.append(target) install_target = mozpath.basedir(target, install_manifests_bases) self._install_manifests[install_target].add_content( ''.join('%s\n' % e for e in sorted(entries)), mozpath.relpath(target, install_target)) # Add information for install manifests. mk.add_statement('INSTALL_MANIFESTS = %s' % ' '.join(self._install_manifests.keys())) # Add dependencies we infered: for target, deps in self._dependencies.iteritems(): mk.create_rule([target]).add_dependencies('$(TOPOBJDIR)/%s' % d for d in deps) mk.add_statement('include $(TOPSRCDIR)/config/faster/rules.mk') for base, install_manifest in self._install_manifests.iteritems(): with self._write_file( mozpath.join(self.environment.topobjdir, 'faster', 'install_%s' % base.replace('/', '_'))) as fh: install_manifest.write(fileobj=fh) # For artifact builds only, write a single unified manifest for consumption by |mach watch|. if self.environment.is_artifact_build: unified_manifest = InstallManifest() for base, install_manifest in self._install_manifests.iteritems(): # Expect 'dist/bin/**', which includes 'dist/bin' with no trailing slash. assert base.startswith('dist/bin') base = base[len('dist/bin'):] if base and base[0] == '/': base = base[1:] unified_manifest.add_entries_from(install_manifest, base=base) with self._write_file( mozpath.join(self.environment.topobjdir, 'faster', 'unified_install_dist_bin')) as fh: unified_manifest.write(fileobj=fh) with self._write_file( mozpath.join(self.environment.topobjdir, 'faster', 'Makefile')) as fh: mk.dump(fh, removal_guard=False)
class TestInstallManifest(HelperMixin, unittest.TestCase): def setUp(self): HelperMixin.setUp(self) self.srcdir = os.path.join(self.test_dir, "src") os.mkdir(self.srcdir) self.objdir = os.path.join(self.test_dir, "obj") os.mkdir(self.objdir) self.manifest = InstallManifest() self.canonical_mapping = {} for s in ["src1", "src2"]: srcfile = os.path.join(self.srcdir, s) objfile = os.path.join(self.objdir, s) self.canonical_mapping[objfile] = srcfile self.manifest.add_copy(srcfile, s) self.manifest_file = os.path.join(self.test_dir, "install-manifest") self.manifest.write(self.manifest_file) def testMakeFileMapping(self): """ Test that valid arguments are validated. """ arg = "%s,%s" % (self.manifest_file, self.objdir) ret = symbolstore.validate_install_manifests([arg]) self.assertEqual(len(ret), 1) manifest, dest = ret[0] self.assertTrue(isinstance(manifest, InstallManifest)) self.assertEqual(dest, self.objdir) file_mapping = symbolstore.make_file_mapping(ret) for obj, src in self.canonical_mapping.iteritems(): self.assertTrue(obj in file_mapping) self.assertEqual(file_mapping[obj], src) def testMissingFiles(self): """ Test that missing manifest files or install directories give errors. """ missing_manifest = os.path.join(self.test_dir, "missing-manifest") arg = "%s,%s" % (missing_manifest, self.objdir) with self.assertRaises(IOError) as e: symbolstore.validate_install_manifests([arg]) self.assertEqual(e.filename, missing_manifest) missing_install_dir = os.path.join(self.test_dir, "missing-dir") arg = "%s,%s" % (self.manifest_file, missing_install_dir) with self.assertRaises(IOError) as e: symbolstore.validate_install_manifests([arg]) self.assertEqual(e.filename, missing_install_dir) def testBadManifest(self): """ Test that a bad manifest file give errors. """ bad_manifest = os.path.join(self.test_dir, "bad-manifest") with open(bad_manifest, "wb") as f: f.write("junk\n") arg = "%s,%s" % (bad_manifest, self.objdir) with self.assertRaises(IOError) as e: symbolstore.validate_install_manifests([arg]) self.assertEqual(e.filename, bad_manifest) def testBadArgument(self): """ Test that a bad manifest argument gives an error. """ with self.assertRaises(ValueError) as e: symbolstore.validate_install_manifests(["foo"])
def consume_finished(self): mk = Makefile() # Add the default rule at the very beginning. mk.create_rule(["default"]) mk.add_statement("TOPSRCDIR = %s" % self.environment.topsrcdir) mk.add_statement("TOPOBJDIR = %s" % self.environment.topobjdir) mk.add_statement("MDDEPDIR = .deps") mk.add_statement("TOUCH ?= touch") mk.add_statement("include $(TOPSRCDIR)/config/makefiles/functions.mk") mk.add_statement("include $(TOPSRCDIR)/config/AB_rCD.mk") mk.add_statement("AB_CD = en-US") if not self._has_xpidl: mk.add_statement("NO_XPIDL = 1") # Add a few necessary variables inherited from configure for var in ( "PYTHON3", "ACDEFINES", "MOZ_BUILD_APP", "MOZ_WIDGET_TOOLKIT", ): value = self.environment.substs.get(var) if value is not None: mk.add_statement("%s = %s" % (var, value)) install_manifests_bases = self._install_manifests.keys() # Add information for chrome manifest generation manifest_targets = [] for target, entries in six.iteritems(self._manifest_entries): manifest_targets.append(target) install_target = mozpath.basedir(target, install_manifests_bases) self._install_manifests[install_target].add_content( "".join("%s\n" % e for e in sorted(entries)), mozpath.relpath(target, install_target), ) # Add information for install manifests. mk.add_statement("INSTALL_MANIFESTS = %s" % " ".join(sorted(self._install_manifests.keys()))) # Add dependencies we inferred: for target, deps in sorted(six.iteritems(self._dependencies)): mk.create_rule([target]).add_dependencies("$(TOPOBJDIR)/%s" % d for d in sorted(deps)) # This is not great, but it's better to have some dependencies on these Python files. python_deps = [ "$(TOPSRCDIR)/python/mozbuild/mozbuild/action/l10n_merge.py", "$(TOPSRCDIR)/third_party/python/compare-locales/compare_locales/compare.py", "$(TOPSRCDIR)/third_party/python/compare-locales/compare_locales/paths.py", ] # Add l10n dependencies we inferred: for target, deps in sorted(six.iteritems(self._l10n_dependencies)): mk.create_rule([target]).add_dependencies( "%s" % d[0] for d in sorted(deps, key=itemgetter(0))) for (merge, ref_file, l10n_file) in deps: rule = mk.create_rule([ merge ]).add_dependencies([ref_file, l10n_file] + python_deps) rule.add_commands([ "$(PYTHON3) -m mozbuild.action.l10n_merge " "--output {} --ref-file {} --l10n-file {}".format( merge, ref_file, l10n_file) ]) # Add a dummy rule for the l10n file since it might not exist. mk.create_rule([l10n_file]) mk.add_statement("include $(TOPSRCDIR)/config/faster/rules.mk") for base, install_manifest in six.iteritems(self._install_manifests): with self._write_file( mozpath.join( self.environment.topobjdir, "faster", "install_%s" % base.replace("/", "_"), )) as fh: install_manifest.write(fileobj=fh) # Write a single unified manifest for consumption by |mach watch|. # Since this doesn't start 'install_', it's not processed by the build. unified_manifest = InstallManifest() for base, install_manifest in six.iteritems(self._install_manifests): # Expect 'dist/bin/**', which includes 'dist/bin' with no trailing slash. assert base.startswith("dist/bin") base = base[len("dist/bin"):] if base and base[0] == "/": base = base[1:] unified_manifest.add_entries_from(install_manifest, base=base) with self._write_file( mozpath.join(self.environment.topobjdir, "faster", "unified_install_dist_bin")) as fh: unified_manifest.write(fileobj=fh) for obj in self._generated_files: for stmt in self._format_statements_for_generated_file( obj, "default"): mk.add_statement(stmt) with self._write_file( mozpath.join(self.environment.topobjdir, "faster", "Makefile")) as fh: mk.dump(fh, removal_guard=False)
def test_preprocessor(self): manifest = self.tmppath('m') deps = self.tmppath('m.pp') dest = self.tmppath('dest') include = self.tmppath('p_incl') with open(include, 'wt') as fh: fh.write('#define INCL\n') time = os.path.getmtime(include) - 3 os.utime(include, (time, time)) with open(self.tmppath('p_source'), 'wt') as fh: fh.write('#ifdef FOO\n#if BAZ == QUX\nPASS1\n#endif\n#endif\n') fh.write('#ifdef DEPTEST\nPASS2\n#endif\n') fh.write('#include p_incl\n#ifdef INCLTEST\nPASS3\n#endif\n') time = os.path.getmtime(self.tmppath('p_source')) - 3 os.utime(self.tmppath('p_source'), (time, time)) # Create and write a manifest with the preprocessed file, then apply it. # This should write out our preprocessed file. m = InstallManifest() m.add_preprocess(self.tmppath('p_source'), 'p_dest', deps, '#', { 'FOO': 'BAR', 'BAZ': 'QUX' }) m.write(path=manifest) m = InstallManifest(path=manifest) c = FileCopier() m.populate_registry(c) c.copy(dest) self.assertTrue(os.path.exists(self.tmppath('dest/p_dest'))) with open(self.tmppath('dest/p_dest'), 'rt') as fh: self.assertEqual(fh.read(), 'PASS1\n') # Create a second manifest with the preprocessed file, then apply it. # Since this manifest does not exist on the disk, there should not be a # dependency on it, and the preprocessed file should not be modified. m2 = InstallManifest() m2.add_preprocess(self.tmppath('p_source'), 'p_dest', deps, '#', {'DEPTEST': True}) c = FileCopier() m2.populate_registry(c) result = c.copy(dest) self.assertFalse(self.tmppath('dest/p_dest') in result.updated_files) self.assertTrue(self.tmppath('dest/p_dest') in result.existing_files) # Write out the second manifest, then load it back in from the disk. # This should add the dependency on the manifest file, so our # preprocessed file should be regenerated with the new defines. # We also set the mtime on the destination file back, so it will be # older than the manifest file. m2.write(path=manifest) time = os.path.getmtime(manifest) - 1 os.utime(self.tmppath('dest/p_dest'), (time, time)) m2 = InstallManifest(path=manifest) c = FileCopier() m2.populate_registry(c) self.assertTrue(c.copy(dest)) with open(self.tmppath('dest/p_dest'), 'rt') as fh: self.assertEqual(fh.read(), 'PASS2\n') # Set the time on the manifest back, so it won't be picked up as # modified in the next test time = os.path.getmtime(manifest) - 1 os.utime(manifest, (time, time)) # Update the contents of a file included by the source file. This should # cause the destination to be regenerated. with open(include, 'wt') as fh: fh.write('#define INCLTEST\n') time = os.path.getmtime(include) - 1 os.utime(self.tmppath('dest/p_dest'), (time, time)) c = FileCopier() m2.populate_registry(c) self.assertTrue(c.copy(dest)) with open(self.tmppath('dest/p_dest'), 'rt') as fh: self.assertEqual(fh.read(), 'PASS2\nPASS3\n')
def consume_finished(self): mk = Makefile() # Add the default rule at the very beginning. mk.create_rule(['default']) mk.add_statement('TOPSRCDIR = %s' % self.environment.topsrcdir) mk.add_statement('TOPOBJDIR = %s' % self.environment.topobjdir) mk.add_statement('MDDEPDIR = .deps') mk.add_statement('TOUCH ?= touch') mk.add_statement('include $(TOPSRCDIR)/config/makefiles/functions.mk') mk.add_statement('include $(TOPSRCDIR)/config/AB_rCD.mk') mk.add_statement('AB_CD = en-US') if not self._has_xpidl: mk.add_statement('NO_XPIDL = 1') # Add a few necessary variables inherited from configure for var in ( 'PYTHON3', 'ACDEFINES', 'MOZ_BUILD_APP', 'MOZ_WIDGET_TOOLKIT', ): value = self.environment.substs.get(var) if value is not None: mk.add_statement('%s = %s' % (var, value)) install_manifests_bases = self._install_manifests.keys() # Add information for chrome manifest generation manifest_targets = [] for target, entries in six.iteritems(self._manifest_entries): manifest_targets.append(target) install_target = mozpath.basedir(target, install_manifests_bases) self._install_manifests[install_target].add_content( ''.join('%s\n' % e for e in sorted(entries)), mozpath.relpath(target, install_target)) # Add information for install manifests. mk.add_statement('INSTALL_MANIFESTS = %s' % ' '.join(sorted(self._install_manifests.keys()))) # Add dependencies we inferred: for target, deps in sorted(six.iteritems(self._dependencies)): mk.create_rule([target]).add_dependencies('$(TOPOBJDIR)/%s' % d for d in sorted(deps)) # This is not great, but it's better to have some dependencies on these Python files. python_deps = [ '$(TOPSRCDIR)/python/mozbuild/mozbuild/action/l10n_merge.py', '$(TOPSRCDIR)/third_party/python/compare-locales/compare_locales/compare.py', '$(TOPSRCDIR)/third_party/python/compare-locales/compare_locales/paths.py', ] # Add l10n dependencies we inferred: for target, deps in sorted(six.iteritems(self._l10n_dependencies)): mk.create_rule([target]).add_dependencies( '%s' % d[0] for d in sorted(deps, key=itemgetter(0))) for (merge, ref_file, l10n_file) in deps: rule = mk.create_rule([ merge ]).add_dependencies([ref_file, l10n_file] + python_deps) rule.add_commands([ '$(PYTHON3) -m mozbuild.action.l10n_merge ' '--output {} --ref-file {} --l10n-file {}'.format( merge, ref_file, l10n_file) ]) # Add a dummy rule for the l10n file since it might not exist. mk.create_rule([l10n_file]) mk.add_statement('include $(TOPSRCDIR)/config/faster/rules.mk') for base, install_manifest in six.iteritems(self._install_manifests): with self._write_file( mozpath.join(self.environment.topobjdir, 'faster', 'install_%s' % base.replace('/', '_'))) as fh: install_manifest.write(fileobj=fh) # For artifact builds only, write a single unified manifest # for consumption by |mach watch|. if self.environment.is_artifact_build: unified_manifest = InstallManifest() for base, install_manifest in six.iteritems( self._install_manifests): # Expect 'dist/bin/**', which includes 'dist/bin' with no trailing slash. assert base.startswith('dist/bin') base = base[len('dist/bin'):] if base and base[0] == '/': base = base[1:] unified_manifest.add_entries_from(install_manifest, base=base) with self._write_file( mozpath.join(self.environment.topobjdir, 'faster', 'unified_install_dist_bin')) as fh: unified_manifest.write(fileobj=fh) for obj in self._generated_files: for stmt in self._format_statements_for_generated_file( obj, 'default'): mk.add_statement(stmt) with self._write_file( mozpath.join(self.environment.topobjdir, 'faster', 'Makefile')) as fh: mk.dump(fh, removal_guard=False)