diff --git a/src/python/pants/CHANGELOG.rst b/src/python/pants/CHANGELOG.rst index 33772b76dbb..3ecd3144fda 100644 --- a/src/python/pants/CHANGELOG.rst +++ b/src/python/pants/CHANGELOG.rst @@ -1,9 +1,68 @@ RELEASE HISTORY =============== -0.0.34 (7/6/2015) +0.0.35 (7/10/2015) ------------------ +Release Notes +~~~~~~~~~~~~~ + +With this release, if you use the +`isolated jvm compile strategy `_, +java annotation processers that emit java sourcefiles or classfiles will be +handled correctly and the generated code will be bundled appropriately in jars. +In particular, this makes libraries like Google's AutoValue useable in a pants +build. See: `RB #2451 `_. + +API Changes +~~~~~~~~~~~ + +* Deprecate with_description. + `RB #2444 `_ + +Bugfixes +~~~~~~~~ + +* Fixup BuildFile must_exist logic. + `RB #2441 `_ + +* Upgrade to pex 1.0.1. + `Issue #1658 `_ + `RB #2438 `_ + +New Features +~~~~~~~~~~~~ + +* Add an option --main to the run.jvm task to override the specification of 'main' on a jvm_binary() target. + `RB #2442 `_ + +* Add jvm_options for thrift-linter. + `RB #2445 `_ + +* Added cwd argument to allow JavaTest targets to require particular working directories. + `RB #2440 `_ + +Small improvements, Refactoring and Tooling +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* Record all output classes for the jvm isolated compile strategy. + `RB #2451 `_ + +* Robustify the pants ivy configuration. + `Issue #1779 `_ + `RB #2450 `_ + +* Some refactoring of global options. + `RB #2446 `_ + +* Improved error messaging for unknown Target kwargs. + `RB #2443 `_ + +* Remove Nailgun specific classes from zinc, since pants invokes Main directly. + `RB #2439 `_ + +0.0.34 (7/6/2015) +----------------- Release Notes ~~~~~~~~~~~~~ @@ -19,7 +78,6 @@ for this new release. Download the pants source code and run: ./pants run migrations/options/src/python:migrate_config -- - API Changes ~~~~~~~~~~~ diff --git a/src/python/pants/backend/core/BUILD b/src/python/pants/backend/core/BUILD index 7ca4feb6b80..0f51afbc50d 100644 --- a/src/python/pants/backend/core/BUILD +++ b/src/python/pants/backend/core/BUILD @@ -25,7 +25,6 @@ python_library( '3rdparty/python:six', '3rdparty/python/twitter/commons:twitter.common.dirutil', 'src/python/pants/base:build_environment', - 'src/python/pants/base:deprecated', ], ) diff --git a/src/python/pants/backend/core/wrapped_globs.py b/src/python/pants/backend/core/wrapped_globs.py index f219206976e..37b056bd09a 100644 --- a/src/python/pants/backend/core/wrapped_globs.py +++ b/src/python/pants/backend/core/wrapped_globs.py @@ -6,13 +6,11 @@ unicode_literals, with_statement) import os -from copy import deepcopy from six import string_types from twitter.common.dirutil.fileset import Fileset from pants.base.build_environment import get_buildroot -from pants.base.deprecated import deprecated class FilesetWithSpec(object): @@ -31,33 +29,6 @@ def __iter__(self): def __getitem__(self, index): return self._result[index] - @deprecated(removal_version='0.0.35', - hint_message='Instead of globs(a) + globs(b), use globs(a, b)') - def __add__(self, other): - filespec = deepcopy(self.filespec) - if isinstance(other, FilesetWithSpec): - filespec['globs'] += other.filespec['globs'] - other_result = other._result - else: - filespec['globs'] += [os.path.join(self._rel_root, other_path) for other_path in other] - other_result = other - result = list(set(self._result).union(set(other_result))) - return FilesetWithSpec(self._rel_root, result, filespec) - - @deprecated(removal_version='0.0.35', - hint_message='Instead of glob arithmetic, use glob(..., exclude=[...])') - def __sub__(self, other): - filespec = deepcopy(self.filespec) - exclude = filespec.get('exclude', []) - if isinstance(other, FilesetWithSpec): - exclude.append(other.filespec) - other_result = other._result - else: - exclude.append({'globs' : [os.path.join(self._rel_root, other_path) for other_path in other]}) - other_result = other - filespec['exclude'] = exclude - result = list(set(self._result) - set(other_result)) - return FilesetWithSpec(self._rel_root, result, filespec) class FilesetRelPathWrapper(object): def __init__(self, parse_context): diff --git a/src/python/pants/backend/python/tasks/BUILD b/src/python/pants/backend/python/tasks/BUILD index 2888a94b3f2..c626f073c0e 100644 --- a/src/python/pants/backend/python/tasks/BUILD +++ b/src/python/pants/backend/python/tasks/BUILD @@ -23,7 +23,6 @@ python_library( 'src/python/pants/base:address_lookup_error', 'src/python/pants/base:build_environment', 'src/python/pants/base:build_graph', - 'src/python/pants/base:deprecated', 'src/python/pants/base:exceptions', 'src/python/pants/base:generator', 'src/python/pants/base:hash_utils', diff --git a/src/python/pants/backend/python/tasks/pytest_run.py b/src/python/pants/backend/python/tasks/pytest_run.py index 0bad167e378..b0c70c72da8 100644 --- a/src/python/pants/backend/python/tasks/pytest_run.py +++ b/src/python/pants/backend/python/tasks/pytest_run.py @@ -24,7 +24,6 @@ from pants.backend.python.python_setup import PythonRepos, PythonSetup from pants.backend.python.targets.python_tests import PythonTests from pants.backend.python.tasks.python_task import PythonTask -from pants.base.deprecated import deprecated from pants.base.exceptions import TaskError, TestFailedTaskError from pants.base.target import Target from pants.base.workunit import WorkUnit @@ -67,43 +66,6 @@ def failed_targets(self): return self._failed_targets -def deprecated_env_accessors(removal_version, **replacement_mapping): - """Generates accessors for legacy env ver/replacement option pairs. - - The generated accessors issue a deprecation warning when the deprecated env var is present and - enjoy the "compile" time removal forcing that normal @deprecated functions and methods do. - """ - def create_accessor(env_name, option_name): - @deprecated(removal_version=removal_version, - hint_message='Use the {option} option instead of the deprecated {env} environment ' - 'variable'.format(option=option_name, env=env_name)) - def deprecated_accessor(): - return os.environ.get(env_name) - - def accessor(self): - value = None - if env_name in os.environ: - value = deprecated_accessor() - sanitized_option_name = option_name.lstrip('-').replace('-', '_') - value = self.get_options()[sanitized_option_name] or value - return value - return accessor - - def decorator(clazz): - for env_name, option_name in replacement_mapping.items(): - setattr(clazz, 'get_DEPRECATED_{}'.format(env_name), create_accessor(env_name, option_name)) - return clazz - - return decorator - - -# TODO(John Sirois): Replace this helper and use of the accessors it generates with direct options -# access prior to releasing 0.0.35 -@deprecated_env_accessors(removal_version='0.0.35', - JUNIT_XML_BASE='--junit-xml-dir', - PANTS_PROFILE='--profile', - PANTS_PYTHON_TEST_FAILSOFT='--fail-slow', - PANTS_PY_COVERAGE='--coverage') class PytestRun(PythonTask): _TESTING_TARGETS = [ # Note: the requirement restrictions on pytest and pytest-cov match those in requirements.txt, @@ -175,8 +137,7 @@ def run_tests(self, targets, workunit): else: results = {} # Coverage often throws errors despite tests succeeding, so force failsoft in that case. - fail_hard = (not self.get_DEPRECATED_PANTS_PYTHON_TEST_FAILSOFT() and - not self.get_DEPRECATED_PANTS_PY_COVERAGE()) + fail_hard = not self.get_options().fail_slow and not self.get_options().coverage for target in targets: if isinstance(target, PythonTests): rv = self._do_run_tests([target], workunit) @@ -247,7 +208,7 @@ def pytest_collection_modifyitems(session, config, items): @contextmanager def _maybe_emit_junit_xml(self, targets): args = [] - xml_base = self.get_DEPRECATED_JUNIT_XML_BASE() + xml_base = self.get_options().junit_xml_dir if xml_base and targets: xml_base = os.path.realpath(xml_base) xml_path = os.path.join(xml_base, Target.maybe_readable_identify(targets) + '.xml') @@ -376,7 +337,7 @@ def is_python_lib(tgt): @contextmanager def _maybe_emit_coverage_data(self, targets, chroot, pex, workunit): - coverage = self.get_DEPRECATED_PANTS_PY_COVERAGE() + coverage = self.get_options().coverage if coverage is None: yield [] return @@ -468,7 +429,7 @@ def _do_run_tests_with_args(self, pex, workunit, args): env = { 'PYTHONUNBUFFERED': '1', } - profile = self.get_DEPRECATED_PANTS_PROFILE() + profile = self.get_options().profile if profile: env['PEX_PROFILE'] = '{0}.subprocess.{1:.6f}'.format(profile, time.time()) with environment_as(**env): diff --git a/src/python/pants/version.py b/src/python/pants/version.py index 30ac6f2443b..615b3e7ae35 100644 --- a/src/python/pants/version.py +++ b/src/python/pants/version.py @@ -6,4 +6,4 @@ unicode_literals, with_statement) -VERSION = '0.0.34' +VERSION = '0.0.35' diff --git a/tests/python/pants_test/backend/core/test_wrapped_globs.py b/tests/python/pants_test/backend/core/test_wrapped_globs.py index 261b70a6df7..5a4539a952c 100644 --- a/tests/python/pants_test/backend/core/test_wrapped_globs.py +++ b/tests/python/pants_test/backend/core/test_wrapped_globs.py @@ -170,88 +170,3 @@ def test_rglob_respects_follow_links_override(self): 'java_library(name="w", sources=rglobs("*.java", follow_links=False))') graph = self.context().scan(self.build_root) assert ['foo.java'] == list(graph.get_target_from_spec('z/w').sources_relative_to_source_root()) - - # Remove the following tests when operator support is dropped from globs - def test_globs_add_globs_added_to_spec(self): - self.add_to_build_file('y/BUILD', 'java_library(name="y",' - ' sources=globs("morx.java") + globs("fleem.java"))') - graph = self.context().scan(self.build_root) - globs = graph.get_target_from_spec('y').globs_relative_to_buildroot() - self.assertEquals({'globs': ['y/morx.java', 'y/fleem.java']}, - globs) - - def test_globs_add_list_added_to_spec(self): - self.add_to_build_file('y/BUILD', 'java_library(name="y",' - ' sources=globs("morx.java") + ["fleem.java"])') - graph = self.context().scan(self.build_root) - globs = graph.get_target_from_spec('y').globs_relative_to_buildroot() - self.assertEquals({'globs': ['y/morx.java', 'y/fleem.java']}, - globs) - - def test_rglob_add_operator_with_other_rglob(self): - self.add_to_build_file('y/BUILD', - 'java_library(name="y",' - ' sources=rglobs("fleem.java") + rglobs("morx.java"))' - ) - graph = self.context().scan(self.build_root) - self.assertEqual(['fleem.java','morx.java'], - list(graph.get_target_from_spec('y').sources_relative_to_source_root())) - - def test_rglob_add_operator_with_list(self): - self.add_to_build_file('y/BUILD', - 'java_library(name="y",' - ' sources=rglobs("fleem.java") + ["morx.java"])' - ) - graph = self.context().scan(self.build_root) - self.assertEqual(['fleem.java', 'morx.java'], - list(graph.get_target_from_spec('y').sources_relative_to_source_root())) - - def test_rglob_add_operator_with_overlapping_rglob_has_distinct_list(self): - self.add_to_build_file('y/BUILD', - 'java_library(name="y",' - ' sources=rglobs("*.java") + rglobs("*.java"))') - graph = self.context().scan(self.build_root) - self.assertEqual(['fleem.java', 'morx.java'], - list(graph.get_target_from_spec('y').sources_relative_to_source_root())) - - def test_globs_sub_globs_added_to_spec_exclude(self): - self.add_to_build_file('y/BUILD', 'java_library(name="y",' - ' sources=globs("*.java") - globs("fleem.java"))') - graph = self.context().scan(self.build_root) - globs = graph.get_target_from_spec('y').globs_relative_to_buildroot() - self.assertEquals({'globs': ['y/*.java'], - 'exclude': [{'globs': ['y/fleem.java']}]}, - globs) - - def test_glob_sub_list_added_to_spec_exclude(self): - self.add_to_build_file('y/BUILD', 'java_library(name="y",' - ' sources=globs("*.java") - ["fleem.java"])') - graph = self.context().scan(self.build_root) - globs = graph.get_target_from_spec('y').globs_relative_to_buildroot() - self.assertEquals({'globs': ['y/*.java'], - 'exclude': [{'globs': ['y/fleem.java']}]}, - globs) - - def test_rglob_sub_operator_with_other_rglob(self): - self.add_to_build_file('y/BUILD', - 'java_library(name="y",' - ' sources=rglobs("*.java") - rglobs("morx.java"))') - graph = self.context().scan(self.build_root) - self.assertEqual(['fleem.java'], - list(graph.get_target_from_spec('y').sources_relative_to_source_root())) - - def test_rglob_sub_operator_with_list(self): - self.add_to_build_file('y/BUILD', - 'java_library(name="y",' - ' sources=rglobs("*.java") - ["morx.java"])') - graph = self.context().scan(self.build_root) - self.assertEqual(['fleem.java'], - list(graph.get_target_from_spec('y').sources_relative_to_source_root())) - - def test_rglob_sub_operator_with_non_overlapping_rglob(self): - self.add_to_build_file('y/BUILD', - 'java_library(name="y",' - ' sources=rglobs("*.java") - rglobs("*.scala"))') - graph = self.context().scan(self.build_root) - self.assertEqual(['fleem.java', 'morx.java'], - list(graph.get_target_from_spec('y').sources_relative_to_source_root())) diff --git a/tests/python/pants_test/backend/python/tasks/test_pytest_run.py b/tests/python/pants_test/backend/python/tasks/test_pytest_run.py index 4b3ca28a244..582ae743f5f 100644 --- a/tests/python/pants_test/backend/python/tasks/test_pytest_run.py +++ b/tests/python/pants_test/backend/python/tasks/test_pytest_run.py @@ -14,7 +14,7 @@ from pants.backend.python.tasks.pytest_run import PytestRun from pants.base.exceptions import TestFailedTaskError -from pants.util.contextutil import environment_as, pushd +from pants.util.contextutil import pushd from pants_test.backend.python.tasks.python_task_test import PythonTaskTest @@ -158,7 +158,7 @@ def test_red(self): def test_mixed(self): self.run_failing_tests(targets=[self.green, self.red], failed_targets=[self.red]) - def assert_expected_junit_xml(self, report_basedir, **kwargs): + def test_junit_xml_option(self): # We expect xml of the following form: # # @@ -166,7 +166,9 @@ def assert_expected_junit_xml(self, report_basedir, **kwargs): # ... # # - self.run_failing_tests(targets=[self.green, self.red], failed_targets=[self.red], **kwargs) + report_basedir = os.path.join(self.build_root, 'dist', 'junit_option') + self.run_failing_tests(targets=[self.green, self.red], failed_targets=[self.red], + junit_xml_dir=report_basedir) files = glob.glob(os.path.join(report_basedir, '*.xml')) self.assertEqual(1, len(files), 'Expected 1 file, found: {}'.format(files)) @@ -184,15 +186,6 @@ def assert_expected_junit_xml(self, report_basedir, **kwargs): self.assertEqual(1, len(children_by_test_name['test_two'].childNodes)) self.assertEqual('failure', children_by_test_name['test_two'].firstChild.nodeName) - def test_junit_xml_option(self): - basedir = os.path.join(self.build_root, 'dist', 'junit_option') - self.assert_expected_junit_xml(basedir, junit_xml_dir=basedir) - - def test_junit_xml_env(self): - basedir = os.path.join(self.build_root, 'dist', 'junit_env') - with environment_as(JUNIT_XML_BASE=basedir): - self.assert_expected_junit_xml(basedir) - def coverage_data_file(self): return os.path.join(self.build_root, '.coverage') @@ -204,114 +197,84 @@ def load_coverage_data(self, path): _, all_statements, not_run_statements, _ = coverage_data.analysis(path) return all_statements, not_run_statements - def assert_expected_coverage(self, **kwargs): + def test_coverage_simple_option(self): + # TODO(John Sirois): Consider eliminating support for "simple" coverage or at least formalizing + # the coverage option value that turns this on to "1" or "all" or "simple" = anything formal. + simple_coverage_kwargs = {'coverage': '1'} + self.assertFalse(os.path.isfile(self.coverage_data_file())) covered_file = os.path.join(self.build_root, 'lib', 'core.py') - self.run_tests(targets=[self.green], **kwargs) + self.run_tests(targets=[self.green], **simple_coverage_kwargs) all_statements, not_run_statements = self.load_coverage_data(covered_file) self.assertEqual([1, 2, 5, 6], all_statements) self.assertEqual([6], not_run_statements) - self.run_failing_tests(targets=[self.red], failed_targets=[self.red], **kwargs) + self.run_failing_tests(targets=[self.red], failed_targets=[self.red], **simple_coverage_kwargs) all_statements, not_run_statements = self.load_coverage_data(covered_file) self.assertEqual([1, 2, 5, 6], all_statements) self.assertEqual([2], not_run_statements) - self.run_failing_tests(targets=[self.green, self.red], failed_targets=[self.red], **kwargs) + self.run_failing_tests(targets=[self.green, self.red], failed_targets=[self.red], + **simple_coverage_kwargs) all_statements, not_run_statements = self.load_coverage_data(covered_file) self.assertEqual([1, 2, 5, 6], all_statements) self.assertEqual([], not_run_statements) # The all target has no coverage attribute and the code under test does not follow the # auto-discover pattern so we should get no coverage. - self.run_failing_tests(targets=[self.all], failed_targets=[self.all], **kwargs) + self.run_failing_tests(targets=[self.all], failed_targets=[self.all], **simple_coverage_kwargs) all_statements, not_run_statements = self.load_coverage_data(covered_file) self.assertEqual([1, 2, 5, 6], all_statements) self.assertEqual([1, 2, 5, 6], not_run_statements) self.run_failing_tests(targets=[self.all_with_coverage], failed_targets=[self.all_with_coverage], - **kwargs) + **simple_coverage_kwargs) all_statements, not_run_statements = self.load_coverage_data(covered_file) self.assertEqual([1, 2, 5, 6], all_statements) self.assertEqual([], not_run_statements) - def test_coverage_simple_option(self): - # TODO(John Sirois): Consider eliminating support for "simple" coverage or at least formalizing - # the coverage option value that turns this on to "1" or "all" or "simple" = anything formal. - self.assert_expected_coverage(coverage='1') - - def test_coverage_simple_env(self): - with environment_as(PANTS_PY_COVERAGE='1'): - self.assert_expected_coverage() - - def assert_modules_dne(self, **kwargs): + def test_coverage_modules_dne_option(self): self.assertFalse(os.path.isfile(self.coverage_data_file())) covered_file = os.path.join(self.build_root, 'lib', 'core.py') # modules: should trump .coverage - self.run_failing_tests(targets=[self.green, self.red], failed_targets=[self.red], **kwargs) + self.run_failing_tests(targets=[self.green, self.red], failed_targets=[self.red], + coverage='modules:does_not_exist,nor_does_this') all_statements, not_run_statements = self.load_coverage_data(covered_file) self.assertEqual([1, 2, 5, 6], all_statements) self.assertEqual([1, 2, 5, 6], not_run_statements) - def test_coverage_modules_dne_option(self): - self.assert_modules_dne(coverage='modules:does_not_exist,nor_does_this') - - def test_coverage_modules_dne_env(self): - with environment_as(PANTS_PY_COVERAGE='modules:does_not_exist,nor_does_this'): - self.assert_modules_dne() - - def assert_modules(self, **kwargs): + def test_coverage_modules_option(self): self.assertFalse(os.path.isfile(self.coverage_data_file())) covered_file = os.path.join(self.build_root, 'lib', 'core.py') - self.run_failing_tests(targets=[self.all], failed_targets=[self.all], **kwargs) + self.run_failing_tests(targets=[self.all], failed_targets=[self.all], coverage='modules:core') all_statements, not_run_statements = self.load_coverage_data(covered_file) self.assertEqual([1, 2, 5, 6], all_statements) self.assertEqual([], not_run_statements) - def test_coverage_modules_option(self): - self.assert_modules(coverage='modules:core') - - def test_coverage_modules_env(self): - with environment_as(PANTS_PY_COVERAGE='modules:core'): - self.assert_modules() - - def assert_paths_dne(self, **kwargs): + def test_coverage_paths_dne_option(self): self.assertFalse(os.path.isfile(self.coverage_data_file())) covered_file = os.path.join(self.build_root, 'lib', 'core.py') # paths: should trump .coverage - self.run_failing_tests(targets=[self.green, self.red], failed_targets=[self.red], **kwargs) + self.run_failing_tests(targets=[self.green, self.red], failed_targets=[self.red], + coverage='paths:does_not_exist/,nor_does_this/') all_statements, not_run_statements = self.load_coverage_data(covered_file) self.assertEqual([1, 2, 5, 6], all_statements) self.assertEqual([1, 2, 5, 6], not_run_statements) - def test_coverage_paths_dne_option(self): - self.assert_paths_dne(coverage='paths:does_not_exist/,nor_does_this/') - - def test_coverage_paths_dne_env(self): - with environment_as(PANTS_PY_COVERAGE='paths:does_not_exist/,nor_does_this/'): - self.assert_paths_dne() - - def assert_paths(self, **kwargs): + def test_coverage_paths_option(self): self.assertFalse(os.path.isfile(self.coverage_data_file())) covered_file = os.path.join(self.build_root, 'lib', 'core.py') - self.run_failing_tests(targets=[self.all], failed_targets=[self.all], **kwargs) + self.run_failing_tests(targets=[self.all], failed_targets=[self.all], coverage='paths:core.py') all_statements, not_run_statements = self.load_coverage_data(covered_file) self.assertEqual([1, 2, 5, 6], all_statements) self.assertEqual([], not_run_statements) - def test_coverage_paths_option(self): - self.assert_paths(coverage='paths:core.py') - - def test_coverage_paths_env(self): - with environment_as(PANTS_PY_COVERAGE='paths:core.py'): - self.assert_paths() - def test_sharding(self): self.run_failing_tests(targets=[self.red, self.green], failed_targets=[self.red], shard='0/2') self.run_tests(targets=[self.red, self.green], shard='1/2')