siom79 / japicmp Goto Github PK
View Code? Open in Web Editor NEWComparison of two versions of a jar archive
Home Page: https://siom79.github.io/japicmp
License: Apache License 2.0
Comparison of two versions of a jar archive
Home Page: https://siom79.github.io/japicmp
License: Apache License 2.0
3.0.5 of maven is prevalent with many projects and is being widely used. e.g. netbeans 8.0.1 gets shipped with maven 3.0.5. There is an inertia which prevents upgrades without benefits.
While evealuating japicmp, I realized that even if the prerequisite is 3.0.5, japicmp works as probably the API japicmp cares about has not changed.
Can we make 3.0.5 prerequisite? The issues that come with taking such a case will be the cost people who stick to 3.0.5 will have to pay.
If your jenkins project name contains a space then some apparent mishandling of paths will be observed
[INFO] java.io.FileNotFoundException: /home/jenkins/workspace/Generic%20Utilities/target/japicmp/japicmp.xsd (No such file or directory)
com.sun.xml.internal.txw2.TxwException: java.io.FileNotFoundException: /home/jenkins/workspace/Generic%20Utilities/target/japicmp/japicmp.xsd (No such file or directory)
at com.sun.xml.internal.txw2.output.StreamSerializer.<init>(StreamSerializer.java:81)
at com.sun.xml.internal.txw2.output.ResultFactory.createSerializer(ResultFactory.java:60)
at com.sun.xml.internal.bind.v2.schemagen.XmlSchemaGenerator$Namespace.writeTo(XmlSchemaGenerator.java:607)
at com.sun.xml.internal.bind.v2.schemagen.XmlSchemaGenerator$Namespace.access$800(XmlSchemaGenerator.java:490)
at com.sun.xml.internal.bind.v2.schemagen.XmlSchemaGenerator.write(XmlSchemaGenerator.java:472)
at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.generateSchema(JAXBContextImpl.java:792)
at japicmp.output.xml.XmlOutputGenerator.createXmlDocumentAndSchema(XmlOutputGenerator.java:71)
at japicmp.output.xml.XmlOutputGenerator.generate(XmlOutputGenerator.java:34)
at japicmp.maven.JApiCmpMojo.generateXmlOutput(JApiCmpMojo.java:209)
at japicmp.maven.JApiCmpMojo.execute(JApiCmpMojo.java:107)
at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:132)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:208)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:116)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:80)
at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:51)
at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:120)
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:347)
at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:154)
at org.jvnet.hudson.maven3.launcher.Maven32Launcher.main(Maven32Launcher.java:132)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.codehaus.plexus.classworlds.launcher.Launcher.launchStandard(Launcher.java:330)
at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:238)
at jenkins.maven3.agent.Maven32Main.launch(Maven32Main.java:181)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at hudson.maven.Maven3Builder.call(Maven3Builder.java:136)
at hudson.maven.Maven3Builder.call(Maven3Builder.java:71)
at hudson.remoting.UserRequest.perform(UserRequest.java:118)
at hudson.remoting.UserRequest.perform(UserRequest.java:48)
at hudson.remoting.Request$2.run(Request.java:328)
at hudson.remoting.InterceptingExecutorService$1.call(InterceptingExecutorService.java:72)
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.io.FileNotFoundException: /home/jenkins/workspace/Generic%20Utilities/target/japicmp/japicmp.xsd (No such file or directory)
at java.io.FileOutputStream.open(Native Method)
at java.io.FileOutputStream.<init>(FileOutputStream.java:221)
at java.io.FileOutputStream.<init>(FileOutputStream.java:110)
at com.sun.xml.internal.txw2.output.StreamSerializer.<init>(StreamSerializer.java:77)
The ordering of classes and methods in reports appears to be random. Please sort them so that reports have a consistent order.
The type (enum, interface, class, annotation) of a class can change. This should be tracked.
The Java SDK provides object serialization as described in the serialization specification.
japicmp should detect incompatible changes regarding this protocol (see section about Versioning).
Hi,
It would be great if it was possible to specify excludes (and includes) at the level of the method, something like:
package.Class.method
or maybe even something more specific including parameters types.
The motivation is that we use your (wonderful I must say) tool (the maven plugin in particular) to prevent changes to an API and so we want to always fail a build if there was such changes.
But of course, sometimes, there is exceptions and these exceptions are for specific method.
So we would like to skip checks for these :)
Thanks!
Improve javadoc documentation.
I have been testing the 0.2.3
version and have noticed that it does not correctly report changes to methods. I have created a repository for demonstrating the problems I am seeing. To reproduce:
git clone https://[email protected]/ojburn/japicmp-bugs.git
cd japicmp-bugs/method-changes
./gradlew jar cmp
The first problem is changing the method compareTo
from:
public class MethodAnnotations implements Comparable<String>
{
@Override
public int compareTo(String o)
{
return 0;
}
}
to:
public class MethodAnnotations
{
public int compareTo(String o)
{
return 0;
}
}
japicmp reports that the method is both UNCHANGED
and REMOVED
Processing samples.MethodAnnotations, change status MODIFIED
Number methods: 2
method compareTo, change status UNCHANGED, compat true
method compareTo, change status REMOVED, compat false
The second problem is that when a method return type is changed, japicmp
is reporting that a method had been REMOVED
as well as a NEW
one added. I would suggest that japicmp should report that the method has been CHANGED
.
An example would be the changes in the return types from:
public interface InterfaceMethodChanges
{
void returnChangedVoidToInt();
int returnChangedIntToVoid();
String returnChangedStringToObject();
}
to:
public interface InterfaceMethodChanges
{
int returnChangedVoidToInt();
void returnChangedIntToVoid();
Object returnChangedStringToObject();
}
It would be nice for API compatibility checking to show only the binary incompatible changes. (And trigger failure when incompatibilities are present).
We have integrated japicmp
into Groovy to integrate binary compatibility checks. See [1] and [2].
However, it fails to recognize the change in implemented interfaces. To reproduce:
git clone https://github.com/melix/japicmp-bugs.git
cd changed-interface
./gradlew japicmp
The build should fail, but it doesn't, because japicmp failed to recognize the new interface and says the class is unchanged.
[1] https://github.com/melix/japicmp-gradle-plugin
[2] groovy/groovy-core@29e3bd4
breakBuildOnBinaryIncompatibleModifications
set to true
Expected: It should fail
Actual: It doesn't fail
Adding a new method (without default implementation) to an interface breaks compatibility, because existing implementations will break. See Eclipse's reference for this too. Or am I missing some configuration for this?
In my HTML output I noticed any class with changes has an additional "Superclass UNCHANGED" table, even with the -m option passed. This takes up a lot of space in large diffs and is just noise. Ignoring the -m option seems like a bug.
Wishlist improvements:
Thanks for finishing the HTML reports though, its still pretty useful!
It would be nice if JApiMethod
provided some helper methods like:
...
All those methods would be useful, because some changes are critical (changing from public to private for example), but others are not (protected to public). The synthetic check is also nice because some methods are generated by the compiler and for internal use only, so they do not participate the same way in breaking changes.
The difficulty of this ticket is that JApiMethod
documents a method change, so references 2 methods, and that oldMethod
and newMethod
are jassist classes which do not provide those helper methods.
Another option would be to rely on the visitor pattern: register visitors on the comparator, then call visitModifierChange(JApiMethod, int oldModifier, int newModifier)
(and provide a bunch of those methods that allow to customize the behavior of the comparator).
The problem is probably the directory which the plugin creates and tries to recreate on subsequent runs. This problem does not exist if 'clean' goal is given every single time which first deletes the target folder of the mvn project.
Comparing the same versions could output "no differences" instead of an empty list of differences.
In order to compare two versions of an artifact during the build process, the functionality of japicmp should be available per maven plugin.
The command line options should be available as maven elements.
There should be an option to break the build every time a change in the API has been detected.
We have integrated japicmp
into Groovy to integrate binary compatibility checks. See [1] and [2].
However, it fails to recognize added constructors. To reproduce:
git clone https://github.com/melix/japicmp-bugs.git
cd added-constructor
./gradlew japicmp
The build should fail, but it doesn't, because japicmp failed to recognize the added constructor and says the class is unchanged.
[1] https://github.com/melix/japicmp-gradle-plugin
[2] groovy/groovy-core@29e3bd4
A new command-line option should be added, that filters the classes under investigation by their package name. The package names should be given as a list of comma-separated regular expressions.
The option --html-stylesheet
for the CLI tool should be also available for the maven plugin.
Thanks for providing a compare tool that works with java8, used clirr before.
Currently build jar files with different classifiers that I want to compare, but classifier is not supported inside dependency when configuring old version to compare against.
want to be able to use classifier like below
japicmp japicmp-test-v1 0.5.3 somevariant ##Best regards
Pether
Hi,
I am trying to use this plugin. I couldn't understand why we need to configure the libraries dependencies under the configuration element. Why can't it pull the dependencies from the pom ?
In my opinion it is not correct, to use the same classpath when looking for interfaces or base classes when comparing two classes.
Assume i have a service interface A in a.jar and its implementation B in b.jar. Now i want to compare two versions B1 (in b1.jar) and B2 (in b2.jar) which implement A1 (in a1.jar) and A2 (in a2.jar) which may or may not differ. So for correct results when loading B1 i need a1.jar on the classpath as for loading B2 i need a2.jar.
All actions for release 0.5.1
The user should be able to provide the source code for both versions (either on the command line as source jar or the maven plugin loads it if available from the repository) and japicmp should in this case additionally also compare the javadoc comments.
If the javadoc comment has changed this may indicate that the behavior of the method has changed. For modified methods is also shows whether the javadoc comment has been adjusted.
The current version reports methods whose parameter list or name has changed as a pair of new and deleted methods. There should be some kind of heuristic similarity detection that finds the old version of the method signature in the new version (for most cases).
Currently all changes in the API are tracked. It would be nice to have an indicator for binary compatibility.
japicmp should provide a single page HTML report.
There should be a CLI option that transforms the given XML document into an HTML report.
As terminal user without a 64" display with 180 lines in my terminal session, I want to read an error message without scrolling ๐
compare to other shell tools like find
-> find --unknown-opt
, find --help
, man find
.. -jar japicmp.jar
I only want to see short usage information and an error message or the successful result of japicmp.. -jar japicmp.jar --help
I want to see the full help - man page equivalent and no error message, the exit code sould be zeroactual on feature-semver branch
$ .. -jar japicmp.jar --help
E: Required option '-o' is missing
NAME
java -jar japicmp.jar - Compares jars
...
...
$ echo $?
255
$ # why is -o required for help output?
I tought it was fixed with 145b7f6 and 3ab5e55 but I can't find its content anymore. ... Maybe also abandoned with my other changes ... It feels a little bit like "Don't write test and don't send me pull requests, I'll rollback them later" ๐ ๐
First I would like to thank you for this great tool.
I plan to use japicmp maven plugin in a full development stack (https://github.com/seedstack) which contains a lot of modules, some of which are of pom packaging type. I configured japicmp in my parent pom with maven properties to automatically check the current version against the latest release:
<oldVersion>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
<version>RELEASE</version>
</dependency>
</oldVersion>
<newVersion>
<file>
<path>${project.build.directory}/${project.build.finalName}.jar</path>
</file>
</newVersion>
I found that japicmp doesn't automatically skip pom packaging modules and fails instead because it cannot find the file specified in . I worked around this problem by using a property in the tag that is set to true by default but I must override it to false in each of my 69 jar modules.
It would help me a lot if the japicmp maven plugin could automatically skip every non jar-packaged module (with an info log). This behavior may be enabled with a configuration option.
Regards.
I have a multi module project with a module that is used from other modules. That common module in turn depends on Guice and exposes a class that extends AbstractModule.
guice
`- a
`- b
`- c
I have to declare guice as a dependency in the plugin configuration for both b
and c
or I'll get an error:
[ERROR] Failed to execute goal com.github.siom79.japicmp:japicmp-maven-plugin:0.5.1:cmp (default) on project apollo-nameless: Execution default of goal com.github.siom79.japicmp:japicmp-maven-plugin:0.5.1:cmp failed: Could not load 'com.example.ExampleModule': com.google.inject.AbstractModule. Please make sure that all libraries have been added to the classpath...
Is there a good reason for not including transitive dependencies automatically?
It would be nice to have some statistics at the end of the XML file or the diff output (e.g. number of changed classes/methods).
Whenever a package matches an include filter, it is taken into account, ignoring the exclude filter.
I would have expected the exclude filter has precedence over the include filter
eg:
include: com.example.*
exclude: com.example.internal.*
would ignore all com.example.internal.* packages
We have integrated japicmp
into Groovy to integrate binary compatibility checks. See [1] and [2].
However, it fails to recognize the change of superclass. To reproduce:
git clone https://github.com/melix/japicmp-bugs.git
cd changed-superclass
./gradlew japicmp
The build should fail, but it doesn't, because japicmp failed to recognize the new superclass and says the class is unchanged.
[1] https://github.com/melix/japicmp-gradle-plugin
[2] groovy/groovy-core@29e3bd4
Compiling classes/interfaces that extend parameterized interfaces/classes the compiler creates a synthetic method (see [here](Effects of Type Erasure and Bridge Methods)). These can be detected and should not be reported as separated added method.
The maven plugin should have a skip parameter to be able to skip the execution under specific circumstances.
We have integrated japicmp
into Groovy to integrate binary compatibility checks. See [1] and [2].
However, it fails to recognize removed constructors. To reproduce:
git clone https://github.com/melix/japicmp-bugs.git
cd removed-constructor
./gradlew japicmp
The build should fail, but it doesn't, because japicmp failed to recognize the removed constructor and says the class is unchanged.
[1] https://github.com/melix/japicmp-gradle-plugin
[2] groovy/groovy-core@29e3bd4
I am evaluating the code on the master branch whether I can use this to follow the semantic versions for some artifacts we build. But I found out that there are false positives.
Here are the links to get the 2 jars:
http://www.malhar-inc.com/chetan/dt-api-2.0.0.jar
http://www.malhar-inc.com/chetan/dt-api-2.0.1.jar
And here is the command that I use (note that even if i change the association of new and old - in both cases it reports that methods are removed):
chetan@chubi:~/work/japicmp$ java -jar ./japicmp/target/japicmp-0.4.2-SNAPSHOT-jar-with-dependencies.jar -n /home/chetan/datatorrent/releases/2.0.1/lib/dt-api-2.0.1.jar -o /home/chetan/datatorrent/releases/2.0.0/lib/dt-api-2.0.0.jar -b
Comparing /home/chetan/datatorrent/releases/2.0.0/lib/dt-api-2.0.0.jar with /home/chetan/datatorrent/releases/2.0.1/lib/dt-api-2.0.1.jar:
***! MODIFIED CLASS: PUBLIC ABSTRACT com.datatorrent.api.LocalMode (not serializable)
---! REMOVED METHOD: PUBLIC(-) ABSTRACT(-) com.datatorrent.api.DAG prepareDAG(com.datatorrent.api.StreamingApplication, org.apache.hadoop.conf.Configuration)
---! REMOVED METHOD: PUBLIC(-) STATIC(-) void runApp(com.datatorrent.api.StreamingApplication, org.apache.hadoop.conf.Configuration, int)
***! MODIFIED INTERFACE: PUBLIC ABSTRACT com.datatorrent.api.StreamCodec (not serializable)
---! REMOVED METHOD: PUBLIC(-) ABSTRACT(-) java.lang.Object fromByteArray(com.datatorrent.common.util.Slice)
***! MODIFIED INTERFACE: PUBLIC ABSTRACT com.datatorrent.api.StreamingApplication (not serializable)
---! REMOVED METHOD: PUBLIC(-) ABSTRACT(-) void populateDAG(com.datatorrent.api.DAG, org.apache.hadoop.conf.Configuration)
chetan@chubi:~/work/japicmp$ java -jar ./japicmp/target/japicmp-0.4.2-SNAPSHOT-jar-with-dependencies.jar -o /home/chetan/datatorrent/releases/2.0.1/lib/dt-api-2.0.1.jar -n /home/chetan/datatorrent/releases/2.0.0/lib/dt-api-2.0.0.jar -b
Comparing /home/chetan/datatorrent/releases/2.0.1/lib/dt-api-2.0.1.jar with /home/chetan/datatorrent/releases/2.0.0/lib/dt-api-2.0.0.jar:
***! MODIFIED CLASS: PUBLIC ABSTRACT com.datatorrent.api.LocalMode (not serializable)
---! REMOVED METHOD: PUBLIC(-) ABSTRACT(-) com.datatorrent.api.DAG prepareDAG(com.datatorrent.api.StreamingApplication, org.apache.hadoop.conf.Configuration)
---! REMOVED METHOD: PUBLIC(-) STATIC(-) void runApp(com.datatorrent.api.StreamingApplication, org.apache.hadoop.conf.Configuration, int)
***! MODIFIED INTERFACE: PUBLIC ABSTRACT com.datatorrent.api.StreamCodec (not serializable)
---! REMOVED METHOD: PUBLIC(-) ABSTRACT(-) java.lang.Object fromByteArray(com.datatorrent.common.util.Slice)
***! MODIFIED INTERFACE: PUBLIC ABSTRACT com.datatorrent.api.StreamingApplication (not serializable)
---! REMOVED METHOD: PUBLIC(-) ABSTRACT(-) void populateDAG(com.datatorrent.api.DAG, org.apache.hadoop.conf.Configuration)
When one of the two modifiers static or final is changed for a method/class, this should be tracked as change.
These methods are generated by the compiler and their implementation should be allowed to change between compiled versions. This would include bridge methods and accessor methods.
This was originally going to be a report requesting non-reporting of removed anonymous inner classes but I can't (yet) formulate the rules that should be applied.
Tool is great. No, it is just awesome. Thanks. ๐
Our team consider to use japicmp to automate a binary compatibility verification of a bunch of jars. We deliver 15 jars and i can run appropriate command 15 times but the problem is with reporting. We would like to have one html (instead of 15) for a hole verification.
I found few examples for comparison of one jar (with old version). But are there any practices to compare a bunch of jars and report properly ?
Thank you.
Although adding or removing annotations does not affect the binary compatibility, it would be nice to track annotation changes. This feature could be used to analyse changes in annotation-based APIs/features (like e.g. JPA, JAX-RS, etc.).
In order to evaluate if a change is binary compatible or not, super classes and implemented interfaces have to be included on the classpath. For a quick check it would be nice if this would not be necessary. But this would also mean that the statement "binary compatible" is only partly valid in this case.
The current version displays all changes, no matter what modifier (public, private, etc) the class or class member has. There should be a new option that let's the user restrict the modifier, so that for example only public classes and class members are reported.
When I run japicmp the output.xml gets this header
<japicmp xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" newJar="c:\new.jar" oldJar="c:\old.jar" xsi:schemaLocation="https://github.com/siom79/japicmp/schema/japicmp.xsd japicmp.xsd" xsi:noNamespaceSchemaLocation="japicmp.xsd">
As developer I want to know a short info about api changes - as lib maintainer I want check the compatibility with previous jar without details.
java -jar japicmp.jar --old .. --new .. --only-semver-diff
the output is one of the following values: 1.0.0
or 0.1.0
or 0.0.1
1.0.0
means: there are incompatible (-b
) public (-a public
) changes like method deletions
0.1.0
means: there are public (-a public
) changes like new methods or classes
0.0.1
means: there are no public (-a public
) changes
--only-semver-diff
are --exclude, --include, --new, --old
The output of this call can be interpreted like output = newVersion - oldVersion
.
See also: http://semver.org/
(I know that semver addresses also internal behavior and not only api changes, but I think it is a helpful short information. - I don't want to work with given maven pom versions.)
Support for .editorconfig
Changing the declaration order of enums, which could break code relying on the value of Enum#ordinal()
, should perhaps be detected as a breaking change.
Giving a non-jar file as argument should not output "Comparing..."
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.