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(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 _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 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 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 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_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(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_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 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 _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 _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 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 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 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): f"1.{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 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 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 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 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 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 findbugs(self, target): runtime_classpaths = self.context.products.get_data( 'runtime_classpath') runtime_classpath = runtime_classpaths.get_for_targets( target.closure(bfs=True)) aux_classpath = OrderedSet(jar for conf, jar in runtime_classpath if conf == 'default') target_jars = OrderedSet( jar for conf, jar in runtime_classpaths.get_for_target(target) if conf == 'default') bug_counts = {'error': 0, 'high': 0, 'normal': 0, 'low': 0} if not target_jars: self.context.log.info(' No jars to be analyzed') return bug_counts output_dir = os.path.join(self.workdir, target.id) safe_mkdir(output_dir) output_file = os.path.join(output_dir, 'findbugsXml.xml') aux_classpath_file = os.path.join( self.workdir, '{}.classpath'.format(os.path.basename(output_dir))) with open(aux_classpath_file, 'w') as f: f.write('\n'.join(aux_classpath - target_jars)) args = [ '-auxclasspathFromFile', aux_classpath_file, '-projectName', target.address.spec, '-xml:withMessages', '-effort:{}'.format(self.get_options().effort), '-{}'.format(self.get_options().threshold), '-nested:{}'.format( 'true' if self.get_options().nested else 'false'), '-output', output_file, '-noClassOk' ] if self.get_options().exclude_filter_file: args.extend([ '-exclude', os.path.join(get_buildroot(), self.get_options().exclude_filter_file) ]) if self.get_options().include_filter_file: args.extend([ '-include', os.path.join(get_buildroot(), self.get_options().include_filter_file) ]) if self.get_options().max_rank: args.extend(['-maxRank', str(self.get_options().max_rank)]) if self.get_options().relaxed: args.extend(['-relaxed']) if self.get_options().level == 'debug': args.extend(['-progress']) args.extend(target_jars) # Try to run spotbugs with the same java version as the target # The minimum JDK for spotbugs 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) result = self.runjava(classpath=self.tool_classpath('findbugs'), main=self._FINDBUGS_MAIN, jvm_options=self.get_options().jvm_options, args=args, workunit_name='findbugs', workunit_labels=[WorkUnitLabel.LINT]) if result != 0: raise TaskError( 'java {main} ... exited non-zero ({result})'.format( main=self._FINDBUGS_MAIN, result=result)) xml = XmlParser.from_file(output_file) for error in xml.parsed.getElementsByTagName('Error'): self.context.log.warn( 'Error: {msg}'.format(msg=error.getElementsByTagName( 'ErrorMessage')[0].firstChild.data)) bug_counts['error'] += 1 for bug_instance in xml.parsed.getElementsByTagName('BugInstance'): bug_rank = bug_instance.getAttribute('rank') if int(bug_rank) <= self._HIGH_PRIORITY_LOWEST_RANK: priority = 'high' elif int(bug_rank) <= self._NORMAL_PRIORITY_LOWEST_RANK: priority = 'normal' else: priority = 'low' bug_counts[priority] += 1 source_line = bug_instance.getElementsByTagName( 'Class')[0].getElementsByTagName('SourceLine')[0] self.context.log.warn( 'Bug[{priority}]: {type} {desc} {line}'.format( priority=priority, type=bug_instance.getAttribute('type'), desc=bug_instance.getElementsByTagName( 'LongMessage')[0].firstChild.data, line=source_line.getElementsByTagName( 'Message')[0].firstChild.data)) return bug_counts
def test_strict_usage(self): init_subsystem( JvmPlatform, options={ "jvm-platform": { "platforms": { "default-platform": {"target": "9"}, "8-platform": {"target": "8"}, "9-platform": {"target": "9"}, "strict-8-platform": {"target": "8", "strict": True}, "strict-9-platform": {"target": "9", "strict": True}, }, "default_platform": "default-platform", "default_runtime_platform": None, } }, ) instance = JvmPlatform.global_instance() strict_8_platform = instance.get_platform_by_name("strict-8-platform") default_9_platform = instance.default_platform # TODO maybe this should use the runtime platform assert instance._preferred_jvm_distribution_args([]) == { "jdk": False, } assert JvmPlatform._preferred_jvm_distribution_args([default_9_platform]) == { "minimum_version": Revision.lenient("9.0.0"), "maximum_version": None, "jdk": False, } assert JvmPlatform._preferred_jvm_distribution_args([default_9_platform], strict=True) == { "minimum_version": Revision.lenient("9.0.0"), "maximum_version": Revision.lenient("9.0.9999"), "jdk": False, } assert instance._preferred_jvm_distribution_args([strict_8_platform]) == { "minimum_version": Revision.lenient("1.8.0"), "maximum_version": Revision.lenient("1.8.9999"), "jdk": False, } assert instance._preferred_jvm_distribution_args([strict_8_platform], strict=False) == { "minimum_version": Revision.lenient("1.8.0"), "maximum_version": None, "jdk": False, } with self.assertRaisesRegex( JvmPlatform.IncompatiblePlatforms, "lenient platform with higher minimum version, 9, than strict requirement of 1.8", ): # requested strict 8 & lenient 9. # fail because 9 is lower bound JvmPlatform._preferred_jvm_distribution_args( [ instance.get_platform_by_name("9-platform"), instance.get_platform_by_name("strict-8-platform"), ] ) with self.assertRaisesRegex( JvmPlatform.IncompatiblePlatforms, "Multiple strict platforms with differing target releases were found: 1.8, 9", ): # two different strict platforms can't work JvmPlatform._preferred_jvm_distribution_args( [ instance.get_platform_by_name("strict-9-platform"), instance.get_platform_by_name("strict-8-platform"), ] ) # two of the same strict platform thumbs up assert JvmPlatform._preferred_jvm_distribution_args( [ instance.get_platform_by_name("strict-8-platform"), instance.get_platform_by_name("strict-8-platform"), ] ) == { "minimum_version": Revision.lenient("1.8.0"), "maximum_version": Revision.lenient("1.8.9999"), "jdk": False, } # strict highest, matching highest non-strict, other non-strict assert JvmPlatform._preferred_jvm_distribution_args( [ instance.get_platform_by_name("strict-9-platform"), instance.get_platform_by_name("9-platform"), instance.get_platform_by_name("8-platform"), ] ) == { "minimum_version": Revision.lenient("9.0.0"), "maximum_version": Revision.lenient("9.0.9999"), "jdk": False, }
def test(self): self.assertComponents(Revision.lenient("1.2.3"), 1, 2, 3) self.assertComponents(Revision.lenient("1.2.3-SNAPSHOT-eabc"), 1, 2, 3, "SNAPSHOT", "eabc") self.assertComponents(Revision.lenient("1.2.3-SNAPSHOT4"), 1, 2, 3, "SNAPSHOT", 4) self.assertTrue(Revision.lenient("a") < Revision.lenient("b")) self.assertTrue(Revision.lenient("1") < Revision.lenient("2")) self.assertTrue(Revision.lenient("1") < Revision.lenient("a")) self.assertEqual(Revision.lenient("1.2.3"), Revision.lenient("1.2.3")) self.assertTrue(Revision.lenient("1.2.3") < Revision.lenient("1.2.3-SNAPSHOT")) self.assertTrue(Revision.lenient("1.2.3-SNAPSHOT") < Revision.lenient("1.2.3-SNAPSHOT-abc")) self.assertTrue( Revision.lenient("1.2.3-SNAPSHOT-abc") < Revision.lenient("1.2.3-SNAPSHOT-bcd") ) self.assertTrue( Revision.lenient("1.2.3-SNAPSHOT-abc6") < Revision.lenient("1.2.3-SNAPSHOT-abc10") )
def findbugs(self, target): runtime_classpaths = self.context.products.get_data('runtime_classpath') runtime_classpath = runtime_classpaths.get_for_targets(target.closure(bfs=True)) aux_classpath = OrderedSet(jar for conf, jar in runtime_classpath if conf == 'default') target_jars = OrderedSet(jar for conf, jar in runtime_classpaths.get_for_target(target) if conf == 'default') bug_counts = { 'error': 0, 'high': 0, 'normal': 0, 'low': 0 } if not target_jars: self.context.log.info(' No jars to be analyzed') return bug_counts output_dir = os.path.join(self.workdir, target.id) safe_mkdir(output_dir) output_file = os.path.join(output_dir, 'findbugsXml.xml') aux_classpath_file = os.path.join(self.workdir, '{}.classpath'.format(os.path.basename(output_dir))) with open(aux_classpath_file, 'w') as f: f.write('\n'.join(aux_classpath - target_jars)) args = [ '-auxclasspathFromFile', aux_classpath_file, '-projectName', target.address.spec, '-xml:withMessages', '-effort:{}'.format(self.get_options().effort), '-{}'.format(self.get_options().threshold), '-nested:{}'.format('true' if self.get_options().nested else 'false'), '-output', output_file, '-noClassOk' ] if self.get_options().exclude_filter_file: args.extend(['-exclude', os.path.join(get_buildroot(), self.get_options().exclude_filter_file)]) if self.get_options().include_filter_file: args.extend(['-include', os.path.join(get_buildroot(), self.get_options().include_filter_file)]) if self.get_options().max_rank: args.extend(['-maxRank', str(self.get_options().max_rank)]) if self.get_options().relaxed: args.extend(['-relaxed']) if self.get_options().level == 'debug': args.extend(['-progress']) args.extend(target_jars) # Try to run spotbugs with the same java version as the target # The minimum JDK for spotbugs 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) result = self.runjava(classpath=self.tool_classpath('findbugs'), main=self._FINDBUGS_MAIN, jvm_options=self.get_options().jvm_options, args=args, workunit_name='findbugs', workunit_labels=[WorkUnitLabel.LINT]) if result != 0: raise TaskError('java {main} ... exited non-zero ({result})'.format( main=self._FINDBUGS_MAIN, result=result)) xml = XmlParser.from_file(output_file) for error in xml.parsed.getElementsByTagName('Error'): self.context.log.warn('Error: {msg}'.format( msg=error.getElementsByTagName('ErrorMessage')[0].firstChild.data)) bug_counts['error'] += 1 for bug_instance in xml.parsed.getElementsByTagName('BugInstance'): bug_rank = bug_instance.getAttribute('rank') if int(bug_rank) <= self._HIGH_PRIORITY_LOWEST_RANK: priority = 'high' elif int(bug_rank) <= self._NORMAL_PRIORITY_LOWEST_RANK: priority = 'normal' else: priority = 'low' bug_counts[priority] += 1 source_line = bug_instance.getElementsByTagName('Class')[0].getElementsByTagName('SourceLine')[0] self.context.log.warn('Bug[{priority}]: {type} {desc} {line}'.format( priority=priority, type=bug_instance.getAttribute('type'), desc=bug_instance.getElementsByTagName('LongMessage')[0].firstChild.data, line=source_line.getElementsByTagName('Message')[0].firstChild.data)) return bug_counts
def test(self): # TODO we may want to change these particular cases assert Revision.lenient("1") > Revision.lenient("1.0.0") assert Revision.lenient("1.0") > Revision.lenient("1.0.0") assert Revision.lenient("1") < Revision.lenient("1.0.1") assert Revision.lenient("1.0") < Revision.lenient("1.0.1") assert Revision.lenient("1.0.1") < Revision.lenient("1.0.2") assert Revision.lenient("1.2.3").components == [1, 2, 3] assert Revision.lenient("1.2.3-SNAPSHOT-eabc").components == [1, 2, 3, "SNAPSHOT", "eabc"] assert Revision.lenient("1.2.3-SNAPSHOT4").components == [1, 2, 3, "SNAPSHOT", 4] assert Revision.lenient("a") < Revision.lenient("b") assert Revision.lenient("1") < Revision.lenient("2") assert Revision.lenient("1") < Revision.lenient("a") assert Revision.lenient("1.2.3") == Revision.lenient("1.2.3") assert Revision.lenient("1.2.3") < Revision.lenient("1.2.3-SNAPSHOT") assert Revision.lenient("1.2.3-SNAPSHOT") < Revision.lenient("1.2.3-SNAPSHOT-abc") assert Revision.lenient("1.2.3-SNAPSHOT-abc") < Revision.lenient("1.2.3-SNAPSHOT-bcd") assert Revision.lenient("1.2.3-SNAPSHOT-abc6") < Revision.lenient("1.2.3-SNAPSHOT-abc10")
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 test(self): self.assertComponents(Revision.lenient("1.2.3"), 1, 2, 3) self.assertComponents(Revision.lenient("1.2.3-SNAPSHOT-eabc"), 1, 2, 3, "SNAPSHOT", "eabc") self.assertComponents(Revision.lenient("1.2.3-SNAPSHOT4"), 1, 2, 3, "SNAPSHOT", 4) self.assertTrue(Revision.lenient("a") < Revision.lenient("b")) self.assertTrue(Revision.lenient("1") < Revision.lenient("2")) self.assertTrue(Revision.lenient("1") < Revision.lenient("a")) self.assertEqual(Revision.lenient("1.2.3"), Revision.lenient("1.2.3")) self.assertTrue(Revision.lenient("1.2.3") < Revision.lenient("1.2.3-SNAPSHOT")) self.assertTrue(Revision.lenient("1.2.3-SNAPSHOT") < Revision.lenient("1.2.3-SNAPSHOT-abc")) self.assertTrue(Revision.lenient("1.2.3-SNAPSHOT-abc") < Revision.lenient("1.2.3-SNAPSHOT-bcd")) self.assertTrue(Revision.lenient("1.2.3-SNAPSHOT-abc6") < Revision.lenient("1.2.3-SNAPSHOT-abc10"))
def test(self): self.assertComponents(Revision.lenient('1.2.3'), 1, 2, 3) self.assertComponents(Revision.lenient('1.2.3-SNAPSHOT-eabc'), 1, 2, 3, 'SNAPSHOT', 'eabc') self.assertComponents(Revision.lenient('1.2.3-SNAPSHOT4'), 1, 2, 3, 'SNAPSHOT', 4) self.assertTrue(Revision.lenient('a') < Revision.lenient('b')) self.assertTrue(Revision.lenient('1') < Revision.lenient('2')) self.assertTrue(Revision.lenient('1') < Revision.lenient('a')) self.assertEqual(Revision.lenient('1.2.3'), Revision.lenient('1.2.3')) self.assertTrue(Revision.lenient('1.2.3') < Revision.lenient('1.2.3-SNAPSHOT')) self.assertTrue(Revision.lenient('1.2.3-SNAPSHOT') < Revision.lenient('1.2.3-SNAPSHOT-abc')) self.assertTrue(Revision.lenient('1.2.3-SNAPSHOT-abc') < Revision.lenient('1.2.3-SNAPSHOT-bcd')) self.assertTrue( Revision.lenient('1.2.3-SNAPSHOT-abc6') < Revision.lenient('1.2.3-SNAPSHOT-abc10'))
def _version(self, version): return Revision.lenient(version)
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))
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))
def _wire_compiler_version(self): wire_compiler = self.tool_targets(self.context, 'wire_compiler')[0] wire_compiler_jar = wire_compiler.jar_dependencies[0] wire_compiler_version = wire_compiler_jar.rev return Revision.lenient(wire_compiler_version)
def findbugs(self, target): runtime_classpaths = self.context.products.get_data( "runtime_classpath") runtime_classpath = runtime_classpaths.get_for_targets( target.closure(bfs=True)) aux_classpath = OrderedSet(jar for conf, jar in runtime_classpath if conf == "default") target_jars = OrderedSet( jar for conf, jar in runtime_classpaths.get_for_target(target) if conf == "default") bug_counts = {"error": 0, "high": 0, "normal": 0, "low": 0} if not target_jars: self.context.log.info(" No jars to be analyzed") return bug_counts output_dir = os.path.join(self.workdir, target.id) safe_mkdir(output_dir) output_file = os.path.join(output_dir, "findbugsXml.xml") aux_classpath_file = os.path.join( self.workdir, "{}.classpath".format(os.path.basename(output_dir))) with open(aux_classpath_file, "w") as f: f.write("\n".join(aux_classpath - target_jars)) args = [ "-auxclasspathFromFile", aux_classpath_file, "-projectName", target.address.spec, "-xml:withMessages", "-effort:{}".format(self.get_options().effort), "-{}".format(self.get_options().threshold), "-nested:{}".format( "true" if self.get_options().nested else "false"), "-output", output_file, "-noClassOk", ] if self.get_options().exclude_filter_file: args.extend([ "-exclude", os.path.join(get_buildroot(), self.get_options().exclude_filter_file) ]) if self.get_options().include_filter_file: args.extend([ "-include", os.path.join(get_buildroot(), self.get_options().include_filter_file) ]) if self.get_options().max_rank: args.extend(["-maxRank", str(self.get_options().max_rank)]) if self.get_options().relaxed: args.extend(["-relaxed"]) if self.debug: args.extend(["-progress"]) args.extend(target_jars) # Try to run spotbugs with the same java version as the target # The minimum JDK for spotbugs 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) result = self.runjava( classpath=self.tool_classpath("findbugs"), main=self._FINDBUGS_MAIN, jvm_options=self.get_options().jvm_options, args=args, workunit_name="findbugs", workunit_labels=[WorkUnitLabel.LINT], ) if result != 0: raise TaskError( "java {main} ... exited non-zero ({result})".format( main=self._FINDBUGS_MAIN, result=result)) xml = XmlParser.from_file(output_file) for error in xml.parsed.getElementsByTagName("Error"): self.context.log.warn( "Error: {msg}".format(msg=error.getElementsByTagName( "ErrorMessage")[0].firstChild.data)) bug_counts["error"] += 1 for bug_instance in xml.parsed.getElementsByTagName("BugInstance"): bug_rank = bug_instance.getAttribute("rank") if int(bug_rank) <= self._HIGH_PRIORITY_LOWEST_RANK: priority = "high" elif int(bug_rank) <= self._NORMAL_PRIORITY_LOWEST_RANK: priority = "normal" else: priority = "low" bug_counts[priority] += 1 source_line = bug_instance.getElementsByTagName( "Class")[0].getElementsByTagName("SourceLine")[0] self.context.log.warn( "Bug[{priority}]: {type} {desc} {line}".format( priority=priority, type=bug_instance.getAttribute("type"), desc=bug_instance.getElementsByTagName( "LongMessage")[0].firstChild.data, line=source_line.getElementsByTagName("Message") [0].firstChild.data, )) return bug_counts