def test_create_canonical_classpath_with_common_prefix(self): """ A special case when two targets' canonical classpath share a common prefix. Until we use `target.id` for canonical classpath, today's implementation is error-prone. This is such a regression test case added for a bug discovered in https://github.com/pantsbuild/pants/pull/2664 TODO(peiyu) Remove once we fully migrate to use `target.id`. """ # a and c' canonical classpath share a common prefix: a/b/b a = self.make_target('a/b', JvmTarget) c = self.make_target('a/b/b/c', JvmTarget) classpath_products = ClasspathProducts(self.pants_workdir) classpath_products.add_for_target(a, [('default', self._path('a.jar'))]) classpath_products.add_for_target(c, [('default', self._path('c.jar'))]) # target c first to verify its first created canonical classpath is preserved self._test_canonical_classpath_helper(classpath_products, [c, a], [ 'a/b/b/c/c/0-c.jar', 'a/b/b/0-a.jar', ], { 'a/b/b/classpath.txt': '{}/a.jar\n'.format(self.pants_workdir), 'a/b/b/c/c/classpath.txt': '{}/c.jar\n'.format(self.pants_workdir), }, False)
def test_create_canonical_classpath_no_duplicate_entry(self): """Test no more than one symlink are created for the same classpath entry.""" jar_path = 'ivy/jars/org.x/lib/x-1.0.jar' resolved_jar = ResolvedJar(M2Coordinate(org='org', name='x', rev='1.0'), cache_path='somewhere', pants_path=self._create_file(jar_path)) target_a = self.make_target('a', JvmTarget) target_b = self.make_target('b', JvmTarget) classpath_products = ClasspathProducts(self.pants_workdir) # Both target a and target b depend on the same jar library classpath_products.add_jars_for_targets([target_a], 'default', [resolved_jar]) classpath_products.add_jars_for_targets([target_b], 'default', [resolved_jar]) with temporary_dir() as base_dir: # Only target a generates symlink to jar library, target b skips creating the # symlink for the same jar library. Both targets' classpath.txt files should # still contain the jar library. self._test_canonical_classpath_helper(classpath_products, [target_a, target_b], base_dir, ['a.a-0.jar'], { 'a.a-classpath.txt': '{}/{}\n'.format(self.pants_workdir, jar_path), 'b.b-classpath.txt': '{}/{}\n'.format(self.pants_workdir, jar_path), })
def execute(self): jar_import_products = self.context.products.get_data(JarImportProducts, init_func=JarImportProducts) # Gather all targets that are both capable of importing jars and actually declare some imports. targets = self.context.targets(predicate=self.has_imports) if not targets: return # Create a list of all of these targets plus the list of JarDependencies they depend on. all_targets = set(targets) for target in targets: all_targets.update(target.imported_targets) imports_classpath = ClasspathProducts(self.get_options().pants_workdir) self.resolve(executor=self.create_java_executor(), targets=all_targets, classpath_products=imports_classpath, invalidate_dependents=True) for target in targets: cp_entries = imports_classpath.get_classpath_entries_for_targets(target.closure(bfs=True)) for conf, cp_entry in cp_entries: if isinstance(cp_entry, ArtifactClasspathEntry): jar_import_products.imported(target, cp_entry.coordinate, cp_entry.path)
def test_single_classpath_element_no_excludes(self): a = self.make_target('a', JvmTarget) classpath_product = ClasspathProducts() path = os.path.join(self.build_root, 'jar/path') self.add_jar_classpath_element_for_path(classpath_product, a, path) self.assertEqual([('default', path)], classpath_product.get_for_target(a))
def test_single_classpath_element_no_excludes(self): a = self.make_target('a', JvmTarget) classpath_product = ClasspathProducts(self.pants_workdir) path = self.path('jar/path') self.add_jar_classpath_element_for_path(classpath_product, a, path) self.assertEqual([('default', path)], classpath_product.get_for_target(a))
def test_fails_if_paths_outside_buildroot(self): a = self.make_target('a', JvmTarget) classpath_product = ClasspathProducts() with self.assertRaises(TaskError) as cm: classpath_product.add_for_target(a, [('default', '/dev/null')]) self.assertEqual( 'Classpath entry /dev/null for target a:a is located outside the buildroot.', str(cm.exception))
def test_fails_if_jar_paths_outside_buildroot(self): a = self.make_target('a', JvmTarget) classpath_product = ClasspathProducts(self.pants_workdir) with self.assertRaises(TaskError) as cm: classpath_product.add_jars_for_targets([a], 'default', [(resolved_example_jar_at('/dev/null'))]) self.assertEqual( 'Classpath entry /dev/null for target a:a is located outside the working directory "{}".'.format(self.pants_workdir), str(cm.exception))
def test_transitive_dependencies_excluded_classpath_element(self): b = self.make_target('b', JvmTarget, excludes=[Exclude('com.example', 'lib')]) a = self.make_target('a', JvmTarget, dependencies=[b]) classpath_product = ClasspathProducts(self.pants_workdir) self.add_jar_classpath_element_for_path(classpath_product, a, self._example_jar_path()) self.add_excludes_for_targets(classpath_product, b, a) classpath = classpath_product.get_for_target(a) self.assertEqual([], classpath)
def _bootstrap_classpath(self, jvm_tool, targets): self._check_underspecified_tools(jvm_tool, targets) workunit_name = 'bootstrap-{}'.format(jvm_tool.key) if JvmResolveSubsystem.global_instance().get_options().resolver == 'ivy': ivy_classpath = self.ivy_classpath(targets, silent=True, workunit_name=workunit_name) return ivy_classpath else: classpath_holder = ClasspathProducts(self.get_options().pants_workdir) CoursierMixin.resolve(self, targets, classpath_holder, sources=False, javadoc=False, executor=None) coursier_classpath = [cp_entry for _, cp_entry in classpath_holder.get_for_targets(targets)] return coursier_classpath
def test_get_product_target_mappings_for_targets_respect_excludes(self): a = self.make_target('a', JvmTarget, excludes=[Exclude('com.example', 'lib')]) classpath_product = ClasspathProducts(self.pants_workdir) example_jar_path = self._example_jar_path() self.add_jar_classpath_element_for_path(classpath_product, a, example_jar_path) self.add_excludes_for_targets(classpath_product, a) classpath_by_product = classpath_product.get_product_target_mappings_for_targets([a]) self.assertEqual([], classpath_by_product)
def test_jar_missing_pants_path_fails_adding(self): b = self.make_target("b", JvmTarget) classpath_products = ClasspathProducts(self.pants_workdir) with self.assertRaises(TaskError) as cm: classpath_products.add_jars_for_targets( [b], "default", [ResolvedJar(M2Coordinate(org="org", name="name"), cache_path="somewhere", pants_path=None)], ) self.assertEqual("Jar: org:name:::jar has no specified path.", str(cm.exception))
def test_excludes_org_name(self): b = self.make_target('b', JvmTarget) a = self.make_target('a', JvmTarget, excludes=[Exclude('com.example')], dependencies=[b]) classpath_product = ClasspathProducts(self.pants_workdir) self.add_example_jar_classpath_element_for(classpath_product, b) self.add_excludes_for_targets(classpath_product, a) classpath = classpath_product.get_for_target(a) self.assertEqual([], classpath)
def test_excluded_classpath_element(self): a = self.make_target("a", JvmTarget, excludes=[Exclude("com.example", "lib")]) classpath_product = ClasspathProducts(self.pants_workdir) example_jar_path = self._example_jar_path() self.add_jar_classpath_element_for_path(classpath_product, a, example_jar_path) self.add_excludes_for_targets(classpath_product, a) classpath = classpath_product.get_for_target(a) self.assertEqual([], classpath)
def test_excludes_used_across_targets(self): b = self.make_target('b', JvmTarget) a = self.make_target('a', JvmTarget, excludes=[Exclude('com.example', 'lib')]) classpath_product = ClasspathProducts(self.pants_workdir) self.add_example_jar_classpath_element_for(classpath_product, b) self.add_excludes_for_targets(classpath_product, a) classpath = classpath_product.get_for_target(a) self.assertEqual([], classpath)
def test_excluded_classpath_element(self): a = self.make_target('a', JvmTarget, excludes=[Exclude('com.example', 'lib')]) classpath_product = ClasspathProducts() example_jar_path = self._example_jar_path() self.add_jar_classpath_element_for_path(classpath_product, a, example_jar_path) self.add_excludes_for_targets(classpath_product, a) classpath = classpath_product.get_for_target(a) self.assertEqual([], classpath)
def test_excludes_similar_org_name(self): b = self.make_target('b', JvmTarget) a = self.make_target('a', JvmTarget, excludes=[Exclude('com.exam')], dependencies=[b]) classpath_product = ClasspathProducts(self.pants_workdir) self.add_example_jar_classpath_element_for(classpath_product, b) self.add_excludes_for_targets(classpath_product, a) classpath = classpath_product.get_for_targets(a.closure(bfs=True)) self.assertEqual([('default', self._example_jar_path())], classpath)
def test_parent_excludes_ignored_for_resolving_child_target(self): b = self.make_target('b', JvmTarget) a = self.make_target('a', JvmTarget, dependencies=[b], excludes=[Exclude('com.example', 'lib')]) example_jar_path = self._example_jar_path() classpath_product = ClasspathProducts(self.pants_workdir) self.add_jar_classpath_element_for_path(classpath_product, b, example_jar_path) self.add_excludes_for_targets(classpath_product, a) classpath = classpath_product.get_for_target(b) self.assertEqual([('default', example_jar_path)], classpath)
def test_parent_exclude_excludes_dependency_jar(self): b = self.make_target("b", JvmTarget) a = self.make_target("a", JvmTarget, dependencies=[b], excludes=[Exclude("com.example", "lib")]) classpath_product = ClasspathProducts(self.pants_workdir) example_jar_path = self._example_jar_path() self.add_jar_classpath_element_for_path(classpath_product, b, example_jar_path) self.add_excludes_for_targets(classpath_product, b, a) classpath = classpath_product.get_for_target(a) self.assertEqual([], classpath)
def test_jar_provided_exclude_with_similar_org(self): provider = self.make_target('provider', ExportableJvmLibrary, provides=Artifact('com.example.lib', '', Repository())) root = self.make_target('root', JvmTarget, dependencies=[provider]) classpath_product = ClasspathProducts(self.pants_workdir) self.add_example_jar_classpath_element_for(classpath_product, root) self.add_excludes_for_targets(classpath_product, provider, root) classpath = classpath_product.get_for_target(root) self.assertEqual([('default', self._example_jar_path())], classpath)
def test_get_product_target_mappings_for_targets_ignore_excludes(self): a = self.make_target("a", JvmTarget, excludes=[Exclude("com.example", "lib")]) classpath_product = ClasspathProducts(self.pants_workdir) example_jar_path = self._example_jar_path() resolved_jar = self.add_jar_classpath_element_for_path(classpath_product, a, example_jar_path, conf="fred-conf") self.add_excludes_for_targets(classpath_product, a) classpath_target_tuples = classpath_product.get_product_target_mappings_for_targets([a], respect_excludes=False) expected_entry = ArtifactClasspathEntry(example_jar_path, resolved_jar.coordinate, resolved_jar.cache_path) self.assertEqual([(("fred-conf", expected_entry), a)], classpath_target_tuples)
def test_jar_missing_pants_path_fails_adding(self): b = self.make_target('b', JvmTarget) classpath_products = ClasspathProducts(self.pants_workdir) with self.assertRaises(TaskError) as cm: classpath_products.add_jars_for_targets([b], 'default', [ResolvedJar(M2Coordinate(org='org', name='name'), cache_path='somewhere', pants_path=None)]) self.assertEqual( 'Jar: org:name: has no specified path.', str(cm.exception))
def test_parent_exclude_excludes_dependency_jar(self): b = self.make_target('b', JvmTarget) a = self.make_target('a', JvmTarget, dependencies=[b], excludes=[Exclude('com.example', 'lib')]) classpath_product = ClasspathProducts() example_jar_path = self._example_jar_path() self.add_jar_classpath_element_for_path(classpath_product, b, example_jar_path) self.add_excludes_for_targets(classpath_product, b, a) classpath = classpath_product.get_for_target(a) self.assertEqual([], classpath)
def test_jar_provided_exclude_with_similar_name(self): # note exclude 'jars/com.example/l' should not match jars/com.example/lib/jars/123.4.jar provider = self.make_target('provider', ExportableJvmLibrary, provides=Artifact('com.example', 'li', Repository())) root = self.make_target('root', JvmTarget, dependencies=[provider]) classpath_product = ClasspathProducts(self.pants_workdir) self.add_example_jar_classpath_element_for(classpath_product, root) self.add_excludes_for_targets(classpath_product, provider, root) classpath = classpath_product.get_for_target(root) self.assertEqual([('default', self._example_jar_path())], classpath)
def test_jar_provided_by_transitive_target_excluded(self): provider = self.make_target('provider', ExportableJvmLibrary, provides=Artifact('com.example', 'lib', Repository())) consumer = self.make_target('consumer', JvmTarget) root = self.make_target('root', JvmTarget, dependencies=[provider, consumer]) classpath_product = ClasspathProducts(self.pants_workdir) self.add_example_jar_classpath_element_for(classpath_product, consumer) self.add_excludes_for_targets(classpath_product, consumer, provider, root) classpath = classpath_product.get_for_target(root) self.assertEqual([], classpath)
def test_jar_in_classpath_not_a_resolved_jar_ignored_by_excludes(self): b = self.make_target('b', JvmTarget) a = self.make_target('a', JvmTarget, excludes=[Exclude('com.example')], dependencies=[b]) example_jar_path = self._example_jar_path() classpath_product = ClasspathProducts(self.pants_workdir) classpath_product.add_for_target(b, [('default', example_jar_path)]) self.add_excludes_for_targets(classpath_product, a) classpath = classpath_product.get_for_targets(a.closure(bfs=True)) self.assertEqual([('default', example_jar_path)], classpath)
def execute(self): basedir = os.path.join(self.get_options().pants_distdir, self._output_folder) runtime_classpath = self.context.products.get_data('runtime_classpath') targets = self.context.targets() if self.get_options().manifest_jar_only: classpath = ClasspathUtil.classpath(targets, runtime_classpath) # Safely create e.g. dist/export-classpath/manifest.jar safe_classpath(classpath, basedir, "manifest.jar") else: ClasspathProducts.create_canonical_classpath(runtime_classpath, targets, basedir, save_classpath_file=True)
def test_fails_if_paths_outside_buildroot(self): a = self.make_target("a", JvmTarget) classpath_product = ClasspathProducts(self.pants_workdir) with self.assertRaises(TaskError) as cm: classpath_product.add_for_target(a, [("default", "/dev/null")]) self.assertEqual( 'Classpath entry /dev/null for target a:a is located outside the working directory "{}".'.format( self.pants_workdir ), str(cm.exception), )
def test_get_classpath_entries_for_targets_dedup(self): b = self.make_target("b", JvmTarget) a = self.make_target("a", JvmTarget, dependencies=[b]) classpath_product = ClasspathProducts(self.pants_workdir) example_jar_path = self._example_jar_path() # resolved_jar is added for both a and b but should return only as one classpath entry resolved_jar = self.add_jar_classpath_element_for_path(classpath_product, a, example_jar_path, conf="fred-conf") self.add_jar_classpath_element_for_path(classpath_product, b, example_jar_path, conf="fred-conf") classpath_target_tuples = classpath_product.get_classpath_entries_for_targets([a], respect_excludes=False) expected_entry = ArtifactClasspathEntry(example_jar_path, resolved_jar.coordinate, resolved_jar.cache_path) self.assertEqual([("fred-conf", expected_entry)], classpath_target_tuples)
def test_path_with_overlapped_conf_added(self): a = self.make_target('a', JvmTarget) classpath_product = ClasspathProducts(self.pants_workdir) path = os.path.join(self.pants_workdir, 'jar/path') classpath_product.add_for_target(a, [('default', path)]) classpath = ClasspathUtil.compute_classpath([a], classpath_product, extra_classpath_tuples=[], confs=['not-default', 'default']) self.assertEqual([path], classpath)
def test_get_classpath_entries_for_targets_ignore_excludes(self): a = self.make_target('a', JvmTarget, excludes=[Exclude('com.example', 'lib')]) classpath_product = ClasspathProducts() example_jar_path = self._example_jar_path() resolved_jar = self.add_jar_classpath_element_for_path(classpath_product, a, example_jar_path, conf='fred-conf') self.add_excludes_for_targets(classpath_product, a) classpath = classpath_product.get_classpath_entries_for_targets([a], respect_excludes=False) expected_entry = ArtifactClasspathEntry(example_jar_path, resolved_jar.coordinate, resolved_jar.cache_path) self.assertEqual([('fred-conf', expected_entry)], list(classpath))
def test_create_canonical_classpath_no_duplicate_entry(self): """Test no more than one symlink are created for the same classpath entry.""" jar_path = "ivy/jars/org.x/lib/x-1.0.jar" resolved_jar = ResolvedJar( M2Coordinate(org="org", name="x", rev="1.0"), cache_path="somewhere", pants_path=self._create_file(jar_path), ) target_a = self.make_target("a", JvmTarget) target_b = self.make_target("b", JvmTarget) classpath_products = ClasspathProducts(self.pants_workdir) # Both target a and target b depend on the same jar library classpath_products.add_jars_for_targets([target_a], "default", [resolved_jar]) classpath_products.add_jars_for_targets([target_b], "default", [resolved_jar]) with temporary_dir() as base_dir: # Only target a generates symlink to jar library, target b skips creating the # symlink for the same jar library. Both targets' classpath.txt files should # still contain the jar library. self._test_canonical_classpath_helper( classpath_products, [target_a, target_b], base_dir, ["a.a-0.jar"], { "a.a-classpath.txt": f"{self.pants_workdir}/{jar_path}\n", "b.b-classpath.txt": f"{self.pants_workdir}/{jar_path}\n", }, )
def test_create_canonical_classpath_with_common_prefix(self): """ A special case when two targets' canonical classpath share a common prefix. Until we use `target.id` for canonical classpath, today's implementation is error-prone. This is such a regression test case added for a bug discovered in https://github.com/pantsbuild/pants/pull/2664 NOTE: incremental test case is covered by `RuntimeClasspathPublisherTest`. TODO(peiyu) Remove once we fully migrate to use `target.id`. """ # a and c' canonical classpath share a common prefix: a/b/b a = self.make_target('a/b', JvmTarget) c = self.make_target('a/b/b/c', JvmTarget) classpath_products = ClasspathProducts(self.pants_workdir) classpath_products.add_for_target(a, [('default', self._path('a.jar'))]) classpath_products.add_for_target(c, [('default', self._path('c.jar'))]) # target c first to verify its first created canonical classpath is preserved with temporary_dir() as base_dir: self._test_canonical_classpath_helper( classpath_products, [c, a], base_dir, False, [ 'a/b/b/c/c/0.jar', 'a/b/b/0.jar', ], { 'a/b/b/classpath.txt': '{}/a.jar\n'.format(self.pants_workdir), 'a/b/b/c/c/classpath.txt': '{}/c.jar\n'.format(self.pants_workdir), })
def test_create_canonical_classpath_no_duplicate_entry(self): """Test no more than one symlink are created for the same classpath entry.""" jar_path = 'ivy/jars/org.x/lib/x-1.0.jar' resolved_jar = ResolvedJar(M2Coordinate(org='org', name='x', rev='1.0'), cache_path='somewhere', pants_path=self._create_file(jar_path)) target_a = self.make_target('a', JvmTarget) target_b = self.make_target('b', JvmTarget) classpath_products = ClasspathProducts(self.pants_workdir) # Both target a and target b depend on the same jar library classpath_products.add_jars_for_targets([target_a], 'default', [resolved_jar]) classpath_products.add_jars_for_targets([target_b], 'default', [resolved_jar]) with temporary_dir() as base_dir: # Only target a generates symlink to jar library, target b skips creating the # symlink for the same jar library. Both targets' classpath.txt files should # still contain the jar library. self._test_canonical_classpath_helper( classpath_products, [target_a, target_b], base_dir, ['a.a-0.jar'], { 'a.a-classpath.txt': '{}/{}\n'.format(self.pants_workdir, jar_path), 'b.b-classpath.txt': '{}/{}\n'.format(self.pants_workdir, jar_path), })
def bundle(self, app, results_dir): """Create a self-contained application bundle. The bundle will contain the target classes, dependencies and resources. """ assert isinstance(app, BundleCreate.App) bundle_dir = self.get_bundle_dir(app.id, results_dir) self.context.log.debug( f"creating {os.path.relpath(bundle_dir, get_buildroot())}") safe_mkdir(bundle_dir, clean=True) classpath = OrderedSet() # Create symlinks for both internal and external dependencies under `lib_dir`. This is # only needed when not creating a deployjar lib_dir = os.path.join(bundle_dir, self.LIBS_DIR) if not app.deployjar: os.mkdir(lib_dir) consolidated_classpath = self.context.products.get_data( "consolidated_classpath") classpath.update( ClasspathProducts.create_canonical_classpath( consolidated_classpath, app.target.closure(bfs=True, **self._target_closure_kwargs), lib_dir, internal_classpath_only=False, excludes=app.binary.deploy_excludes, )) bundle_jar = os.path.join(bundle_dir, f"{app.binary.basename}.jar") with self.monolithic_jar(app.binary, bundle_jar, manifest_classpath=classpath) as jar: self.add_main_manifest_entry(jar, app.binary) # Make classpath complete by adding the monolithic jar. classpath.update([jar.path]) if app.binary.shading_rules: for jar_path in classpath: # In case `jar_path` is a symlink, this is still safe, shaded jar will overwrite jar_path, # original file `jar_path` linked to remains untouched. # TODO run in parallel to speed up self.shade_jar(shading_rules=app.binary.shading_rules, jar_path=jar_path) self.symlink_bundles(app, bundle_dir) return bundle_dir
def map_external_jars(self, targets): external_jar_dir = os.path.join(self.gen_project_workdir, 'external-libs') safe_mkdir(external_jar_dir, clean=True) external_source_jar_dir = os.path.join(self.gen_project_workdir, 'external-libsources') safe_mkdir(external_source_jar_dir, clean=True) external_javadoc_jar_dir = os.path.join(self.gen_project_workdir, 'external-libjavadoc') safe_mkdir(external_javadoc_jar_dir, clean=True) classpath_products = self.resolve_jars(targets) or ClasspathProducts( self.get_options().pants_workdir) cp_entry_by_classifier_by_orgname = defaultdict( lambda: defaultdict(dict)) for conf, jar_entry in classpath_products.get_artifact_classpath_entries_for_targets( targets): coord = (jar_entry.coordinate.org, jar_entry.coordinate.name) classifier = jar_entry.coordinate.classifier cp_entry_by_classifier_by_orgname[coord][classifier] = jar_entry def copy_jar(cp_entry, dest_dir): if not cp_entry: return None cp_jar = os.path.join(dest_dir, os.path.basename(cp_entry.path)) shutil.copy(cp_entry.path, cp_jar) return cp_jar # Per org.name (aka maven "project"), collect the primary artifact and any extra classified # artifacts, taking special note of 'sources' and 'javadoc' artifacts that IDEs handle specially # to provide source browsing and javadocs for 3rdparty libs. for cp_entry_by_classifier in cp_entry_by_classifier_by_orgname.values( ): primary_jar = copy_jar(cp_entry_by_classifier.pop(None, None), external_jar_dir) sources_jar = copy_jar(cp_entry_by_classifier.pop('sources', None), external_source_jar_dir) javadoc_jar = copy_jar(cp_entry_by_classifier.pop('javadoc', None), external_javadoc_jar_dir) if primary_jar: self._project.external_jars.add( ClasspathEntry(jar=primary_jar, source_jar=sources_jar, javadoc_jar=javadoc_jar)) # Treat all other jars as opaque with no source or javadoc attachments of their own. An # example are jars with the 'tests' classifier. for jar_entry in cp_entry_by_classifier.values(): extra_jar = copy_jar(jar_entry, external_jar_dir) self._project.external_jars.add(ClasspathEntry(extra_jar))
def ensure_classpath_products(self, context): """Gets or creates the classpath products expected by `JvmBinaryTask`. :API: public :param context: The pants run context to get/create/associate classpath products with. :type context: :class:`pants.goal.context.Context` :returns: The classpath products associated with the given `context` :rtype: :class:`pants.backend.jvm.tasks.classpath_products.ClasspathProducts` """ return context.products.get_data('runtime_classpath', init_func=ClasspathProducts.init_func( self.pants_workdir))
def resolve_jars(self, targets): executor = self.create_java_executor() confs = ['default'] if self.get_options().source_jars: confs.append('sources') if self.get_options().javadoc_jars: confs.append('javadoc') compile_classpath = ClasspathProducts(self.get_options().pants_workdir) self.resolve(executor=executor, targets=targets, classpath_products=compile_classpath, confs=confs) return compile_classpath
def test_intransitive_dependencies_excluded_classpath_element(self): b = self.make_target('b', JvmTarget, excludes=[Exclude('com.example', 'lib')]) a = self.make_target('a', JvmTarget, dependencies=[b]) classpath_product = ClasspathProducts(self.pants_workdir) example_jar_path = self._example_jar_path() classpath_product.add_for_target(a, [('default', example_jar_path)]) classpath_product.add_excludes_for_targets([a, b]) intransitive_classpath = classpath_product.get_for_target(a) self.assertEqual([('default', example_jar_path)], intransitive_classpath)
def _assert_jars_created(self, *, transitive_only: bool) -> None: with temporary_dir( root_dir=self.pants_workdir) as jar_dir, temporary_dir( root_dir=self.pants_workdir) as dist_dir: self.set_options(pants_distdir=dist_dir, transitive_only=transitive_only) init_target = self.make_target( "java/classpath:java_lib", target_type=JavaLibrary, sources=["com/foo/Bar.java"], ) target_with_dep = self.make_target( "java/classpath:java_lib_with_dep", target_type=JavaLibrary, sources=["com/foo/Bar.java"], dependencies=[init_target], ) context = self.context(target_roots=[target_with_dep]) runtime_classpath = context.products.get_data( "runtime_classpath", init_func=ClasspathProducts.init_func(self.pants_workdir)) task = self.create_task(context) target_classpath_output = os.path.join(dist_dir, self.options_scope) # Create a classpath entry. touch(os.path.join(jar_dir, "dep-target.jar")) touch(os.path.join(jar_dir, "root-target.jar")) runtime_classpath.add_for_target( init_target, [(self.DEFAULT_CONF, os.path.join(jar_dir, "dep-target.jar"))]) runtime_classpath.add_for_target(target_with_dep, [ (self.DEFAULT_CONF, os.path.join(jar_dir, "root-target.jar")) ]) task.execute() all_output = os.listdir(target_classpath_output) # Check only one symlink and classpath.txt were created. expected_artifacts = 2 if transitive_only else 4 self.assertEqual(len(all_output), expected_artifacts) self.assertIn("java.classpath.java_lib-0.jar", all_output) if transitive_only: self.assertNotIn("java.classpath.java_lib_with_dep-0.jar", all_output) else: self.assertIn("java.classpath.java_lib_with_dep-0.jar", all_output)
def execute(self): targets = self.context.targets(predicate=self.is_node_module) if not targets: return node_paths = self.context.products.get_data(NodePaths) # This is required even if node does not need `compile_classpaths` because certain downstream # tasks require `runtime_classpath` to be initialized correctly with `compile_classpaths` compile_classpath = self.context.products.get_data( 'compile_classpath', init_func=ClasspathProducts.init_func( self.get_options().pants_workdir)) runtime_classpath = self.context.products.get_data( 'runtime_classpath', compile_classpath.copy) bundleable_js_product = self.context.products.get_data( 'bundleable_js', init_func=lambda: defaultdict(MultipleRootedProducts)) with self.invalidated( targets, invalidate_dependents=True) as invalidation_check: for vt in invalidation_check.all_vts: target = vt.target node_installed_path = node_paths.node_path(target) with pushd(node_installed_path): if not vt.valid: self._run_build_script(target, vt.results_dir, node_installed_path, node_paths.all_node_paths) if not target.payload.dev_dependency: output_dir = self._get_output_dir( target, node_installed_path) # Make sure that there is output generated. if not os.path.exists(output_dir): raise TaskError( 'Target {} has build script {} specified, but did not generate any output ' 'at {}.\n'.format(target.address.reference(), target.payload.build_script, output_dir)) absolute_symlink( output_dir, os.path.join(vt.results_dir, target.address.target_name)) bundleable_js_product[target].add_abs_paths( output_dir, [output_dir]) runtime_classpath.add_for_target( target, [('default', self._snapshotted_classpath(vt.results_dir))])
def execute(self): deprecated_conditional( lambda: True, removal_version="1.30.0.dev0", entity_description="The `imports` goal", hint_message="Contact the Pants team on Slack or [email protected] " "if you need this functionality.", ) jar_import_products = self.context.products.get_data( JarImportProducts, init_func=JarImportProducts ) # Gather all targets that are both capable of importing jars and actually declare some imports. targets = self.context.targets(predicate=self.has_imports) if not targets: return # Create a list of all of these targets plus the list of JarDependencies they depend on. all_targets = set(targets) for target in targets: all_targets.update(target.imported_targets) imports_classpath = ClasspathProducts(self.get_options().pants_workdir) self.resolve( targets=all_targets, compile_classpath=imports_classpath, sources=False, javadoc=False, executor=self.create_java_executor(), ) for target in targets: cp_entries = imports_classpath.get_classpath_entries_for_targets( target.closure(bfs=True) ) for conf, cp_entry in cp_entries: if isinstance(cp_entry, ArtifactClasspathEntry): jar_import_products.imported(target, cp_entry.coordinate, cp_entry.path)
def _bootstrap_classpath(self, jvm_tool, targets): self._check_underspecified_tools(jvm_tool, targets) workunit_name = 'bootstrap-{}'.format(jvm_tool.key) if JvmResolveSubsystem.global_instance().get_options( ).resolver == 'ivy': ivy_classpath = self.ivy_classpath(targets, silent=True, workunit_name=workunit_name) return ivy_classpath else: classpath_holder = ClasspathProducts( self.get_options().pants_workdir) CoursierMixin.resolve(self, targets, classpath_holder, sources=False, javadoc=False, executor=None) coursier_classpath = [ cp_entry for _, cp_entry in classpath_holder.get_for_targets(targets) ] return coursier_classpath
def test_exclude_leaves_other_jars_unaffected(self): b = self.make_target("b", JvmTarget, excludes=[Exclude("com.example", "lib")]) a = self.make_target("a", JvmTarget, dependencies=[b]) classpath_product = ClasspathProducts(self.pants_workdir) com_example_jar_path = self._example_jar_path() org_example_jar_path = self.path("ivy/jars/org.example/lib/123.4.jar") classpath_product.add_jars_for_targets( [a], "default", [ resolved_example_jar_at(com_example_jar_path), resolved_example_jar_at(org_example_jar_path, org="org.example"), ], ) self.add_excludes_for_targets(classpath_product, b) classpath = classpath_product.get_for_target(a) self.assertEqual([("default", org_example_jar_path)], classpath)
def test_excluded_classpath_element(self): a = self.make_target('a', JvmTarget, excludes=[Exclude('com.example', 'lib')]) classpath_product = ClasspathProducts() example_jar_path = self._example_jar_path() classpath_product.add_for_target(a, [('default', example_jar_path)]) classpath_product.add_excludes_for_targets([a]) classpath = classpath_product.get_for_target(a) self.assertEqual([], classpath)
def test_copy(self): b = self.make_target("b", JvmTarget, excludes=[Exclude("com.example", "lib")]) a = self.make_target("a", JvmTarget, dependencies=[b]) classpath_product = ClasspathProducts(self.pants_workdir) resolved_jar = self.add_jar_classpath_element_for_path( classpath_product, a, self._example_jar_path()) classpath_product.add_for_target(a, [("default", self.path("a/path"))]) copied = classpath_product.copy() a_closure = a.closure(bfs=True) self.assertEqual( [("default", resolved_jar.pants_path), ("default", self.path("a/path"))], classpath_product.get_for_targets(a_closure), ) self.assertEqual( [("default", resolved_jar.pants_path), ("default", self.path("a/path"))], copied.get_for_targets(a_closure), ) self.add_excludes_for_targets(copied, b, a) self.assertEqual( [("default", resolved_jar.pants_path), ("default", self.path("a/path"))], classpath_product.get_for_targets(a_closure), ) self.assertEqual([("default", self.path("a/path"))], copied.get_for_targets(a_closure)) copied.add_for_target(b, [("default", self.path("b/path"))]) self.assertEqual( [("default", resolved_jar.pants_path), ("default", self.path("a/path"))], classpath_product.get_for_targets(a_closure), ) self.assertEqual( [("default", self.path("a/path")), ("default", self.path("b/path"))], copied.get_for_targets(a_closure), )
def execute(self): cp_init_func = ClasspathProducts.init_func( self.get_options().pants_workdir) compile_classpath = self.context.products.get_data( 'compile_classpath', init_func=cp_init_func) with self.invalidated( self.context.targets(is_tools_jar)) as invalidation_check: for vt in invalidation_check.all_vts: tools_classpath = self._tools_classpath_pairs(vt.results_dir) if not vt.valid: self._symlink_tools_classpath(tools_classpath) compile_classpath.add_for_target( vt.target, [('default', entry) for _, entry in tools_classpath])
def execute(self): """Resolves the specified confs for the configured targets and returns an iterator over tuples of (conf, jar path). """ jvm_resolve_subsystem = JvmResolveSubsystem.global_instance() if jvm_resolve_subsystem.get_options().resolver != 'coursier': return # executor = self.create_java_executor() classpath_products = self.context.products.get_data('compile_classpath', init_func=ClasspathProducts.init_func( self.get_options().pants_workdir)) self.resolve(self.context.targets(), classpath_products, sources=False, javadoc=False)
def _setup(self, target_classfiles): """Takes a dict mapping targets to lists of classfiles.""" context = self.context(target_roots=target_classfiles.keys()) # Create classfiles in a target-specific directory, and add it to the classpath for the target. classpath_products = context.products.get_data('runtime_classpath', ClasspathProducts.init_func(self.pants_workdir)) for target, classfiles in target_classfiles.items(): target_dir = os.path.join(self.test_workdir, target.id) safe_mkdir(target_dir) for classfile in classfiles: touch(os.path.join(target_dir, classfile)) classpath_products.add_for_target(target, [('default', target_dir)]) product_deps_by_src = context.products.get_data('product_deps_by_src', dict) return self.create_task(context), product_deps_by_src
def execute(self): """Resolves the specified confs for the configured targets and returns an iterator over tuples of (conf, jar path).""" classpath_products = self.context.products.get_data( "compile_classpath", init_func=ClasspathProducts.init_func(self.get_options().pants_workdir), ) executor = self.create_java_executor() self.resolve( self.context.targets(), classpath_products, sources=self.context.products.is_required_data("resolve_sources_signal"), javadoc=self.context.products.is_required_data("resolve_javadocs_signal"), executor=executor, )
def test_excludes_used_across_targets(self): b = self.make_target('b', JvmTarget) a = self.make_target('a', JvmTarget, excludes=[Exclude('com.example', 'lib')]) classpath_product = ClasspathProducts() classpath_product.add_for_target( b, [('default', self._example_jar_path())]) classpath_product.add_excludes_for_targets([a]) classpath = classpath_product.get_for_target(a) self.assertEqual([], classpath)
def test_excludes_org_name(self): b = self.make_target('b', JvmTarget) a = self.make_target('a', JvmTarget, excludes=[Exclude('com.example')], dependencies=[b]) classpath_product = ClasspathProducts() classpath_product.add_for_target( b, [('default', self._example_jar_path())]) classpath_product.add_excludes_for_targets([a]) classpath = classpath_product.get_for_target(a) self.assertEqual([], classpath)
def test_parent_exclude_excludes_dependency_jar(self): b = self.make_target('b', JvmTarget) a = self.make_target('a', JvmTarget, dependencies=[b], excludes=[Exclude('com.example', 'lib')]) classpath_product = ClasspathProducts() example_jar_path = self._example_jar_path() classpath_product.add_for_target(b, [('default', example_jar_path)]) classpath_product.add_excludes_for_targets([a, b]) classpath = classpath_product.get_for_target(a) self.assertEqual([], classpath)
def execute(self): """Resolves the specified confs for the configured targets and returns an iterator over tuples of (conf, jar path). """ executor = self.create_java_executor() targets = self.context.targets() compile_classpath = self.context.products.get_data( 'compile_classpath', init_func=ClasspathProducts.init_func( self.get_options().pants_workdir)) resolve_hash_name = self.resolve(executor=executor, targets=targets, classpath_products=compile_classpath, confs=self.confs, extra_args=self._args) if self._report: self._generate_ivy_report(resolve_hash_name)
def test_jar_provided_exclude_with_similar_org(self): provider = self.make_target('provider', ExportableJvmLibrary, provides=Artifact('com.example.lib', '', Repository())) root = self.make_target('root', JvmTarget, dependencies=[provider]) classpath_product = ClasspathProducts() classpath_product.add_for_target( root, [('default', self._example_jar_path())]) classpath_product.add_excludes_for_targets([root, provider]) classpath = classpath_product.get_for_target(root) self.assertEqual([('default', self._example_jar_path())], classpath)
def resolve_jars(self, targets): """Resolve :param targets: targets that have dependencies to resolve :return: structure containing the path to resolved jars :rtype: ClasspathProducts """ executor = SubprocessExecutor(DistributionLocator.cached()) classpath_products = self.context.products.get_data( 'classpath_products', init_func=ClasspathProducts.init_func( self.get_options().pants_workdir)) self.resolve(executor=executor, targets=targets, classpath_products=classpath_products, confs=['default'], extra_args=()) return classpath_products
def test_jar_provided_exclude_with_similar_name(self): # note exclude 'jars/com.example/l' should not match jars/com.example/lib/jars/123.4.jar provider = self.make_target('provider', ExportableJvmLibrary, provides=Artifact('com.example', 'li', Repository())) root = self.make_target('root', JvmTarget, dependencies=[provider]) classpath_product = ClasspathProducts() classpath_product.add_for_target( root, [('default', self._example_jar_path())]) classpath_product.add_excludes_for_targets([root, provider]) classpath = classpath_product.get_for_target(root) self.assertEqual([('default', self._example_jar_path())], classpath)
def test_resolve_specific_with_sources_javadocs(self): # Create a jar_library with a single dep, and another library with no deps. dep = JarDependency('commons-lang', 'commons-lang', '2.5') jar_lib = self.make_target('//:a', JarLibrary, jars=[dep]) scala_lib = self.make_target('//:b', JavaLibrary, sources=[]) with self._temp_workdir() as workdir: # Confirm that the deps were added to the appropriate targets. context = self.context(target_roots=[jar_lib, scala_lib]) task = self.prepare_execute(context) compile_classpath = context.products.get_data('compile_classpath', init_func=ClasspathProducts.init_func(workdir) ) task.resolve([jar_lib, scala_lib], compile_classpath, sources=True, javadoc=True, executor=None) # Both javadoc and sources jars are added to the classpath product self.assertEqual(['default', 'src_doc', 'src_doc'], sorted([c[0] for c in compile_classpath.get_for_target(jar_lib)])) self.assertEqual(0, len(compile_classpath.get_for_target(scala_lib)))
def add_consolidated_bundle(self, context, tgt, files_dict): """Add a bundle to the classpath as if it has been consolidated already.""" consolidated_classpath = context.products.get_data( "consolidated_classpath", init_func=ClasspathProducts.init_func(self.pants_workdir)) # Create a temporary directory under the target id, then dump all files. target_dir = os.path.join(self.test_workdir, tgt.id) safe_mkdir(target_dir) entry_path = safe_mkdtemp(dir=target_dir) classpath_dir = safe_mkdtemp(dir=target_dir) for rel_path, content in files_dict.items(): safe_file_dump(os.path.join(entry_path, rel_path), content) # Create Jar to mimic consolidate classpath behavior. jarpath = os.path.join(classpath_dir, "output-0.jar") with self.task.open_jar(jarpath, overwrite=True, compressed=False) as jar: jar.write(entry_path) consolidated_classpath.add_for_target(tgt, [("default", jarpath)])
def test_target_annotation_processor(self): options = attrdict(coverage=True, coverage_jvm_options=[]) syscalls = MockSystemCalls() settings = self.get_settings(options, syscalls) classpath_products = ClasspathProducts(self.pants_workdir) self._add_for_target(classpath_products, self.annotation_target, "anno/target/dir") Cobertura.initialize_instrument_classpath(self.pants_workdir, settings, [self.annotation_target], classpath_products) self.assertEqual(len(syscalls.copy2_calls), 0, "Should be 0 call for the single annotation target.") self._assert_target_copytree(syscalls, frm="anno/target/dir", to="coverage/classes/foo.foo-anno/0")
def _test_canonical_classpath_helper( self, classpath_products, targets, libs_dir, expected_canonical_classpath, expected_classpath_files, excludes=None, ): """Helper method to call `create_canonical_classpath` and verify generated canonical classpath. :param ClasspathProducts classpath_products: Classpath products. :param list targets: List of targets to generate canonical classpath from. :param string libs_dir: Directory where canonical classpath are to be generated. :param list expected_canonical_classpath: List of canonical classpath relative to a base directory. :param dict expected_classpath_files: A dict of classpath.txt path to its expected content. """ canonical_classpath = ClasspathProducts.create_canonical_classpath( classpath_products, targets, libs_dir, save_classpath_file=True, internal_classpath_only=False, excludes=excludes, ) # check canonical path returned self.assertEqual(expected_canonical_classpath, relativize_paths(canonical_classpath, libs_dir)) # check canonical path created contain the exact set of files, no more, no less self.assertTrue( contains_exact_files( libs_dir, expected_canonical_classpath + list(expected_classpath_files.keys()))) # check the content of classpath.txt for classpath_file in expected_classpath_files: self.assertTrue( check_file_content( os.path.join(libs_dir, classpath_file), expected_classpath_files[classpath_file], ))