Skip to content

Commit

Permalink
Use .jdk dir for hermetic execution (pantsbuild#6502)
Browse files Browse the repository at this point in the history
  • Loading branch information
wisechengyi authored and Stu Hood committed Sep 24, 2018
1 parent 7ad9f90 commit 4d475b3
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 14 deletions.
48 changes: 41 additions & 7 deletions src/python/pants/backend/jvm/subsystems/zinc.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import os
from builtins import object
from hashlib import sha1
from threading import Lock

from future.utils import text_type

Expand All @@ -18,10 +19,12 @@
from pants.backend.jvm.targets.scala_jar_dependency import ScalaJarDependency
from pants.backend.jvm.tasks.classpath_products import ClasspathEntry
from pants.backend.jvm.tasks.classpath_util import ClasspathUtil
from pants.backend.jvm.tasks.nailgun_task import NailgunTaskBase
from pants.base.build_environment import get_buildroot
from pants.base.workunit import WorkUnitLabel
from pants.engine.fs import DirectoryToMaterialize, PathGlobs, PathGlobsAndRoot
from pants.engine.isolated_process import ExecuteProcessRequest
from pants.java.distribution.distribution import Distribution
from pants.java.jar.jar_dependency import JarDependency
from pants.subsystem.subsystem import Subsystem
from pants.util.dirutil import fast_relpath, safe_mkdir
Expand All @@ -41,6 +44,8 @@ class Zinc(object):
ZINC_BOOTSTRAPPER_TOOL_NAME = 'zinc-bootstrapper'
ZINC_EXTRACTOR_TOOL_NAME = 'zinc-extractor'

_lock = Lock()

class Factory(Subsystem, JvmToolMixin):
options_scope = 'zinc'

Expand Down Expand Up @@ -168,19 +173,20 @@ def _scala_library(self, products):
def _scala_reflect(self, products):
return self._fetch_tool_jar_from_scalac_classpath(products, 'scala-reflect')

def create(self, products):
def create(self, products, execution_strategy):
"""Create a Zinc instance from products active in the current Pants run.
:param products: The active Pants run products to pluck classpaths from.
:type products: :class:`pants.goal.products.Products`
:returns: A Zinc instance with access to relevant Zinc compiler wrapper jars and classpaths.
:rtype: :class:`Zinc`
"""
return Zinc(self, products)
return Zinc(self, products, execution_strategy)

def __init__(self, zinc_factory, products):
def __init__(self, zinc_factory, products, execution_strategy):
self._zinc_factory = zinc_factory
self._products = products
self._execution_strategy = execution_strategy

@memoized_property
def zinc(self):
Expand All @@ -190,11 +196,38 @@ def zinc(self):
"""
return self._zinc_factory._zinc(self._products)

@property
@memoized_property
def dist(self):
"""Return the distribution selected for Zinc.
"""Return the `Distribution` selected for Zinc based on execution strategy.
:rtype: list of str
:rtype: pants.java.distribution.distribution.Distribution
"""
underlying_dist = self.underlying_dist
if self._execution_strategy != NailgunTaskBase.HERMETIC:
# symlink .pants.d/.jdk -> /some/java/home/
jdk_home_symlink = os.path.relpath(
os.path.join(self._zinc_factory.get_options().pants_workdir, '.jdk'),
get_buildroot())

# Since this code can be run in multi-threading mode due to multiple
# zinc workers, we need to make sure the file operations below is atomic.
with self._lock:
# Create the symlink if it does not exist
if not os.path.exists(jdk_home_symlink):
os.symlink(underlying_dist.home, jdk_home_symlink)
# Recreate if the symlink exists but does not match `underlying_dist.home`.
elif os.readlink(jdk_home_symlink) != underlying_dist.home:
os.remove(jdk_home_symlink)
os.symlink(underlying_dist.home, jdk_home_symlink)

return Distribution(home_path=jdk_home_symlink)
else:
return underlying_dist

@property
def underlying_dist(self):
"""
:rtype: pants.java.distribution.distribution.Distribution
"""
return self._zinc_factory.dist

Expand Down Expand Up @@ -286,7 +319,8 @@ def _run_bootstrapper(self, bridge_jar, context):
output_files=(self._relative_to_buildroot(bridge_jar),),
output_directories=(self._relative_to_buildroot(self._compiler_bridge_cache_dir),),
description='bootstrap compiler bridge.',
jdk_home=self.dist.home,
# Since this is always hermetic, we need to use `underlying_dist`
jdk_home=self.underlying_dist.home,
)
return context.execute_process_synchronously_or_raise(req, 'zinc-subsystem', [WorkUnitLabel.COMPILER])

Expand Down
2 changes: 1 addition & 1 deletion src/python/pants/backend/jvm/tasks/analysis_extraction.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def _create_products_if_should_run(self):

@memoized_property
def _zinc(self):
return Zinc.Factory.global_instance().create(self.context.products)
return Zinc.Factory.global_instance().create(self.context.products, self.get_options().execution_strategy)

def _summary_json_file(self, vt):
return os.path.join(vt.results_dir, 'summary.json')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ def cache_target_dirs(self):

@memoized_property
def _zinc(self):
return Zinc.Factory.global_instance().create(self.context.products)
return Zinc.Factory.global_instance().create(self.context.products, self.execution_strategy)

def _zinc_tool_classpath(self, toolname):
return self._zinc.tool_classpath_from_products(self.context.products,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ def cache_incremental(self):

@memoized_property
def _zinc(self):
return Zinc.Factory.global_instance().create(self.context.products)
return Zinc.Factory.global_instance().create(self.context.products, self.execution_strategy)

def __init__(self, *args, **kwargs):
super(BaseZincCompile, self).__init__(*args, **kwargs)
Expand Down Expand Up @@ -295,9 +295,6 @@ def compile(self, ctx, args, dependency_classpath, upstream_analysis,
if self.get_options().capture_classpath:
self._record_compile_classpath(absolute_classpath, ctx.target, ctx.classes_dir.path)

# TODO: Allow use of absolute classpath entries with hermetic execution,
# specifically by using .jdk dir for Distributions:
# https://github.com/pantsbuild/pants/issues/6430
self._verify_zinc_classpath(absolute_classpath, allow_dist=(self.execution_strategy != self.HERMETIC))
# TODO: Investigate upstream_analysis for hermetic compiles
self._verify_zinc_classpath(upstream_analysis.keys())
Expand Down Expand Up @@ -437,7 +434,8 @@ def relative_to_exec_root(path):
output_directories=(classes_dir,),
description="zinc compile for {}".format(ctx.target.address.spec),
# TODO: These should always be unicodes
jdk_home=text_type(self._zinc.dist.home),
# Since this is always hermetic, we need to use `underlying_dist`
jdk_home=text_type(self._zinc.underlying_dist.home),
)
res = self.context.execute_process_synchronously_without_raising(req, self.name(), [WorkUnitLabel.COMPILER])
# TODO: Materialize as a batch in do_compile or somewhere
Expand Down

0 comments on commit 4d475b3

Please sign in to comment.