def _resolve_conflict(self, existing, proposed): if proposed == existing: return existing elif existing.force and proposed.force: raise TaskError( 'Cannot force %s#%s to both rev %s and %s' % (proposed.org, proposed.name, existing.rev, proposed.rev)) elif existing.force: self._log.debug( 'Ignoring rev %s for %s#%s already forced to %s' % (proposed.rev, proposed.org, proposed.name, existing.rev)) return existing elif proposed.force: self._log.debug( 'Forcing %s#%s from %s to %s' % (proposed.org, proposed.name, existing.rev, proposed.rev)) return proposed else: try: if Revision.lenient(proposed.rev) > Revision.lenient( existing.rev): self._log.debug('Upgrading %s#%s from rev %s to %s' % ( proposed.org, proposed.name, existing.rev, proposed.rev, )) return proposed else: return existing except Revision.BadRevision as e: raise TaskError('Failed to parse jar revision', e)
def _resolve_conflict(existing, proposed): if proposed == existing: if proposed.force: return proposed return existing elif existing.force and proposed.force: raise TaskError('Cannot force {}#{};{} to both rev {} and {}'.format( proposed.org, proposed.name, proposed.classifier or '', existing.rev, proposed.rev )) elif existing.force: logger.debug('Ignoring rev {} for {}#{};{} already forced to {}'.format( proposed.rev, proposed.org, proposed.name, proposed.classifier or '', existing.rev )) return existing elif proposed.force: logger.debug('Forcing {}#{};{} from {} to {}'.format( proposed.org, proposed.name, proposed.classifier or '', existing.rev, proposed.rev )) return proposed else: try: if Revision.lenient(proposed.rev) > Revision.lenient(existing.rev): logger.debug('Upgrading {}#{};{} from rev {} to {}'.format( proposed.org, proposed.name, proposed.classifier or '', existing.rev, proposed.rev, )) return proposed else: return existing except Revision.BadRevision as e: raise TaskError('Failed to parse jar revision', e)
def _resolve_conflict(cls, existing, proposed): if proposed == existing: if proposed.force: return proposed return existing elif existing.force and proposed.force: raise cls.IvyResolveConflictingDepsError('Cannot force {}#{};{} to both rev {} and {}'.format( proposed.org, proposed.name, proposed.classifier or '', existing.rev, proposed.rev )) elif existing.force: logger.debug('Ignoring rev {} for {}#{};{} already forced to {}'.format( proposed.rev, proposed.org, proposed.name, proposed.classifier or '', existing.rev )) return existing elif proposed.force: logger.debug('Forcing {}#{};{} from {} to {}'.format( proposed.org, proposed.name, proposed.classifier or '', existing.rev, proposed.rev )) return proposed else: if Revision.lenient(proposed.rev) > Revision.lenient(existing.rev): logger.debug('Upgrading {}#{};{} from rev {} to {}'.format( proposed.org, proposed.name, proposed.classifier or '', existing.rev, proposed.rev, )) return proposed else: return existing
def _resolve_conflict(cls, existing, proposed): if existing.rev is None: return proposed if proposed.rev is None: return existing if proposed == existing: if proposed.force: return proposed return existing elif existing.force and proposed.force: raise cls.IvyResolveConflictingDepsError('Cannot force {}#{};{} to both rev {} and {}'.format( proposed.org, proposed.name, proposed.classifier or '', existing.rev, proposed.rev )) elif existing.force: logger.debug('Ignoring rev {} for {}#{};{} already forced to {}'.format( proposed.rev, proposed.org, proposed.name, proposed.classifier or '', existing.rev )) return existing elif proposed.force: logger.debug('Forcing {}#{};{} from {} to {}'.format( proposed.org, proposed.name, proposed.classifier or '', existing.rev, proposed.rev )) return proposed else: if Revision.lenient(proposed.rev) > Revision.lenient(existing.rev): logger.debug('Upgrading {}#{};{} from rev {} to {}'.format( proposed.org, proposed.name, proposed.classifier or '', existing.rev, proposed.rev, )) return proposed else: return existing
def _resolve_conflict(self, existing, proposed): if proposed == existing: if proposed.force: return proposed return existing elif existing.force and proposed.force: raise TaskError('Cannot force %s#%s to both rev %s and %s' % ( proposed.org, proposed.name, existing.rev, proposed.rev )) elif existing.force: self._log.debug('Ignoring rev %s for %s#%s already forced to %s' % ( proposed.rev, proposed.org, proposed.name, existing.rev )) return existing elif proposed.force: self._log.debug('Forcing %s#%s from %s to %s' % ( proposed.org, proposed.name, existing.rev, proposed.rev )) return proposed else: try: if Revision.lenient(proposed.rev) > Revision.lenient(existing.rev): self._log.debug('Upgrading %s#%s from rev %s to %s' % ( proposed.org, proposed.name, existing.rev, proposed.rev, )) return proposed else: return existing except Revision.BadRevision as e: raise TaskError('Failed to parse jar revision', e)
def _get_deprecated_tense(self, deprecated_version, future_tense='Will be', past_tense='Was'): """Provides the grammatical tense for a given deprecated version vs the current version.""" return future_tense if (Revision.semver(deprecated_version) >= Revision.semver(VERSION)) else past_tense
def errorprone(self, target): runtime_classpaths = self.context.products.get_data('runtime_classpath') runtime_classpath = [jar for conf, jar in runtime_classpaths.get_for_targets(target.closure(bfs=True))] output_dir = os.path.join(self.workdir, target.id) safe_mkdir(output_dir) runtime_classpath.append(output_dir) # Try to run errorprone with the same java version as the target # The minimum JDK for errorprone is JDK 1.8 min_jdk_version = max(target.platform.target_level, Revision.lenient('1.8')) if min_jdk_version.components[0] == 1: max_jdk_version = Revision(min_jdk_version.components[0], min_jdk_version.components[1], '9999') else: max_jdk_version = Revision(min_jdk_version.components[0], '9999') self.set_distribution(minimum_version=min_jdk_version, maximum_version=max_jdk_version, jdk=True) jvm_options = self.get_options().jvm_options[:] if self.dist.version < Revision.lenient('9'): # For Java 8 we need to add the errorprone javac jar to the bootclasspath to # avoid the "java.lang.NoSuchFieldError: ANNOTATION_PROCESSOR_MODULE_PATH" error # See https://github.com/google/error-prone/issues/653 for more information jvm_options.extend(['-Xbootclasspath/p:{}'.format(self.tool_classpath('errorprone-javac')[0])]) args = [ '-d', output_dir, ] # Errorprone does not recognize source or target 10 yet if target.platform.source_level < Revision.lenient('10'): args.extend(['-source', str(target.platform.source_level)]) if target.platform.target_level < Revision.lenient('10'): args.extend(['-target', str(target.platform.target_level)]) errorprone_classpath_file = os.path.join(self.workdir, '{}.classpath'.format(os.path.basename(output_dir))) with open(errorprone_classpath_file, 'w') as f: f.write('-classpath ') f.write(':'.join(runtime_classpath)) args.append('@{}'.format(errorprone_classpath_file)) for opt in self.get_options().command_line_options: args.extend(safe_shlex_split(opt)) with argfile.safe_args(self.calculate_sources(target), self.get_options()) as batched_sources: args.extend(batched_sources) result = self.runjava(classpath=self.tool_classpath('errorprone'), main=self._ERRORPRONE_MAIN, jvm_options=jvm_options, args=args, workunit_name='errorprone', workunit_labels=[WorkUnitLabel.LINT]) self.context.log.debug('java {main} ... exited with result ({result})'.format( main=self._ERRORPRONE_MAIN, result=result)) return result
def _validate_supports_more_than_one_source(self): # Support for doing the right thing with multiple files landed in # https://issues.apache.org/jira/browse/THRIFT-3776; first available in 0.10.0 if self.get_options().multiple_files_per_target_override: return required_version = '0.10.0' if Revision.semver(self._thrift_version) < Revision.semver(required_version): raise TaskError('A single .thrift source file is supported per go_thrift_library with thrift ' 'version `{}`: upgrade to at least `{}` to support multiple files.'.format( self._thrift_version, required_version))
def _preferred_jvm_distribution_args(cls, platforms, strict=None, jdk=False): if not platforms: return {"jdk": jdk} if strict is False: # treat all the platforms as non-strict min_version = max(p.target_level for p in platforms) set_max_version = False else: # treat strict platforms as strict & ensure no non-strict directive is broken strict_target_levels = { p.target_level for p in platforms if p.strict } lenient_target_levels = { p.target_level for p in platforms if not p.strict } if len(strict_target_levels) == 0: min_version = max(lenient_target_levels) set_max_version = strict elif len(strict_target_levels) > 1: differing = ", ".join(str(t) for t in strict_target_levels) raise cls.IncompatiblePlatforms( f"Multiple strict platforms with differing target releases were found:" f" {differing}") else: if len(lenient_target_levels) == 0: min_version = next(iter(strict_target_levels)) set_max_version = True else: strict_level = next(iter(strict_target_levels)) non_strict_max = max(t for t in lenient_target_levels) if non_strict_max and non_strict_max > strict_level: raise cls.IncompatiblePlatforms( f"lenient platform with higher minimum version," f" {non_strict_max}, than strict requirement of" f" {strict_level}") min_version = strict_level set_max_version = True if len(min_version.components ) <= 2: # ensure at least three components. min_version = Revision(*(min_version.components + [0] * (3 - len(min_version.components)))) max_version = None if set_max_version: max_version = Revision(*(min_version.components[0:2] + [9999])) return { "minimum_version": min_version, "maximum_version": max_version, "jdk": jdk }
def test_pre_release(self): self.assertEqual(Revision.semver("1.2.3-pre1.release.1"), Revision.semver("1.2.3-pre1.release.1")) self.assertComponents(Revision.semver("1.2.3-pre1.release.1"), 1, 2, 3, "pre1", "release", 1, None) self.assertTrue(Revision.semver("1.2.3-pre1.release.1") < Revision.semver("1.2.3-pre2.release.1")) self.assertTrue(Revision.semver("1.2.3-pre1.release.2") < Revision.semver("1.2.3-pre1.release.10")) self.assertTrue(Revision.semver("1.2.3") < Revision.semver("1.2.3-pre2.release.1"))
def test_pre_release(self): self.assertEqual(Revision.semver('1.2.3-pre1.release.1'), Revision.semver('1.2.3-pre1.release.1')) self.assertComponents(Revision.semver('1.2.3-pre1.release.1'), 1, 2, 3, 'pre1', 'release', 1, None) self.assertTrue( Revision.semver('1.2.3-pre1.release.1') < Revision.semver('1.2.3-pre2.release.1')) self.assertTrue( Revision.semver('1.2.3-pre1.release.2') < Revision.semver('1.2.3-pre1.release.10')) self.assertTrue(Revision.semver('1.2.3') < Revision.semver('1.2.3-pre2.release.1'))
def console_output(self, targets): self._force_option_parsing() for scope, options in sorted( self.context.options.tracker.option_history_by_scope.items()): if not self._scope_filter(scope): continue for option, history in sorted(options.items()): if not self._option_filter(option): continue if not self._rank_filter(history.latest.rank): continue if self.get_options( ).only_overridden and not history.was_overridden: continue # Skip the option if it has already passed the deprecation period. if history.latest.deprecation_version and PANTS_SEMVER >= Revision.semver( history.latest.deprecation_version): continue if self.get_options().skip_inherited: parent_scope, parent_value = self._get_parent_scope_option( scope, option) if parent_scope is not None and parent_value == history.latest.value: continue yield '{} = {}'.format(self._format_scope(scope, option), self._format_record(history.latest)) if self.get_options().show_history: for line in self._show_history(history): yield line
def _parse_java_version(version): # Java version strings have been well defined since release 1.3.1 as defined here: # http://www.oracle.com/technetwork/java/javase/versioning-naming-139433.html # These version strings comply with semver except that the traditional pre-release semver # slot (the 4th) can be delimited by an _ in the case of update releases of the jdk. # We accomodate that difference here. return Revision.semver(version.replace('_', '-'))
def project_template(self): target_levels = {Revision.lenient(platform['target_level']) for platform in self.blob['jvm_platforms']['platforms'].values()} lang_level = max(target_levels) if target_levels else Revision(1, 8) configured_project = TemplateData( root_dir=get_buildroot(), outdir=self.output_directory, git_root=Git.detect_worktree(), modules=self.module_templates_by_filename.values(), java=TemplateData( encoding=self.java_encoding, maximum_heap_size=self.java_maximum_heap_size, jdk='{0}.{1}'.format(*lang_level.components[:2]), language_level='JDK_{0}_{1}'.format(*lang_level.components[:2]), ), resource_extensions=[], scala=None, checkstyle_classpath=';'.join([]), debug_port=self.debug_port, annotation_processing=self.annotation_processing_template, extra_components=[], junit_tests=self._junit_tests_config(), global_junit_vm_parameters=' '.join(self.global_junit_jvm_options), ) return configured_project
def preferred_jvm_distribution(self, platforms): """Returns a jvm Distribution with a version that should work for all the platforms.""" if not platforms: return DistributionLocator.cached() min_version = max(platform.target_level for platform in platforms) max_version = Revision(*(min_version.components + [9999])) if self._strict_jvm_version else None return DistributionLocator.cached(minimum_version=min_version, maximum_version=max_version)
def console_output(self, targets): self._force_option_parsing() for scope, options in sorted(self.context.options.tracker.option_history_by_scope.items()): if not self._scope_filter(scope): continue for option, history in sorted(options.items()): if not self._option_filter(option): continue if not self._rank_filter(history.latest.rank): continue if self.get_options().only_overridden and not history.was_overridden: continue # Skip the option if it has already passed the deprecation period. if history.latest.deprecation_version and PANTS_SEMVER >= Revision.semver( history.latest.deprecation_version): continue if self.get_options().skip_inherited: parent_scope, parent_value = self._get_parent_scope_option(scope, option) if parent_scope is not None and parent_value == history.latest.value: continue yield '{} = {}'.format(self._format_scope(scope, option), self._format_record(history.latest)) if self.get_options().show_history: for line in self._show_history(history): yield line
def git_version(): """Get a Version() based on installed command-line git's version""" process = subprocess.Popen(['git', '--version'], stdout=subprocess.PIPE) (stdout, stderr) = process.communicate() assert process.returncode == 0, "Failed to determine git version." # stdout is like 'git version 1.9.1.598.g9119e8b\n' We want '1.9.1.598' matches = re.search(r'\s(\d+(?:\.\d+)*)[\s\.]', stdout.decode('utf-8')) return Revision.lenient(matches.group(1))
def git_version(): """Get a Version() based on installed command-line git's version""" process = subprocess.Popen(['git', '--version'], stdout=subprocess.PIPE) (stdout, stderr) = process.communicate() assert process.returncode == 0, "Failed to determine git version." # stdout is like 'git version 1.9.1.598.g9119e8b\n' We want '1.9.1.598' matches = re.search(r'\s(\d+(?:\.\d+)*)[\s\.]', stdout) return Revision.lenient(matches.group(1))
def test_validate_version(self): with pytest.raises(Distribution.Error): with self.distribution(executables=self.exe('java', '1.7.0_25')) as jdk: Distribution(bin_path=jdk, minimum_version='1.7.0_45').validate() with self.distribution(executables=self.exe('java', '1.7.0_25')) as jdk: Distribution(bin_path=jdk, minimum_version='1.7.0_25').validate() Distribution(bin_path=jdk, minimum_version=Revision.semver('1.6.0')).validate()
def _check_deprecated(self, dest, kwargs): """Checks option for deprecation and issues a warning/error if necessary.""" deprecated_ver = kwargs.get('deprecated_version', None) if deprecated_ver is not None: msg = ( "Option '{dest}' in {scope} is deprecated and removed in version {removal_version}. {hint}" ).format(dest=dest, scope=self._scope_str(), removal_version=deprecated_ver, hint=kwargs.get('deprecated_hint', '')) if Revision.semver(VERSION) >= Revision.semver(deprecated_ver): # Once we've hit the deprecated_version, raise an error instead of warning. This allows for # more actionable options hinting to continue beyond the deprecation period until removal. raise DeprecatedOptionError(msg) else: # Out of range stacklevel to suppress printing src line. warnings.warn('*** {}'.format(msg), DeprecationWarning, stacklevel=9999)
def test_preferred_jvm_distributions(self): with temporary_dir() as strict_jdk_home: with temporary_dir() as non_strict_jdk_home: strict_cache_key = (Revision(1, 6), Revision(1, 6, 9999), False) non_strict_cache_key = (Revision(1, 6), None, False) DistributionLocator._CACHE[strict_cache_key] = Distribution( home_path=strict_jdk_home) DistributionLocator._CACHE[ non_strict_cache_key] = Distribution( home_path=non_strict_jdk_home) self.assertEqual( { 'strict': strict_jdk_home, 'non_strict': non_strict_jdk_home }, self.execute_export_json()['preferred_jvm_distributions'] ['java6'])
def git_version() -> Revision: """Get a Version() based on installed command-line git's version""" stdout = subprocess.run( ['git', '--version'], stdout=subprocess.PIPE, encoding="utf-8", check=True ).stdout # stdout is like 'git version 1.9.1.598.g9119e8b\n' We want '1.9.1.598' matches = re.search(r'\s(\d+(?:\.\d+)*)[\s\.]', stdout) if matches is None: raise ValueError(f"Not able to parse git version from {stdout}.") return Revision.lenient(matches.group(1))
def test_pre_release(self): self.assertEqual( Revision.semver("1.2.3-pre1.release.1"), Revision.semver("1.2.3-pre1.release.1") ) self.assertComponents( Revision.semver("1.2.3-pre1.release.1"), 1, 2, 3, "pre1", "release", 1, None ) self.assertTrue( Revision.semver("1.2.3-pre1.release.1") < Revision.semver("1.2.3-pre2.release.1") ) self.assertTrue( Revision.semver("1.2.3-pre1.release.2") < Revision.semver("1.2.3-pre1.release.10") ) self.assertTrue(Revision.semver("1.2.3") < Revision.semver("1.2.3-pre2.release.1"))
def test_java_version_aliases(self): expected = {} for version in (6, 7, 8): expected[Revision.lenient('1.{}'.format(version))] = { self._java('j1{}'.format(version), '1.{}'.format(version)), self._java('j{}'.format(version), '{}'.format(version)), } partition = self._partition(list(reduce(set.union, expected.values(), set())), platforms=self._platforms('6', '7', '8', '1.6', '1.7', '1.8')) self.assertEqual(len(expected), len(partition)) self.assert_partitions_equal(expected, partition)
def _parse_java_version(name, version): # Java version strings have been well defined since release 1.3.1 as defined here: # http://www.oracle.com/technetwork/java/javase/versioning-naming-139433.html # These version strings comply with semver except that the traditional pre-release semver # slot (the 4th) can be delimited by an _ in the case of update releases of the jdk. # We accommodate that difference here. if isinstance(version, string_types): version = Revision.semver(version.replace('_', '-')) if version and not isinstance(version, Revision): raise ValueError('%s must be a string or a Revision object, given: %s' % (name, version)) return version
def _parse_java_version(name, version): # Java version strings have been well defined since release 1.3.1 as defined here: # http://www.oracle.com/technetwork/java/javase/versioning-naming-139433.html # These version strings comply with semver except that the traditional pre-release semver # slot (the 4th) can be delimited by an _ in the case of update releases of the jdk. # We accomodate that difference here. if isinstance(version, Compatibility.string): version = Revision.semver(version.replace('_', '-')) if version and not isinstance(version, Revision): raise ValueError('%s must be a string or a Revision object, given: %s' % (name, version)) return version
def test_validate_version(self): with pytest.raises(Distribution.Error): with self.distribution(executables=self.exe("java", "1.7.0_25")) as jdk: Distribution(bin_path=jdk, minimum_version="1.7.0_45").validate() with pytest.raises(Distribution.Error): with self.distribution(executables=self.exe("java", "1.8.0_1")) as jdk: Distribution(bin_path=jdk, maximum_version="1.7.9999").validate() with self.distribution(executables=self.exe("java", "1.7.0_25")) as jdk: Distribution(bin_path=jdk, minimum_version="1.7.0_25").validate() Distribution(bin_path=jdk, minimum_version=Revision.semver("1.6.0")).validate() Distribution(bin_path=jdk, minimum_version="1.7.0_25", maximum_version="1.7.999").validate()
def parse_java_version(cls, version): """Parses the java version (given a string or Revision object). Handles java version-isms, converting things like '7' -> '1.7' appropriately. Truncates input versions down to just the major and minor numbers (eg, 1.6), ignoring extra versioning information after the second number. :param version: the input version, given as a string or Revision object. :return: the parsed and cleaned version, suitable as a javac -source or -target argument. :rtype: Revision """ conversion = {str(i): '1.{}'.format(i) for i in cls.SUPPORTED_CONVERSION_VERSIONS} if str(version) in conversion: return Revision.lenient(conversion[str(version)]) if not hasattr(version, 'components'): version = Revision.lenient(version) if len(version.components) <= 2: return version return Revision(*version.components[:2])
def test_pre_release(self): assert Revision.semver("1.2.3-pre1.release.1") == Revision.semver("1.2.3-pre1.release.1") assert Revision.semver("1.2.3-pre1.release.1").components == [ 1, 2, 3, "pre1", "release", 1, None, ] assert Revision.semver("1.2.3-pre1.release.1") < Revision.semver("1.2.3-pre2.release.1") assert Revision.semver("1.2.3-pre1.release.2") < Revision.semver("1.2.3-pre1.release.10") assert Revision.semver("1.2.3") < Revision.semver("1.2.3-pre2.release.1")
def test_java_version_aliases(self): expected = {} for version in (6, 7, 8): expected[Revision.lenient("1.{}".format(version))] = { self._java("j1{}".format(version), "1.{}".format(version)), self._java("j{}".format(version), "{}".format(version)), } partition = self._partition( list(reduce(set.union, expected.values(), set())), platforms=self._platforms("6", "7", "8", "1.6", "1.7", "1.8"), ) self.assertEqual(len(expected), len(partition)) self.assert_partitions_equal(expected, partition)
def execute(self): indexable_targets = IndexableJavaTargets.global_instance().get( self.context) targets_to_zinc_args = self.context.products.get_data('zinc_args') with self.invalidated( indexable_targets, invalidate_dependents=True) as invalidation_check: extractor_cp = self.tool_classpath('kythe-java-extractor') for vt in invalidation_check.invalid_vts: self.context.log.info('Kythe extracting from {}\n'.format( vt.target.address.spec)) javac_args = self._get_javac_args_from_zinc_args( targets_to_zinc_args[vt.target]) jvm_options = [] if self.dist.version < Revision.lenient('9'): # When run on JDK8, Kythe requires javac9 on the bootclasspath. javac9_cp = self.tool_classpath('javac9') jvm_options.append('-Xbootclasspath/p:{}'.format( ':'.join(javac9_cp))) jvm_options.extend(self.get_options().jvm_options) jvm_options.extend([ '-DKYTHE_CORPUS={}'.format(vt.target.address.spec), '-DKYTHE_ROOT_DIRECTORY={}'.format(vt.target.target_base), '-DKYTHE_OUTPUT_DIRECTORY={}'.format(vt.results_dir) ]) result = self.dist.execute_java( classpath=extractor_cp, main=self._KYTHE_JAVA_EXTRACTOR_MAIN, jvm_options=jvm_options, args=javac_args, create_synthetic_jar=False, workunit_factory=self.context.new_workunit, workunit_name='kythe-extract') if result != 0: raise TaskError( 'java {main} ... exited non-zero ({result})'.format( main=self._KYTHE_JAVA_EXTRACTOR_MAIN, result=result)) for vt in invalidation_check.all_vts: created_files = os.listdir(vt.results_dir) if len(created_files) != 1: raise TaskError( 'Expected a single .kzip file in {}. Got: {}.'.format( vt.results_dir, ', '.join(created_files) if created_files else 'none')) kzip_files = self.context.products.get_data('kzip_files', dict) kzip_files[vt.target] = os.path.join(vt.results_dir, created_files[0])
def _parse_java_version(name, version): # Java version strings have been well defined since release 1.3.1 as defined here: # http://www.oracle.com/technetwork/java/javase/versioning-naming-139433.html # These version strings comply with semver except that the traditional pre-release semver # slot (the 4th) can be delimited by an _ in the case of update releases of the jdk. # We accommodate that difference here using lenient parsing. # We also accommodate specification versions, which just have major and minor # components; eg: `1.8`. These are useful when specifying constraints a distribution must # satisfy; eg: to pick any 1.8 java distribution: '1.8' <= version <= '1.8.99' if isinstance(version, string_types): version = Revision.lenient(version) if version and not isinstance(version, Revision): raise ValueError('{} must be a string or a Revision object, given: {}'.format(name, version)) return version
def _run_tests(self, tests_to_targets, main, extra_jvm_options=None, classpath_prepend=(), classpath_append=()): extra_jvm_options = extra_jvm_options or [] tests_by_properties = self._tests_by_properties(tests_to_targets, self._infer_workdir, lambda target: target.test_platform) result = 0 for (workdir, platform), tests in tests_by_properties.items(): for batch in self._partition(tests): # Batches of test classes will likely exist within the same targets: dedupe them. relevant_targets = set(map(tests_to_targets.get, batch)) classpath = self._task_exports.classpath(relevant_targets, cp=self._task_exports.tool_classpath('junit')) complete_classpath = OrderedSet() complete_classpath.update(classpath_prepend) complete_classpath.update(classpath) complete_classpath.update(classpath_append) if self._strict_jvm_version: max_version = Revision(*(platform.target_level.components + [9999])) distribution = Distribution.cached(minimum_version=platform.target_level, maximum_version=max_version) else: distribution = Distribution.cached(minimum_version=platform.target_level) with binary_util.safe_args(batch, self._task_exports.task_options) as batch_tests: self._context.log.debug('CWD = {}'.format(workdir)) self._context.log.debug('platform = {}'.format(platform)) result += abs(execute_java( classpath=complete_classpath, main=main, jvm_options=self._task_exports.jvm_options + extra_jvm_options, args=self._args + batch_tests + [u'-xmlreport'], workunit_factory=self._context.new_workunit, workunit_name='run', workunit_labels=[WorkUnitLabel.TEST], cwd=workdir, distribution=distribution, )) if result != 0 and self._fail_fast: break if result != 0: failed_targets = self._get_failed_targets(tests_to_targets) raise TestFailedTaskError( 'java {0} ... exited non-zero ({1}); {2} failed targets.' .format(main, result, len(failed_targets)), failed_targets=failed_targets )
def test_java_version_aliases(self): # NB: This feature is only supported for Java 6-8. Java 9+ must be referred to, for example, # as `9`, not `1.9`. expected = {} for version in (6, 7, 8): expected[Revision.lenient(f'1.{version}')] = { self._java(f'j1{version}', f'1.{version}'), self._java(f'j{version}', f'{version}'), } partition = self._partition( list(reduce(set.union, list(expected.values()), set())), platforms=self._platforms('6', '7', '8', '1.6', '1.7', '1.8')) self.assertEqual(len(expected), len(partition)) self.assert_partitions_equal(expected, partition)
def test_validate_version(self): with distribution(executables=EXE('bin/java', '1.7.0_25')) as dist_root: with self.assertRaises(Distribution.Error): Distribution(bin_path=os.path.join(dist_root, 'bin'), minimum_version='1.7.0_45').validate() with distribution(executables=EXE('bin/java', '1.8.0_1')) as dist_root: with self.assertRaises(Distribution.Error): Distribution(bin_path=os.path.join(dist_root, 'bin'), maximum_version='1.8').validate() with distribution(executables=EXE('bin/java', '1.7.0_25')) as dist_root: Distribution(bin_path=os.path.join(dist_root, 'bin'), minimum_version='1.7.0_25').validate() Distribution(bin_path=os.path.join(dist_root, 'bin'), minimum_version=Revision.lenient('1.6')).validate() Distribution(bin_path=os.path.join(dist_root, 'bin'), minimum_version='1.7.0_25', maximum_version='1.7.999').validate()
def wire_compiler_version(self): wire_compiler_jars = set() classpath_spec = self.get_options().wire_compiler for target in self.context.resolve(classpath_spec): if isinstance(target, JarLibrary): wire_compiler_jars.update(jar for jar in target.jar_dependencies if self.is_wire_compiler_jar(jar)) if len(wire_compiler_jars) != 1: msg = ('Expected to find exactly 1 wire-compiler jar in --wire-compiler classpath rooted ' 'at {}, but found {}' .format(classpath_spec, ', '.join(map(str, wire_compiler_jars)) if wire_compiler_jars else 0)) raise self.WireCompilerVersionError(msg) wire_compiler_jar = wire_compiler_jars.pop() wire_compiler_version = wire_compiler_jar.rev return Revision.lenient(wire_compiler_version)
def preferred_jvm_distribution(cls, platforms, strict=False): """Returns a jvm Distribution with a version that should work for all the platforms. Any one of those distributions whose version is >= all requested platforms' versions can be returned unless strict flag is set. :param iterable platforms: An iterable of platform settings. :param bool strict: If true, only distribution whose version matches the minimum required version can be returned, i.e, the max target_level of all the requested platforms. :returns: Distribution one of the selected distributions. """ if not platforms: return DistributionLocator.cached() min_version = max(platform.target_level for platform in platforms) max_version = Revision(*(min_version.components + [9999])) if strict else None return DistributionLocator.cached(minimum_version=min_version, maximum_version=max_version)
def validate_removal_semver(removal_version): """Validates that removal_version is a valid semver. If so, returns that semver. Raises an error otherwise. :param str removal_version: The pantsbuild.pants version which will remove the deprecated entity. :rtype: `pants.base.Revision` :raises DeprecationApplicationError: if the removal_version parameter is invalid. """ if removal_version is None: raise MissingRemovalVersionError('The removal version must be provided.') if not isinstance(removal_version, six.string_types): raise BadRemovalVersionError('The removal_version must be a semver version string.') try: return Revision.semver(removal_version) except Revision.BadRevision as e: raise BadRemovalVersionError('The given removal version {} is not a valid semver: ' '{}'.format(removal_version, e))
def wire_compiler_version(self): wire_compiler_jars = set() classpath_spec = self.get_options().wire_compiler for target in self.context.resolve(classpath_spec): if isinstance(target, JarLibrary): wire_compiler_jars.update(jar for jar in target.jar_dependencies if self.is_wire_compiler_jar(jar)) if len(wire_compiler_jars) != 1: msg = ( 'Expected to find exactly 1 wire-compiler jar in --wire-compiler classpath rooted ' 'at {}, but found {}'.format( classpath_spec, ', '.join(map(str, wire_compiler_jars)) if wire_compiler_jars else 0)) raise self.WireCompilerVersionError(msg) wire_compiler_jar = wire_compiler_jars.pop() wire_compiler_version = wire_compiler_jar.rev return Revision.lenient(wire_compiler_version)
def check_deprecated_semver(removal_version): """Check to see if the removal version is < the current Pants version. :param str removal_version: The pantsbuild.pants version which will remove the deprecated function. :raises DeprecationApplicationError if the removal_version parameter is invalid or the version is not an earlier version than the current release version. """ if not isinstance(removal_version, six.string_types): raise BadRemovalVersionError('The removal_version must be a semver version string.') try: removal_semver = Revision.semver(removal_version) except Revision.BadRevision as e: raise BadRemovalVersionError('The given removal version {} is not a valid semver: ' '{}'.format(removal_version, e)) if removal_semver <= _PANTS_SEMVER: raise PastRemovalVersionError('The removal version must be greater than the current pants ' 'version of {} - given {}'.format(VERSION, removal_version))
def test_wire_compiler_version_robust(self): # Here the wire compiler is both indirected, and not 1st in the classpath order. guava = self.make_target( '3rdparty:guava', JarLibrary, jars=[JarDependency('com.google.guava', 'guava', '18.0')]) wire = self.make_target( '3rdparty:wire', JarLibrary, jars=[ JarDependency('com.squareup.wire', 'wire-compiler', '3.0.0').exclude('com.google.guava', 'guava') ]) alias = self.make_target('a/random/long/address:spec', Target, dependencies=[guava, wire]) self.set_options(wire_compiler='a/random/long/address:spec') task = self.create_task(self.context(target_roots=[alias])) self.assertEqual(Revision(3, 0, 0), task.wire_compiler_version)
def execute(self): indexable_targets = IndexableJavaTargets.global_instance().get(self.context) with self.invalidated(indexable_targets, invalidate_dependents=True) as invalidation_check: if invalidation_check.invalid_vts: indexer_cp = self.tool_classpath('kythe-java-indexer') jvm_options = [] if self.dist.version < Revision.lenient('9'): # When run on JDK8, Kythe requires javac9 on the bootclasspath. javac9_cp = self.tool_classpath('javac9') jvm_options.append('-Xbootclasspath/p:{}'.format(':'.join(javac9_cp))) jvm_options.extend(self.get_options().jvm_options) for vt in invalidation_check.invalid_vts: self._index(vt, indexer_cp, jvm_options) for vt in invalidation_check.all_vts: entries = self._entries_file(vt) self.context.products.get_data('kythe_entries_files', dict)[vt.target] = entries
def execute(self): indexable_targets = IndexableJavaTargets.global_instance().get(self.context) with self.invalidated(indexable_targets, invalidate_dependents=True) as invalidation_check: if invalidation_check.invalid_vts: indexer_cp = self.tool_classpath("kythe-java-indexer") jvm_options = [] if self.dist.version < Revision.lenient("9"): # When run on JDK8, Kythe requires javac9 on the bootclasspath. javac9_cp = self.tool_classpath("javac9") jvm_options.append("-Xbootclasspath/p:{}".format(":".join(javac9_cp))) jvm_options.extend(self.get_options().jvm_options) for vt in invalidation_check.invalid_vts: self._index(vt, indexer_cp, jvm_options) for vt in invalidation_check.all_vts: entries = self._entries_file(vt) self.context.products.get_data("kythe_entries_files", dict)[vt.target] = entries
def check_deprecated_semver(removal_version, check_expired=True): """Check to see if the removal version is < the current Pants version. :param str removal_version: The pantsbuild.pants version which will remove the deprecated function. :raises DeprecationApplicationError if the removal_version parameter is invalid or the version is not an earlier version than the current release version. """ if not isinstance(removal_version, six.string_types): raise BadRemovalVersionError('The removal_version must be a semver version string.') try: removal_semver = Revision.semver(removal_version) except Revision.BadRevision as e: raise BadRemovalVersionError('The given removal version {} is not a valid semver: ' '{}'.format(removal_version, e)) if check_expired and removal_semver <= _PANTS_SEMVER: raise PastRemovalVersionError('The removal version must be greater than the current pants ' 'version of {} - given {}'.format(VERSION, removal_version))
def console_output(self, targets): self._force_option_parsing() if self.is_json(): output_map = {} for scope, options in sorted(self.context.options.tracker.option_history_by_scope.items()): if not self._scope_filter(scope): continue for option, history in sorted(options.items()): if not self._option_filter(option): continue if not self._rank_filter(history.latest.rank): continue if self.get_options().only_overridden and not history.was_overridden: continue # Skip the option if it has already passed the deprecation period. if history.latest.deprecation_version and PANTS_SEMVER >= Revision.semver( history.latest.deprecation_version): continue if self.get_options().skip_inherited: parent_scope, parent_value = self._get_parent_scope_option(scope, option) if parent_scope is not None and parent_value == history.latest.value: continue if self.is_json(): opt_vals = self._format_record(history.latest) scope_key = self._format_scope(scope, option, True) inner_map = dict(value=opt_vals[0], source=opt_vals[1]) output_map[scope_key] = inner_map elif self.is_text(): yield '{} = {}'.format(self._format_scope(scope, option), self._format_record(history.latest)) if self.get_options().show_history: history_list = [] for line in self._show_history(history): if self.is_text(): yield line elif self.is_json(): history_list.append(line.strip()) if self.is_json(): inner_map["history"] = history_list if self.is_json(): yield json.dumps(output_map, indent=2, sort_keys=True)
def execute(self): indexable_targets = IndexableJavaTargets.global_instance().get(self.context) targets_to_zinc_args = self.context.products.get_data('zinc_args') with self.invalidated(indexable_targets, invalidate_dependents=True) as invalidation_check: extractor_cp = self.tool_classpath('kythe-java-extractor') for vt in invalidation_check.invalid_vts: self.context.log.info('Kythe extracting from {}\n'.format(vt.target.address.spec)) javac_args = self._get_javac_args_from_zinc_args(targets_to_zinc_args[vt.target]) jvm_options = [] if self.dist.version < Revision.lenient('9'): # When run on JDK8, Kythe requires javac9 on the bootclasspath. javac9_cp = self.tool_classpath('javac9') jvm_options.append('-Xbootclasspath/p:{}'.format(':'.join(javac9_cp))) jvm_options.extend(self.get_options().jvm_options) jvm_options.extend([ '-DKYTHE_CORPUS={}'.format(vt.target.address.spec), '-DKYTHE_ROOT_DIRECTORY={}'.format(vt.target.target_base), '-DKYTHE_OUTPUT_DIRECTORY={}'.format(vt.results_dir) ]) result = self.dist.execute_java( classpath=extractor_cp, main=self._KYTHE_JAVA_EXTRACTOR_MAIN, jvm_options=jvm_options, args=javac_args, create_synthetic_jar=False, workunit_factory=self.context.new_workunit, workunit_name='kythe-extract') if result != 0: raise TaskError('java {main} ... exited non-zero ({result})'.format( main=self._KYTHE_JAVA_EXTRACTOR_MAIN, result=result)) for vt in invalidation_check.all_vts: created_files = os.listdir(vt.results_dir) if len(created_files) != 1: raise TaskError('Expected a single .kzip file in {}. Got: {}.'.format( vt.results_dir, ', '.join(created_files) if created_files else 'none')) kzip_files = self.context.products.get_data('kzip_files', dict) kzip_files[vt.target] = os.path.join(vt.results_dir, created_files[0])
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))
# coding=utf-8 # Copyright 2014 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) from pants.base.revision import Revision VERSION = '1.1.0-pre6' PANTS_SEMVER = Revision.semver(VERSION)
def get_deprecated_tense(removal_version, future_tense='will be', past_tense='was'): """Provides the grammatical tense for a given deprecated version vs the current version.""" return future_tense if (Revision.semver(removal_version) >= PANTS_SEMVER) else past_tense
# Copyright 2016 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 re from contextlib import contextmanager from pants.base.revision import Revision from pants.scm.git import Git from pants.util.contextutil import environment_as, temporary_dir from pants.util.process_handler import subprocess MIN_REQUIRED_GIT_VERSION = Revision.semver('1.7.10') def git_version(): """Get a Version() based on installed command-line git's version""" process = subprocess.Popen(['git', '--version'], stdout=subprocess.PIPE) (stdout, stderr) = process.communicate() assert process.returncode == 0, "Failed to determine git version." # stdout is like 'git version 1.9.1.598.g9119e8b\n' We want '1.9.1.598' matches = re.search(r'\s(\d+(?:\.\d+)*)[\s\.]', stdout) return Revision.lenient(matches.group(1)) def get_repo_root(): """Return the absolute path to the root directory of the Pants git repo.""" return subprocess.check_output(['git', 'rev-parse', '--show-toplevel']).strip()