def _compute_jars_to_resolve_and_pin(raw_jars, artifact_set, manager): """ This method provides settled lists of jar dependencies and coordinates based on conflict management. :param raw_jars: a collection of `JarDependencies` :param artifact_set: PinnedJarArtifactSet :param manager: JarDependencyManagement :return: (list of settled `JarDependency`, set of pinned `M2Coordinate`) """ if artifact_set is None: artifact_set = PinnedJarArtifactSet() untouched_pinned_artifact = {M2Coordinate.create(x) for x in artifact_set} jar_list = list(raw_jars) for i, dep in enumerate(jar_list): direct_coord = M2Coordinate.create(dep) # Portion to manage pinned jars in case of conflict if direct_coord in artifact_set: managed_coord = artifact_set[direct_coord] untouched_pinned_artifact.remove(managed_coord) if direct_coord.rev != managed_coord.rev: # It may be necessary to actually change the version number of the jar we want to resolve # here, because overrides do not apply directly (they are exclusively transitive). This is # actually a good thing, because it gives us more control over what happens. coord = manager.resolve_version_conflict(managed_coord, direct_coord, force=dep.force) # Once a version is settled, we force it anyway jar_list[i] = dep.copy(rev=coord.rev, force=True) return jar_list, untouched_pinned_artifact
def test_m2_string_representation(self): org_name_ref = M2Coordinate(org='org.example', name='lib', rev='the-rev') self.assertEqual('org.example:lib:the-rev', str(org_name_ref)) self.assertEqual(org_name_ref, M2Coordinate.from_string(str(org_name_ref))) org_name_ref_classifier = M2Coordinate(org='org.example', name='lib', rev='the-rev', classifier='classify') self.assertEqual('org.example:lib:jar:classify:the-rev', str(org_name_ref_classifier)) self.assertEqual(org_name_ref_classifier, M2Coordinate.from_string(str(org_name_ref_classifier))) org_name_classifier = M2Coordinate(org='org.example', name='lib', classifier='classify') self.assertEqual('org.example:lib:jar:classify:', str(org_name_classifier)) self.assertEqual(org_name_classifier, M2Coordinate.from_string(str(org_name_classifier))) org_name_type_classifier = M2Coordinate(org='org.example', name='lib', classifier='classify', ext='zip') self.assertEqual('org.example:lib:zip:classify:', str(org_name_type_classifier)) self.assertEqual(org_name_type_classifier, M2Coordinate.from_string(str(org_name_type_classifier))) org_name_type_jar_classifier = M2Coordinate(org='org.example', name='lib', classifier='classify', ext='jar') self.assertEqual('org.example:lib:jar:classify:', str(org_name_type_jar_classifier)) self.assertEqual(org_name_type_jar_classifier, M2Coordinate.from_string(str(org_name_type_jar_classifier)))
def handle_target(target): if not isinstance(target, ManagedJarDependencies): return for artifact in handle_managed_jar_dependencies(target): if artifact.rev != artifact_set[artifact].rev: handle_conflict(artifact, target) specs_by_coordinate[M2Coordinate.create(artifact)] = target.address.spec artifact_set.put(artifact)
def _generate_resolve_ivy(cls, jars, excludes, ivyxml, confs, resolve_hash_name, pinned_artifacts=None, jar_dep_manager=None): org = IvyUtils.INTERNAL_ORG_NAME name = resolve_hash_name extra_configurations = [conf for conf in confs if conf and conf != 'default'] jars_by_key = OrderedDict() for jar in jars: jars = jars_by_key.setdefault((jar.org, jar.name), []) jars.append(jar) manager = jar_dep_manager or JarDependencyManagement.global_instance() artifact_set = PinnedJarArtifactSet(pinned_artifacts) # Copy, because we're modifying it. for jars in jars_by_key.values(): for i, dep in enumerate(jars): direct_coord = M2Coordinate.create(dep) managed_coord = artifact_set[direct_coord] if direct_coord.rev != managed_coord.rev: # It may be necessary to actually change the version number of the jar we want to resolve # here, because overrides do not apply directly (they are exclusively transitive). This is # actually a good thing, because it gives us more control over what happens. coord = manager.resolve_version_conflict(managed_coord, direct_coord, force=dep.force) jars[i] = dep.copy(rev=coord.rev) elif dep.force: # If this dependency is marked as 'force' and there is no version conflict, use the normal # pants behavior for 'force'. artifact_set.put(direct_coord) dependencies = [cls._generate_jar_template(jars) for jars in jars_by_key.values()] # As it turns out force is not transitive - it only works for dependencies pants knows about # directly (declared in BUILD files - present in generated ivy.xml). The user-level ivy docs # don't make this clear [1], but the source code docs do (see isForce docs) [2]. I was able to # edit the generated ivy.xml and use the override feature [3] though and that does work # transitively as you'd hope. # # [1] http://ant.apache.org/ivy/history/2.3.0/settings/conflict-managers.html # [2] https://svn.apache.org/repos/asf/ant/ivy/core/branches/2.3.0/ # src/java/org/apache/ivy/core/module/descriptor/DependencyDescriptor.java # [3] http://ant.apache.org/ivy/history/2.3.0/ivyfile/override.html overrides = [cls._generate_override_template(_coord) for _coord in artifact_set] excludes = [cls._generate_exclude_template(exclude) for exclude in excludes] template_data = TemplateData( org=org, module=name, extra_configurations=extra_configurations, dependencies=dependencies, excludes=excludes, overrides=overrides) template_relpath = os.path.join('templates', 'ivy_utils', 'ivy.xml.mustache') cls._write_ivy_xml_file(ivyxml, template_data, template_relpath)
def test_m2_coordinate_create(self): attrs = ('org', 'name', 'rev', 'classifier', 'ext') class CoordinateLike(object): def __init__(self): for i, a in enumerate(attrs): setattr(self, a, chr(i+ord('a'))) # Set attrs to the first few letters in the alphabet. coord = CoordinateLike() m2 = M2Coordinate.create(coord) self.assertNotEqual(m2, coord) self.assertEqual(tuple(getattr(coord, a) for a in attrs), tuple(getattr(m2, a) for a in attrs))
def put(self, artifact): """Adds the given coordinate to the set, using its version to pin it. If this set already contains an artifact with the same coordinates other than the version, it is replaced by the new artifact. :param M2Coordinate artifact: the artifact coordinate. """ artifact = M2Coordinate.create(artifact) if artifact.rev is None: raise self.MissingVersion('Cannot pin an artifact to version "None"! {}'.format(artifact)) key = self._key(artifact) previous = self._artifacts_to_versions.get(key) self._artifacts_to_versions[key] = artifact if previous != artifact: self._id = None
def test_m2_coordinate_unversioned_noop(self): m2 = M2Coordinate(org='a', name='b', rev=None, classifier='d', ext='e') m2_un = M2Coordinate.unversioned(m2) # Should just return the original object. self.assertIs(m2, m2_un)
def process_target(current_target): """ :type current_target:pants.build_graph.target.Target """ def get_target_type(target): if target.is_test: return ExportTask.SourceRootTypes.TEST else: if (isinstance(target, Resources) and target in resource_target_map and resource_target_map[target].is_test): return ExportTask.SourceRootTypes.TEST_RESOURCE elif isinstance(target, Resources): return ExportTask.SourceRootTypes.RESOURCE else: return ExportTask.SourceRootTypes.SOURCE info = { 'targets': [], 'libraries': [], 'roots': [], 'id': current_target.id, 'target_type': get_target_type(current_target), # NB: is_code_gen should be removed when export format advances to 1.1.0 or higher 'is_code_gen': current_target.is_synthetic, 'is_synthetic': current_target.is_synthetic, 'pants_target_type': self._get_pants_target_alias(type(current_target)), } if not current_target.is_synthetic: info['globs'] = current_target.globs_relative_to_buildroot() if self.get_options().sources: info['sources'] = list( current_target.sources_relative_to_buildroot()) info['transitive'] = current_target.transitive info['scope'] = str(current_target.scope) info['is_target_root'] = current_target in target_roots_set if isinstance(current_target, PythonRequirementLibrary): reqs = current_target.payload.get_field_value( 'requirements', set()) """:type : set[pants.backend.python.python_requirement.PythonRequirement]""" info['requirements'] = [req.key for req in reqs] if isinstance(current_target, PythonTarget): interpreter_for_target = self.select_interpreter_for_targets( [current_target]) if interpreter_for_target is None: raise TaskError( 'Unable to find suitable interpreter for {}'.format( current_target.address)) python_interpreter_targets_mapping[ interpreter_for_target].append(current_target) info['python_interpreter'] = str( interpreter_for_target.identity) def iter_transitive_jars(jar_lib): """ :type jar_lib: :class:`pants.backend.jvm.targets.jar_library.JarLibrary` :rtype: :class:`collections.Iterator` of :class:`pants.java.jar.M2Coordinate` """ if classpath_products: jar_products = classpath_products.get_artifact_classpath_entries_for_targets( (jar_lib, )) for _, jar_entry in jar_products: coordinate = jar_entry.coordinate # We drop classifier and type_ since those fields are represented in the global # libraries dict and here we just want the key into that dict (see `_jar_id`). yield M2Coordinate(org=coordinate.org, name=coordinate.name, rev=coordinate.rev) target_libraries = OrderedSet() if isinstance(current_target, JarLibrary): target_libraries = OrderedSet( iter_transitive_jars(current_target)) for dep in current_target.dependencies: info['targets'].append(dep.address.spec) if isinstance(dep, JarLibrary): for jar in dep.jar_dependencies: target_libraries.add( M2Coordinate(jar.org, jar.name, jar.rev)) # Add all the jars pulled in by this jar_library target_libraries.update(iter_transitive_jars(dep)) if isinstance(dep, Resources): resource_target_map[dep] = current_target if isinstance(current_target, ScalaLibrary): for dep in current_target.java_sources: info['targets'].append(dep.address.spec) process_target(dep) if isinstance(current_target, JvmTarget): info['excludes'] = [ self._exclude_id(exclude) for exclude in current_target.excludes ] info['platform'] = current_target.platform.name if hasattr(current_target, 'test_platform'): info['test_platform'] = current_target.test_platform.name info['roots'] = map( lambda (source_root, package_prefix): { 'source_root': source_root, 'package_prefix': package_prefix }, self._source_roots_for_target(current_target)) if classpath_products: info['libraries'] = [ self._jar_id(lib) for lib in target_libraries ] targets_map[current_target.address.spec] = info
def test_m2_coordinate_create_noop(self): m2 = M2Coordinate(org='a', name='b', rev='c', classifier='d', ext='e') m2_new = M2Coordinate.create(m2) # Should just return the original object. self.assertIs(m2, m2_new)
def to_m2_coord(cls, coord_str, classifier): # TODO: currently assuming all packaging is a jar return M2Coordinate.from_string(coord_str + ':{}:jar'.format(classifier))
def process_target(current_target): """ :type current_target:pants.build_graph.target.Target """ def get_target_type(tgt): def is_test(t): return isinstance(t, JUnitTests) or isinstance( t, PythonTests) if is_test(tgt): return SourceRootTypes.TEST else: if (isinstance(tgt, Resources) and tgt in resource_target_map and is_test(resource_target_map[tgt])): return SourceRootTypes.TEST_RESOURCE elif isinstance(tgt, Resources): return SourceRootTypes.RESOURCE else: return SourceRootTypes.SOURCE info = { "targets": [], "libraries": [], "roots": [], "id": current_target.id, "target_type": get_target_type(current_target), # NB: is_code_gen should be removed when export format advances to 1.1.0 or higher "is_code_gen": current_target.is_synthetic, "is_synthetic": current_target.is_synthetic, "pants_target_type": self._get_pants_target_alias(type(current_target)), } if not current_target.is_synthetic: info["globs"] = current_target.globs_relative_to_buildroot() if self.get_options().sources: info["sources"] = list( current_target.sources_relative_to_buildroot()) info["transitive"] = current_target.transitive info["scope"] = str(current_target.scope) info["is_target_root"] = current_target in target_roots_set if isinstance(current_target, PythonRequirementLibrary): reqs = current_target.payload.get_field_value( "requirements", set()) """:type : set[pants.python.python_requirement.PythonRequirement]""" info["requirements"] = [req.key for req in reqs] if isinstance(current_target, PythonTarget): interpreter_for_target = self._interpreter_cache.select_interpreter_for_targets( [current_target]) if interpreter_for_target is None: raise TaskError( "Unable to find suitable interpreter for {}".format( current_target.address)) python_interpreter_targets_mapping[ interpreter_for_target].append(current_target) info["python_interpreter"] = str( interpreter_for_target.identity) def iter_transitive_jars(jar_lib): """ :type jar_lib: :class:`pants.backend.jvm.targets.jar_library.JarLibrary` :rtype: :class:`collections.Iterator` of :class:`pants.java.jar.M2Coordinate` """ if classpath_products: jar_products = classpath_products.get_artifact_classpath_entries_for_targets( (jar_lib, )) for _, jar_entry in jar_products: coordinate = jar_entry.coordinate # We drop classifier and type_ since those fields are represented in the global # libraries dict and here we just want the key into that dict (see `_jar_id`). yield M2Coordinate(org=coordinate.org, name=coordinate.name, rev=coordinate.rev) target_libraries = OrderedSet() if isinstance(current_target, JarLibrary): target_libraries = OrderedSet( iter_transitive_jars(current_target)) for dep in current_target.dependencies: info["targets"].append(dep.address.spec) if isinstance(dep, JarLibrary): for jar in dep.jar_dependencies: target_libraries.add( M2Coordinate(jar.org, jar.name, jar.rev)) # Add all the jars pulled in by this jar_library target_libraries.update(iter_transitive_jars(dep)) if isinstance(dep, Resources): resource_target_map[dep] = current_target if isinstance(current_target, ScalaLibrary): for dep in current_target.java_sources: info["targets"].append(dep.address.spec) process_target(dep) if isinstance(current_target, JvmTarget): info["excludes"] = [ self._exclude_id(exclude) for exclude in current_target.excludes ] info["platform"] = current_target.platform.name if hasattr(current_target, "runtime_platform"): info[ "runtime_platform"] = current_target.runtime_platform.name info["roots"] = [{ "source_root": source_root_package_prefix[0], "package_prefix": source_root_package_prefix[1], } for source_root_package_prefix in self._source_roots_for_target( current_target)] if classpath_products: info["libraries"] = [ self._jar_id(lib) for lib in target_libraries ] targets_map[current_target.address.spec] = info
def m2_for(c): return M2Coordinate.from_string(c)
def resolve_version_conflict(self, managed_coord, direct_coord, force=False): """Resolves an artifact version conflict between directly specified and managed jars. This uses the user-defined --conflict-strategy to pick the appropriate artifact version (or to raise an error). This assumes the two conflict coordinates differ only by their version. :param M2Coordinate managed_coord: the artifact coordinate as defined by a managed_jar_dependencies object. :param M2Coordinate direct_coord: the artifact coordinate as defined by a jar_library target. :param bool force: Whether the artifact defined by the jar_library() was marked with force=True. This is checked only if one of the *_IF_FORCED conflict strategies is being used. :return: the coordinate of the artifact that should be resolved. :rtype: M2Coordinate :raises: JarDependencyManagement.DirectManagedVersionConflict if the versions are different and the --conflict-strategy is 'FAIL' (which is the default). """ if M2Coordinate.unversioned(managed_coord) != M2Coordinate.unversioned( direct_coord): raise ValueError( 'Illegal arguments passed to resolve_version_conflict: managed_coord and ' 'direct_coord must only differ by their version!\n' ' Managed: {}\n Direct: {}\n'.format( M2Coordinate.unversioned(managed_coord), M2Coordinate.unversioned(direct_coord), )) if direct_coord.rev is None or direct_coord.rev == managed_coord.rev: return managed_coord strategy = self.get_options().conflict_strategy message = dedent(""" An artifact directly specified by a jar_library target has a different version than what is specified by managed_jar_dependencies. Artifact: jar(org={org}, name={name}, classifier={classifier}, ext={ext}) Direct version: {direct} Managed version: {managed} """).format( org=direct_coord.org, name=direct_coord.name, classifier=direct_coord.classifier, ext=direct_coord.ext, direct=direct_coord.rev, managed=managed_coord.rev, ) if strategy == 'FAIL': raise self.DirectManagedVersionConflict( '{}\nThis raises an error due to the current --jar-dependency-management-conflict-strategy.' .format(message)) is_silent = self.get_options().suppress_conflict_warnings log = logger.debug if is_silent else logger.warn if strategy == 'USE_DIRECT': log(message) log('[{}] Using direct version: {}'.format(strategy, direct_coord)) return direct_coord if strategy == 'USE_DIRECT_IF_FORCED': log(message) if force: log('[{}] Using direct version, because force=True: {}'.format( strategy, direct_coord)) return direct_coord else: log('[{}] Using managed version, because force=False: {}'. format(strategy, managed_coord)) return managed_coord if strategy == 'USE_MANAGED': log(message) log('[{}] Using managed version: {}'.format( strategy, managed_coord)) return managed_coord if strategy == 'USE_NEWER': newer = max([managed_coord, direct_coord], key=lambda coord: Revision.lenient(coord.rev)) log(message) log('[{}] Using newer version: {}'.format(strategy, newer)) return newer raise TaskError( 'Unknown value for --conflict-strategy: {}'.format(strategy))
def test_resolved_jars_with_same_properties(self): jar1 = ResolvedJar(M2Coordinate('org.example', 'lib'), 'path') jar2 = ResolvedJar(M2Coordinate('org.example', 'lib'), 'path') self.assertEqual(jar1, jar2) self.assertEqual(hash(jar1), hash(jar2))
def test_resolved_jars_with_differing_cache_paths_not_equal(self): jar1 = ResolvedJar(M2Coordinate('org.example', 'lib'), 'path1') jar2 = ResolvedJar(M2Coordinate('org.example', 'lib'), 'path2') self.assertNotEqual(jar1, jar2)
def test_put_failure(self): set1 = PinnedJarArtifactSet() with self.assertRaises(PinnedJarArtifactSet.MissingVersion): set1.put(M2Coordinate("hello", "there"))
def test_lookup_noop(self): self.assertEqual( M2Coordinate("org", "foo", "1.2"), PinnedJarArtifactSet()[M2Coordinate("org", "foo", "1.2")], )
class JarDependencyManagementTest(TestBase): _coord_any = M2Coordinate("foobar", "foobar") _coord_one = M2Coordinate("foobar", "foobar", "1.1") _coord_two = M2Coordinate("foobar", "foobar", "1.2") def _jar_dependency_management(self, **flags): Subsystem.reset() options = { JarDependencyManagement.options_scope: flags, } return global_subsystem_instance(JarDependencyManagement, options=options) def test_conflict_strategy_short_circuits(self): manager = self._jar_dependency_management(conflict_strategy="FAIL") manager.resolve_version_conflict( direct_coord=self._coord_any, managed_coord=self._coord_one, ) manager.resolve_version_conflict( direct_coord=self._coord_one, managed_coord=self._coord_one, ) def test_conflict_strategy_fail(self): manager = self._jar_dependency_management(conflict_strategy="FAIL") with self.assertRaises( JarDependencyManagement.DirectManagedVersionConflict): manager.resolve_version_conflict( direct_coord=self._coord_one, managed_coord=self._coord_two, ) def test_conflict_strategy_use_direct(self): manager = self._jar_dependency_management( conflict_strategy="USE_DIRECT") self.assertEqual( self._coord_one, manager.resolve_version_conflict( direct_coord=self._coord_one, managed_coord=self._coord_two, ), ) manager = self._jar_dependency_management( conflict_strategy="USE_DIRECT", suppress_conflict_messages=True) self.assertEqual( self._coord_one, manager.resolve_version_conflict( direct_coord=self._coord_one, managed_coord=self._coord_two, ), ) def test_conflict_strategy_use_managed(self): manager = self._jar_dependency_management( conflict_strategy="USE_MANAGED") self.assertEqual( self._coord_two, manager.resolve_version_conflict( direct_coord=self._coord_one, managed_coord=self._coord_two, ), ) manager = self._jar_dependency_management( conflict_strategy="USE_MANAGED", suppress_conflict_messages=True) self.assertEqual( self._coord_two, manager.resolve_version_conflict( direct_coord=self._coord_one, managed_coord=self._coord_two, ), ) def test_conflict_strategy_use_forced(self): manager = self._jar_dependency_management( conflict_strategy="USE_DIRECT_IF_FORCED") self.assertEqual( self._coord_two, manager.resolve_version_conflict( direct_coord=self._coord_one, managed_coord=self._coord_two, ), ) self.assertEqual( self._coord_one, manager.resolve_version_conflict( direct_coord=self._coord_one, managed_coord=self._coord_two, force=True, ), ) def test_conflict_strategy_use_newer(self): manager = self._jar_dependency_management( conflict_strategy="USE_NEWER") self.assertEqual( self._coord_two, manager.resolve_version_conflict( direct_coord=self._coord_one, managed_coord=self._coord_two, ), ) self.assertEqual( self._coord_two, manager.resolve_version_conflict( direct_coord=self._coord_two, managed_coord=self._coord_one, ), ) def test_conflict_resolution_input_validation(self): manager = self._jar_dependency_management() with self.assertRaises(ValueError): manager.resolve_version_conflict(M2Coordinate("org", "foo", "1.2"), M2Coordinate("com", "bar", "7.8")) with self.assertRaises(ValueError): manager.resolve_version_conflict(M2Coordinate("org", "foo", "1.2"), M2Coordinate("com", "bar", "1.2"))
def test_create_canonical_classpath(self): a = self.make_target("a/b", JvmTarget) jar_path = "ivy/jars/org.x/lib/x-1.0.jar" classpath_products = ClasspathProducts(self.pants_workdir) resolved_jar = ResolvedJar( M2Coordinate(org="org", name="x", rev="1.0"), cache_path="somewhere", pants_path=self._create_file(jar_path), ) classpath_products.add_for_target( a, [("default", self._create_file("a.jar")), ("default", self._create_file("resources"))], ) classpath_products.add_jars_for_targets([a], "default", [resolved_jar]) with temporary_dir() as base_dir: self._test_canonical_classpath_helper( classpath_products, [a], base_dir, ["a.b.b-0.jar", "a.b.b-1", "a.b.b-2.jar"], { "a.b.b-classpath.txt": "{}/a.jar:{}/resources:{}/{}\n".format( self.pants_workdir, self.pants_workdir, self.pants_workdir, jar_path) }, excludes={Exclude(org="org", name="y")}, ) # incrementally delete the resource dependency classpath_products = ClasspathProducts(self.pants_workdir) classpath_products.add_for_target( a, [("default", self._create_file("a.jar"))]) self._test_canonical_classpath_helper( classpath_products, [a], base_dir, ["a.b.b-0.jar"], {"a.b.b-classpath.txt": f"{self.pants_workdir}/a.jar\n"}, ) # incrementally add another jar dependency classpath_products = ClasspathProducts(self.pants_workdir) classpath_products.add_for_target( a, [("default", self._create_file("a.jar")), ("default", self._create_file("b.jar"))]) self._test_canonical_classpath_helper( classpath_products, [a], base_dir, ["a.b.b-0.jar", "a.b.b-1.jar"], { "a.b.b-classpath.txt": "{}/a.jar:{}/b.jar\n".format(self.pants_workdir, self.pants_workdir) }, )
def resolved_example_jar_at(path, org="com.example", name="lib"): return ResolvedJar( M2Coordinate(org=org, name=name), cache_path=os.path.join("resolver-cache-dir", path), pants_path=path, )
def test_m2_coordinate_create_noop(self): m2 = M2Coordinate(org='a', name='b', rev='c', classifier='d', ext='e') m2_new = M2Coordinate.create( m2) # Should just return the original object. self.assertIs(m2, m2_new)
def test_m2_coordinate_artifact_path_explicit_ext(self): coordinate = M2Coordinate('org.example', 'lib', '1.0.0', ext='tar.gz') self.assertEqual('org.example-lib-1.0.0.tar.gz', coordinate.artifact_filename)
def test_m2_coordinate_unversioned_noop(self): m2 = M2Coordinate(org='a', name='b', rev=None, classifier='d', ext='e') m2_un = M2Coordinate.unversioned( m2) # Should just return the original object. self.assertIs(m2, m2_un)
class JarDependencyManagementTest(BaseTest): _coord_any = M2Coordinate('foobar', 'foobar') _coord_one = M2Coordinate('foobar', 'foobar', '1.1') _coord_two = M2Coordinate('foobar', 'foobar', '1.2') def _jar_dependency_management(self, **flags): Subsystem.reset() options = { JarDependencyManagement.options_scope: flags, } return global_subsystem_instance(JarDependencyManagement, options=options) def test_conflict_strategy_short_circuits(self): manager = self._jar_dependency_management(conflict_strategy='FAIL') manager.resolve_version_conflict( direct_coord=self._coord_any, managed_coord=self._coord_one, ) manager.resolve_version_conflict( direct_coord=self._coord_one, managed_coord=self._coord_one, ) def test_conflict_strategy_fail(self): manager = self._jar_dependency_management(conflict_strategy='FAIL') with self.assertRaises( JarDependencyManagement.DirectManagedVersionConflict): manager.resolve_version_conflict( direct_coord=self._coord_one, managed_coord=self._coord_two, ) def test_conflict_strategy_use_direct(self): manager = self._jar_dependency_management( conflict_strategy='USE_DIRECT') self.assertEquals( self._coord_one, manager.resolve_version_conflict( direct_coord=self._coord_one, managed_coord=self._coord_two, )) manager = self._jar_dependency_management( conflict_strategy='USE_DIRECT', suppress_conflict_messages=True) self.assertEquals( self._coord_one, manager.resolve_version_conflict( direct_coord=self._coord_one, managed_coord=self._coord_two, )) def test_conflict_strategy_use_managed(self): manager = self._jar_dependency_management( conflict_strategy='USE_MANAGED') self.assertEquals( self._coord_two, manager.resolve_version_conflict( direct_coord=self._coord_one, managed_coord=self._coord_two, )) manager = self._jar_dependency_management( conflict_strategy='USE_MANAGED', suppress_conflict_messages=True) self.assertEquals( self._coord_two, manager.resolve_version_conflict( direct_coord=self._coord_one, managed_coord=self._coord_two, )) def test_conflict_strategy_use_forced(self): manager = self._jar_dependency_management( conflict_strategy='USE_DIRECT_IF_FORCED') self.assertEquals( self._coord_two, manager.resolve_version_conflict( direct_coord=self._coord_one, managed_coord=self._coord_two, )) self.assertEquals( self._coord_one, manager.resolve_version_conflict( direct_coord=self._coord_one, managed_coord=self._coord_two, force=True, )) def test_conflict_strategy_use_newer(self): manager = self._jar_dependency_management( conflict_strategy='USE_NEWER') self.assertEquals( self._coord_two, manager.resolve_version_conflict( direct_coord=self._coord_one, managed_coord=self._coord_two, )) self.assertEquals( self._coord_two, manager.resolve_version_conflict( direct_coord=self._coord_two, managed_coord=self._coord_one, )) def test_conflict_resolution_input_validation(self): manager = self._jar_dependency_management() with self.assertRaises(ValueError): manager.resolve_version_conflict(M2Coordinate('org', 'foo', '1.2'), M2Coordinate('com', 'bar', '7.8')) with self.assertRaises(ValueError): manager.resolve_version_conflict(M2Coordinate('org', 'foo', '1.2'), M2Coordinate('com', 'bar', '1.2'))
def test_m2_coordinate_unversioned(self): m2 = M2Coordinate(org='a', name='b', rev='c', classifier='d', ext='e') m2_un = M2Coordinate.unversioned(m2) self.assertNotEqual(m2, m2_un) self.assertTrue(m2_un.rev is None)
def resolved_example_jar_at(path, org='com.example', name='lib'): return ResolvedJar(M2Coordinate(org=org, name=name), cache_path=os.path.join('resolver-cache-dir', path), pants_path=path)
def test_m2_coordinates_with_same_properties(self): coordinate1 = M2Coordinate('org.example', 'lib') coordinate2 = M2Coordinate('org.example', 'lib') self.assertEqual(coordinate1, coordinate2) self.assertEqual(hash(coordinate1), hash(coordinate2))
def to_m2_coord(cls, coord_str): return M2Coordinate.from_string(coord_str)
def test_lookup_noop(self): self.assertEquals( M2Coordinate('org', 'foo', '1.2'), PinnedJarArtifactSet()[M2Coordinate('org', 'foo', '1.2')])
def coord(org, name, classifier=None, rev=None, ext=None): rev = rev or '0.0.1' return M2Coordinate(org=org, name=name, rev=rev, classifier=classifier, ext=ext)
def test_m2_coordinate_artifact_path_classifier(self): coordinate = M2Coordinate("org.example", "lib", "1.0.0", "sources") self.assertEqual("org.example-lib-1.0.0-sources.jar", coordinate.artifact_filename)
def test_m2_coordinate_artifact_path_explicit_ext(self): coordinate = M2Coordinate("org.example", "lib", "1.0.0", ext="tar.gz") self.assertEqual("org.example-lib-1.0.0.tar.gz", coordinate.artifact_filename)
def test_m2_coordinate_artifact_path_classifier(self): coordinate = M2Coordinate('org.example', 'lib', '1.0.0', 'sources') self.assertEqual('org.example-lib-1.0.0-sources.jar', coordinate.artifact_filename)
def test_m2_coordinates_with_differing_properties_not_equal(self): coordinate1 = M2Coordinate('org.example', 'lib') coordinate2 = M2Coordinate('org.example', 'lib2') self.assertNotEqual(coordinate1, coordinate2)
def _make_coord(self, rev): return M2Coordinate(org='com.example', name='bar', rev=rev)
def test_m2_coordinates_with_different_types_have_different_hashes(self): coordinate1 = M2Coordinate('org.example', 'lib', ext='zip') coordinate2 = M2Coordinate('org.example', 'lib') self.assertNotEqual(hash(coordinate1), hash(coordinate2))
def test_m2_coordinate_artifact_path_no_rev(self): coordinate = M2Coordinate('org.example', 'lib') self.assertEqual('org.example-lib.jar', coordinate.artifact_filename)
def resolve_version_conflict(self, managed_coord, direct_coord, force=False): """Resolves an artifact version conflict between directly specified and managed jars. This uses the user-defined --conflict-strategy to pick the appropriate artifact version (or to raise an error). This assumes the two conflict coordinates differ only by their version. :param M2Coordinate managed_coord: the artifact coordinate as defined by a managed_jar_dependencies object. :param M2Coordinate direct_coord: the artifact coordinate as defined by a jar_library target. :param bool force: Whether the artifact defined by the jar_library() was marked with force=True. This is checked only if one of the *_IF_FORCED conflict strategies is being used. :return: the coordinate of the artifact that should be resolved. :rtype: M2Coordinate :raises: JarDependencyManagement.DirectManagedVersionConflict if the versions are different and the --conflict-strategy is 'FAIL' (which is the default). """ if M2Coordinate.unversioned(managed_coord) != M2Coordinate.unversioned(direct_coord): raise ValueError('Illegal arguments passed to resolve_version_conflict: managed_coord and ' 'direct_coord must only differ by their version!\n' ' Managed: {}\n Direct: {}\n'.format( M2Coordinate.unversioned(managed_coord), M2Coordinate.unversioned(direct_coord), )) if direct_coord.rev is None or direct_coord.rev == managed_coord.rev: return managed_coord strategy = self.get_options().conflict_strategy message = dedent(""" An artifact directly specified by a jar_library target has a different version than what is specified by managed_jar_dependencies. Artifact: jar(org={org}, name={name}, classifier={classifier}, ext={ext}) Direct version: {direct} Managed version: {managed} """).format( org=direct_coord.org, name=direct_coord.name, classifier=direct_coord.classifier, ext=direct_coord.ext, direct=direct_coord.rev, managed=managed_coord.rev, ) if strategy == 'FAIL': raise self.DirectManagedVersionConflict( '{}\nThis raises an error due to the current --jar-dependency-management-conflict-strategy.' .format(message) ) is_silent = self.get_options().suppress_conflict_warnings log = logger.debug if is_silent else logger.warn if strategy == 'USE_DIRECT': log(message) log('[{}] Using direct version: {}'.format(strategy, direct_coord)) return direct_coord if strategy == 'USE_DIRECT_IF_FORCED': log(message) if force: log('[{}] Using direct version, because force=True: {}'.format(strategy, direct_coord)) return direct_coord else: log('[{}] Using managed version, because force=False: {}'.format(strategy, managed_coord)) return managed_coord if strategy == 'USE_MANAGED': log(message) log('[{}] Using managed version: {}'.format(strategy, managed_coord)) return managed_coord if strategy == 'USE_NEWER': newer = max([managed_coord, direct_coord], key=lambda coord: Revision.lenient(coord.rev)) log(message) log('[{}] Using newer version: {}'.format(strategy, newer)) return newer raise TaskError('Unknown value for --conflict-strategy: {}'.format(strategy))
def test_m2_coordinate_artifact_path_no_rev(self): coordinate = M2Coordinate("org.example", "lib") self.assertEqual("org.example-lib.jar", coordinate.artifact_filename)