diff --git a/3rdparty/BUILD b/3rdparty/BUILD index f84504dd476..107553cc58b 100644 --- a/3rdparty/BUILD +++ b/3rdparty/BUILD @@ -122,6 +122,11 @@ jar_library(name='easymock', jar('org.easymock', 'easymock', '3.3.1') ]) +jar_library(name='gson', + jars = [ + jar(org='com.google.code.gson', name='gson', rev='2.3.1') + ]) + jar_library(name='guava-testlib', jars=[ jar('com.google.guava', 'guava-testlib', '18.0') diff --git a/examples/src/java/org/pantsbuild/example/readme.md b/examples/src/java/org/pantsbuild/example/readme.md index b35d7614684..6506643994e 100644 --- a/examples/src/java/org/pantsbuild/example/readme.md +++ b/examples/src/java/org/pantsbuild/example/readme.md @@ -416,6 +416,70 @@ After building our `hello` example, if we check the binary jar's contents, there org/pantsbuild/example/hello/world.txt $ +Shading +------- + +Sometimes you have dependencies that have conflicting package or class names. This typically occurs +in the following scenario: Your jvm_binary depends on a 3rdparty library A (rev 1.0), and a 3rdparty +library B (rev 1.3). It turns out that A happens to also depend on B, but it depends on B (rev 2.0), +which is backwards-incompatible with rev 1.3. Now B (1.3) and B (2.0) define different versions of +the same classes, with the same fully-qualified class names, and you're pulling them all onto the +classpath for your project. + +This is where shading comes in: you can rename the fully-qualified names of the classes that +conflict, typically by applying a prefix (eg, `__shaded_by_pants__.org.foobar.example`). + +Pants uses jarjar for shading, and allows shading rules to be specified on `jvm_binary` targets with +the `shading_rules` argument. The `shading_rules` argument is a list of rules. Available rules +include: `shading_relocate`, +`shading_exclude`, +`shading_relocate_package`, and +`shading_exclude_package`. + +The order of rules in the list matters, as typical of shading +logic in general. + +These rules are powerful enough to take advantage of jarjar's more +advanced syntax, like using wildcards in the middle of package +names. E.g., this syntax works: + + :::python + # Destination pattern will be inferred to be + # __shaded_by_pants__.com.@1.foo.bar.@2 + shading_relocate('com.*.foo.bar.**') + +Which can also be done by: + + :::python + shading_relocate_package('com.*.foo.bar') + +The default shading prefix is `__shaded_by_pants__`, but you can change it: + + :::python + shading_relocate_package('com.foo.bar', shade_prefix='__my_prefix__.') + +You can rename a specific class: + + :::python + shading_relocate('com.example.foo.Main', 'org.example.bar.NotMain') + +If you want to shade everything in a package except a particular file (or subpackage), you can use +the `shading_exclude` rule. + + :::python + shading_exclude('com.example.foobar.Main') # Omit the Main class. + shading_exclude_package('com.example.foobar.api') # Omit the api subpackage. + shading_relocate_package('com.example.foobar') + +Again, order matters here: excludes have to appear __first__. + +To see an example, take a look at `testprojects/src/java/org/pantsbuild/testproject/shading/BUILD`, +and try running + + :::bash + ./pants binary testprojects/src/java/org/pantsbuild/testproject/shading + jar -tf dist/shading.jar + Further Reading --------------- diff --git a/src/python/pants/backend/jvm/BUILD b/src/python/pants/backend/jvm/BUILD index 273f44b2209..16e0de570f1 100644 --- a/src/python/pants/backend/jvm/BUILD +++ b/src/python/pants/backend/jvm/BUILD @@ -30,6 +30,7 @@ python_library( 'src/python/pants/base:build_file_aliases', 'src/python/pants/goal', 'src/python/pants/goal:task_registrar', + 'src/python/pants/java/jar:shader', ], ) @@ -64,4 +65,4 @@ python_library( ':artifact', 'src/python/pants/base:validation', ], -) \ No newline at end of file +) diff --git a/src/python/pants/backend/jvm/register.py b/src/python/pants/backend/jvm/register.py index 8b15831ff55..6919d7c8d48 100644 --- a/src/python/pants/backend/jvm/register.py +++ b/src/python/pants/backend/jvm/register.py @@ -52,6 +52,7 @@ from pants.base.build_file_aliases import BuildFileAliases from pants.goal.goal import Goal from pants.goal.task_registrar import TaskRegistrar as task +from pants.java.jar.shader import Shading def build_file_aliases(): @@ -86,6 +87,10 @@ def build_file_aliases(): 'jar_rules': JarRules, 'Repository': Repository, 'Skip': Skip, + 'shading_relocate': Shading.Relocate.new, + 'shading_exclude': Shading.Exclude.new, + 'shading_relocate_package': Shading.RelocatePackage.new, + 'shading_exclude_package': Shading.ExcludePackage.new, }, context_aware_object_factories={ 'bundle': Bundle.factory, diff --git a/src/python/pants/backend/jvm/targets/jvm_binary.py b/src/python/pants/backend/jvm/targets/jvm_binary.py index 30078275cc3..99ed7ef80b5 100644 --- a/src/python/pants/backend/jvm/targets/jvm_binary.py +++ b/src/python/pants/backend/jvm/targets/jvm_binary.py @@ -291,6 +291,7 @@ def __init__(self, deploy_excludes=None, deploy_jar_rules=None, manifest_entries=None, + shading_rules=None, **kwargs): """ :param string main: The name of the ``main`` class, e.g., @@ -300,9 +301,6 @@ def __init__(self, ``'hello'``. (By default, uses ``name`` param) :param string source: Name of one ``.java`` or ``.scala`` file (a good place for a ``main``). - :param sources: Overridden by source. If you want more than one source - file, use a library and have the jvm_binary depend on that library. - :param resources: List of ``resource``\s to include in bundle. :param dependencies: Targets (probably ``java_library`` and ``scala_library`` targets) to "link" in. :type dependencies: list of target specs @@ -316,9 +314,10 @@ def __init__(self, deploy jar. :param manifest_entries: dict that specifies entries for `ManifestEntries <#manifest_entries>`_ for adding to MANIFEST.MF when packaging this binary. - :param configurations: Ivy configurations to resolve for this target. - This parameter is not intended for general use. - :type configurations: tuple of strings + :param list shading_rules: Optional list of shading rules to apply when building a shaded + (aka monolithic aka fat) binary jar. The order of the rules matters: the first rule which + matches a fully-qualified class name is used to shade it. See shading_relocate(), + shading_exclude(), shading_relocate_package(), and shading_exclude_package(). """ self.address = address # Set in case a TargetDefinitionException is thrown early if main and not isinstance(main, string_types): @@ -349,7 +348,8 @@ def __init__(self, 'deploy_jar_rules': FingerprintedField(deploy_jar_rules or JarRules.default()), 'manifest_entries': FingerprintedField(ManifestEntries(manifest_entries)), 'main': PrimitiveField(main), - }) + 'shading_rules': PrimitiveField(shading_rules or ()), + }) super(JvmBinary, self).__init__(name=name, address=address, @@ -369,6 +369,10 @@ def deploy_excludes(self): def deploy_jar_rules(self): return self.payload.deploy_jar_rules + @property + def shading_rules(self): + return self.payload.shading_rules + @property def main(self): return self.payload.main diff --git a/src/python/pants/backend/jvm/tasks/BUILD b/src/python/pants/backend/jvm/tasks/BUILD index 75d2741007e..0d8561c4533 100644 --- a/src/python/pants/backend/jvm/tasks/BUILD +++ b/src/python/pants/backend/jvm/tasks/BUILD @@ -70,12 +70,12 @@ python_library( 'src/python/pants/base:exceptions', 'src/python/pants/base:workunit', 'src/python/pants/ivy', - 'src/python/pants/java/distribution', 'src/python/pants/java:executor', 'src/python/pants/java:util', 'src/python/pants/java/jar:shader', 'src/python/pants/backend/jvm/subsystems:jvm_tool_mixin', 'src/python/pants/util:dirutil', + 'src/python/pants/util:memo', ], ) @@ -290,6 +290,11 @@ python_library( ':jar_task', '3rdparty/python/twitter/commons:twitter.common.collections', 'src/python/pants/backend/jvm/targets:jvm', + 'src/python/pants/base:exceptions', + 'src/python/pants/java/jar:shader', + 'src/python/pants/util:contextutil', + 'src/python/pants/util:fileutil', + 'src/python/pants/util:memo', ], ) diff --git a/src/python/pants/backend/jvm/tasks/bootstrap_jvm_tools.py b/src/python/pants/backend/jvm/tasks/bootstrap_jvm_tools.py index e3cdd664f33..ffccce67186 100644 --- a/src/python/pants/backend/jvm/tasks/bootstrap_jvm_tools.py +++ b/src/python/pants/backend/jvm/tasks/bootstrap_jvm_tools.py @@ -20,10 +20,10 @@ from pants.base.exceptions import TaskError from pants.ivy.ivy_subsystem import IvySubsystem from pants.java import util -from pants.java.distribution.distribution import DistributionLocator -from pants.java.executor import Executor, SubprocessExecutor +from pants.java.executor import Executor from pants.java.jar.shader import Shader from pants.util.dirutil import safe_mkdir_for +from pants.util.memo import memoized_property class ShadedToolFingerprintStrategy(IvyResolveFingerprintStrategy): @@ -75,11 +75,10 @@ def register_options(cls, register): super(BootstrapJvmTools, cls).register_options(register) register('--jvm-options', action='append', metavar='