use of org.revapi.Archive in project revapi by revapi.
the class SemverIgnoreTransform method initialize.
@Override
public void initialize(@Nonnull AnalysisContext analysisContext) {
ModelNode node = analysisContext.getConfiguration();
if (hasMultipleElements(analysisContext.getOldApi().getArchives()) || hasMultipleElements(analysisContext.getNewApi().getArchives())) {
throw new IllegalArgumentException("The semver extension doesn't handle changes in multiple archives at once.");
}
enabled = node.get("enabled").isDefined() && node.get("enabled").asBoolean();
if (enabled) {
Iterator<? extends Archive> oldArchives = analysisContext.getOldApi().getArchives().iterator();
Iterator<? extends Archive> newArchives = analysisContext.getNewApi().getArchives().iterator();
if (!oldArchives.hasNext() || !newArchives.hasNext()) {
enabled = false;
return;
}
Archive oldArchive = oldArchives.next();
Archive newArchive = newArchives.next();
if (!(oldArchive instanceof Archive.Versioned)) {
throw new IllegalArgumentException("Old archive doesn't support extracting the version.");
}
if (!(newArchive instanceof Archive.Versioned)) {
throw new IllegalArgumentException("New archive doesn't support extracting the version.");
}
String oldVersionString = ((Archive.Versioned) oldArchive).getVersion();
String newVersionString = ((Archive.Versioned) newArchive).getVersion();
Version oldVersion = Version.parse(oldVersionString);
Version newVersion = Version.parse(newVersionString);
if (newVersion.major == 0 && oldVersion.major == 0 && !node.get("versionIncreaseAllows").isDefined()) {
DifferenceSeverity minorChangeAllowed = asSeverity(node.get("versionIncreaseAllows", "minor"), DifferenceSeverity.BREAKING);
DifferenceSeverity patchVersionAllowed = asSeverity(node.get("versionIncreaseAllows", "patch"), DifferenceSeverity.NON_BREAKING);
if (newVersion.minor > oldVersion.minor) {
allowedSeverity = minorChangeAllowed;
} else if (newVersion.minor == oldVersion.minor && newVersion.patch > oldVersion.patch) {
allowedSeverity = patchVersionAllowed;
} else {
allowedSeverity = null;
}
} else {
DifferenceSeverity majorChangeAllowed = asSeverity(node.get("versionIncreaseAllows", "major"), DifferenceSeverity.BREAKING);
DifferenceSeverity minorChangeAllowed = asSeverity(node.get("versionIncreaseAllows", "minor"), DifferenceSeverity.NON_BREAKING);
DifferenceSeverity patchVersionAllowed = asSeverity(node.get("versionIncreaseAllows", "patch"), DifferenceSeverity.EQUIVALENT);
if (newVersion.major > oldVersion.major) {
allowedSeverity = majorChangeAllowed;
} else if (newVersion.major == oldVersion.major && newVersion.minor > oldVersion.minor) {
allowedSeverity = minorChangeAllowed;
} else {
allowedSeverity = patchVersionAllowed;
}
}
passThroughDifferences = Collections.emptyList();
if (node.get("passThroughDifferences").isDefined()) {
passThroughDifferences = node.get("passThroughDifferences").asList().stream().map(ModelNode::asString).collect(toList());
}
}
}
use of org.revapi.Archive in project revapi by revapi.
the class Compiler method compile.
public CompilationValve compile(final ProbingEnvironment environment, final AnalysisConfiguration.MissingClassReporting missingClassReporting, final boolean ignoreMissingAnnotations, final InclusionFilter inclusionFilter) throws Exception {
File targetPath = Files.createTempDirectory("revapi-java").toAbsolutePath().toFile();
File sourceDir = new File(targetPath, "sources");
sourceDir.mkdir();
File lib = new File(targetPath, "lib");
lib.mkdir();
// make sure the classpath is in the same order as passed in
int classPathSize = size(classPath);
int nofArchives = classPathSize + size(additionalClassPath);
int prefixLength = (int) Math.log10(nofArchives) + 1;
IdentityHashMap<Archive, File> classPathFiles = copyArchives(classPath, lib, 0, prefixLength);
IdentityHashMap<Archive, File> additionClassPathFiles = copyArchives(additionalClassPath, lib, classPathSize, prefixLength);
List<String> options = Arrays.asList("-d", sourceDir.toString(), "-cp", composeClassPath(lib));
List<JavaFileObject> sources = Arrays.<JavaFileObject>asList(new MarkerAnnotationObject(), new ArchiveProbeObject());
// the locale and charset are actually not important, because the only sources we're providing
// are not file-based. The rest of the stuff the compiler will be touching is already compiled
// and therefore not affected by the charset.
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, Locale.getDefault(), Charset.forName("UTF-8"));
final JavaCompiler.CompilationTask task = compiler.getTask(output, fileManager, null, options, Collections.singletonList(ArchiveProbeObject.CLASS_NAME), sources);
ProbingAnnotationProcessor processor = new ProbingAnnotationProcessor(environment);
task.setProcessors(Collections.singletonList(processor));
Future<Boolean> future = processor.submitWithCompilationAwareness(executor, task, () -> {
if (Timing.LOG.isDebugEnabled()) {
Timing.LOG.debug("About to crawl " + environment.getApi());
}
try {
new ClasspathScanner(fileManager, environment, classPathFiles, additionClassPathFiles, missingClassReporting, ignoreMissingAnnotations, inclusionFilter).initTree();
} catch (IOException e) {
throw new IllegalStateException("Failed to scan the classpath.", e);
}
if (Timing.LOG.isDebugEnabled()) {
Timing.LOG.debug("Crawl finished for " + environment.getApi());
}
});
return new CompilationValve(future, targetPath, environment, fileManager);
}
use of org.revapi.Archive in project revapi by revapi.
the class ClasspathScanner method initTree.
void initTree() throws IOException {
List<ArchiveLocation> classPathLocations = classPath.keySet().stream().map(ArchiveLocation::new).collect(toList());
Scanner scanner = new Scanner();
for (ArchiveLocation loc : classPathLocations) {
scanner.scan(loc, classPath.get(loc.getArchive()), true);
}
SyntheticLocation allLoc = new SyntheticLocation();
fileManager.setLocation(allLoc, classPath.values());
Function<String, JavaFileObject> searchHard = className -> Stream.concat(Stream.of(allLoc), Stream.of(POSSIBLE_SYSTEM_CLASS_LOCATIONS)).map(l -> {
try {
return fileManager.getJavaFileForInput(l, className, JavaFileObject.Kind.CLASS);
} catch (IOException e) {
throw new IllegalStateException(e);
}
}).filter(Objects::nonNull).findFirst().orElse(null);
Set<TypeElement> lastUnknowns = Collections.emptySet();
Map<String, ArchiveLocation> cachedArchives = new HashMap<>(additionalClassPath.size());
while (!scanner.requiredTypes.isEmpty() && !lastUnknowns.equals(scanner.requiredTypes.keySet())) {
lastUnknowns = new HashSet<>(scanner.requiredTypes.keySet());
for (TypeElement t : lastUnknowns) {
String name = environment.getElementUtils().getBinaryName(t).toString();
JavaFileObject jfo = searchHard.apply(name);
if (jfo == null) {
// this type is really missing
continue;
}
URI uri = jfo.toUri();
String path;
if ("jar".equals(uri.getScheme())) {
// we pass our archives as jars, so let's dig only into those
path = uri.getSchemeSpecificPart();
// jar:file:/path .. let's get rid of the "file:" part
int colonIdx = path.indexOf(':');
if (colonIdx >= 0) {
path = path.substring(colonIdx + 1);
}
// separate the file path from the in-jar path
path = path.substring(0, path.lastIndexOf('!'));
// remove superfluous forward slashes at the start of the path, if any
int lastSlashIdx = -1;
for (int i = 0; i < path.length() - 1; ++i) {
if (path.charAt(i) == '/' && path.charAt(i + 1) != '/') {
lastSlashIdx = i;
break;
}
}
if (lastSlashIdx > 0) {
path = path.substring(lastSlashIdx);
}
} else {
path = uri.getPath();
}
ArchiveLocation loc = cachedArchives.get(path);
if (loc == null) {
Archive ar = null;
for (Map.Entry<Archive, File> e : additionalClassPath.entrySet()) {
if (e.getValue().getAbsolutePath().equals(path)) {
ar = e.getKey();
break;
}
}
if (ar != null) {
loc = new ArchiveLocation(ar);
cachedArchives.put(path, loc);
}
}
if (loc != null) {
scanner.scanClass(loc, t, false);
}
}
}
// ok, so scanning the archives doesn't give us any new resolved classes that we need in the API...
// let's scan the system classpath. What will be left after this will be the truly missing classes.
// making a copy because the required types might be modified during scanning
Map<TypeElement, Boolean> rts = new HashMap<>(scanner.requiredTypes);
ArchiveLocation systemClassPath = new ArchiveLocation(new Archive() {
@Nonnull
@Override
public String getName() {
return SYSTEM_CLASSPATH_NAME;
}
@Nonnull
@Override
public InputStream openStream() throws IOException {
throw new UnsupportedOperationException();
}
});
for (Map.Entry<TypeElement, Boolean> e : rts.entrySet()) {
scanner.scanClass(systemClassPath, e.getKey(), false);
}
scanner.initEnvironment();
}
use of org.revapi.Archive in project revapi by revapi.
the class JavaArchiveAnalyzerTest method testPreventRecursionWhenConstructingInheritedMembers.
@Test
public void testPreventRecursionWhenConstructingInheritedMembers() throws Exception {
ArchiveAndCompilationPath archive = createCompiledJar("a.jar", "misc/MemberInheritsOwner.java");
JavaArchiveAnalyzer analyzer = new JavaArchiveAnalyzer(new API(Arrays.asList(new ShrinkwrapArchive(archive.archive)), null), Executors.newSingleThreadExecutor(), null, false, InclusionFilter.acceptAll());
try {
JavaElementForest forest = analyzer.analyze();
forest.getRoots();
Assert.assertEquals(1, forest.getRoots().size());
Predicate<Element> findMethod = c -> "method void MemberInheritsOwner::method()".equals(c.getFullHumanReadableString());
Predicate<Element> findMember1 = c -> "interface MemberInheritsOwner.Member1".equals(c.getFullHumanReadableString());
Predicate<Element> findMember2 = c -> "interface MemberInheritsOwner.Member2".equals(c.getFullHumanReadableString());
Element root = forest.getRoots().first();
Assert.assertEquals(3, root.getChildren().size());
Assert.assertTrue(root.getChildren().stream().anyMatch(findMethod));
Assert.assertTrue(root.getChildren().stream().anyMatch(findMember1));
Assert.assertTrue(root.getChildren().stream().anyMatch(findMember2));
Assert.assertEquals(1, root.getChildren().stream().filter(findMember1).findFirst().get().getChildren().size());
Assert.assertEquals(1, root.getChildren().stream().filter(findMember2).findFirst().get().getChildren().size());
} finally {
deleteDir(archive.compilationPath);
analyzer.getCompilationValve().removeCompiledResults();
}
}
use of org.revapi.Archive in project revapi by revapi.
the class BuildTimeReporter method initialize.
@Override
public void initialize(@Nonnull AnalysisContext context) {
allProblems = new ArrayList<>();
oldApi = new ArrayList<>();
for (Archive a : context.getOldApi().getArchives()) {
oldApi.add(a);
}
newApi = new ArrayList<>();
for (Archive a : context.getNewApi().getArchives()) {
newApi.add(a);
}
this.breakingSeverity = (DifferenceSeverity) context.getData(BREAKING_SEVERITY_KEY);
}
Aggregations