Skip to content

Commit

Permalink
[jvm-compile] Copy compile classpath into runtime classpath even if a…
Browse files Browse the repository at this point in the history
…lready defined

In pantsbuild#4309, the problem was caused by jvm_compile assuming it is always first in the compile order. This ensures the compile_classpath is mixed into the runtime classpath even if it has already been initialized.

Another approach would be to move the NodeBuild task to later in the compile goal, and make it an error for jvm_compile to receive a runtime classpath.
  • Loading branch information
baroquebobcat committed Mar 6, 2017
1 parent 8bf2850 commit 421b759
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 2 deletions.
9 changes: 9 additions & 0 deletions src/python/pants/backend/jvm/tasks/classpath_products.py
Expand Up @@ -305,6 +305,15 @@ def get_internal_classpath_entries_for_targets(self, targets, respect_excludes=T
return [(conf, cp_entry) for conf, cp_entry in classpath_tuples
if ClasspathEntry.is_internal_classpath_entry(cp_entry)]

def update(self, other):
"""Adds the contents of other to this ClasspathProducts."""
if self._pants_workdir != other._pants_workdir:
raise ValueError('Other ClasspathProducts from a different pants workdir {}'.format(other._pants_workdir))
for target, products in other._classpaths._products_by_target.items():
self._classpaths.add_for_target(target, products)
for target, products in other._excludes._products_by_target.items():
self._excludes.add_for_target(target, products)

def _filter_by_excludes(self, classpath_target_tuples, root_targets):
# Excludes are always applied transitively, so regardless of whether a transitive
# set of targets was included here, their closure must be included.
Expand Down
13 changes: 11 additions & 2 deletions src/python/pants/backend/jvm/tasks/jvm_compile/jvm_compile.py
Expand Up @@ -440,8 +440,7 @@ def execute(self):
return

# Clone the compile_classpath to the runtime_classpath.
compile_classpath = self.context.products.get_data('compile_classpath')
classpath_product = self.context.products.get_data('runtime_classpath', compile_classpath.copy)
classpath_product = self.create_runtime_classpath()

def classpath_for_context(context):
if self.get_options().use_classpath_jars:
Expand Down Expand Up @@ -484,6 +483,16 @@ def classpath_for_context(context):
classpath_product.remove_for_target(cc.target, [(conf, cc.classes_dir)])
classpath_product.add_for_target(cc.target, [(conf, cc.jar_file)])

def create_runtime_classpath(self):
compile_classpath = self.context.products.get_data('compile_classpath')
classpath_product = self.context.products.get_data('runtime_classpath')
if not classpath_product:
classpath_product = self.context.products.get_data('runtime_classpath', compile_classpath.copy)
else:
classpath_product.update(compile_classpath)

return classpath_product

def do_compile(self,
invalidation_check,
compile_contexts,
Expand Down
11 changes: 11 additions & 0 deletions tests/python/pants_test/backend/jvm/tasks/jvm_compile/BUILD
Expand Up @@ -13,6 +13,17 @@ python_tests(
],
)

python_tests(
name = 'jvm_compile',
sources = ['test_jvm_compile.py'],
dependencies = [
'src/python/pants/backend/jvm/tasks:classpath_products',
'src/python/pants/backend/jvm/tasks/jvm_compile',
'tests/python/pants_test/tasks:task_test_base',
],
)


python_library(
name='base_compile_integration_test',
sources=['base_compile_integration_test.py'],
Expand Down
@@ -0,0 +1,49 @@
# coding=utf-8
# Copyright 2017 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).

from __future__ import (absolute_import, division, generators, nested_scopes, print_function,
unicode_literals, with_statement)

import os

from pants.backend.jvm.targets.java_library import JavaLibrary
from pants.backend.jvm.tasks.classpath_products import ClasspathProducts
from pants.backend.jvm.tasks.jvm_compile.jvm_compile import JvmCompile
from pants_test.tasks.task_test_base import TaskTestBase


class DummyJvmCompile(JvmCompile):

def create_analysis_tools(self):
return None


class JvmCompileTest(TaskTestBase):
DEFAULT_CONF = 'default'

@classmethod
def task_type(cls):
return DummyJvmCompile

def test_if_runtime_classpath_exists(self):
target = self.make_target(
'java/classpath:java_lib',
target_type=JavaLibrary,
sources=['com/foo/Bar.java'],
)

context = self.context(target_roots=[target])
compile_classpath = context.products.get_data('compile_classpath', ClasspathProducts.init_func(self.pants_workdir))

compile_entry = os.path.join(self.pants_workdir, 'compile-entry')
pre_init_runtime_entry = os.path.join(self.pants_workdir, 'pre-inited-runtime-entry')
compile_classpath.add_for_targets([target], [('default', compile_entry)])
runtime_classpath = context.products.get_data('runtime_classpath', ClasspathProducts.init_func(self.pants_workdir))

runtime_classpath.add_for_targets([target], [('default', pre_init_runtime_entry)])

task = self.create_task(context)
resulting_classpath = task.create_runtime_classpath()
self.assertEqual([('default', pre_init_runtime_entry), ('default', compile_entry)],
resulting_classpath.get_for_target(target))

1 comment on commit 421b759

@UnrememberMe
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me.

Please sign in to comment.