blob: 8489d8b64a5f42f33cd041991801b00d140c65b4 [file] [log] [blame]
/*
* Copyright 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.tools.apichecker;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.typeinfo.NotFoundException;
import com.google.gwt.dev.util.log.AbstractTreeLogger;
import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashSet;
/**
* Checks if the new API is compatible with the existing API.
*
*
* To compute Api diffs, follow these 5 steps: i) for each of the two
* repositories, construct an ApiContainer, ii) construct an ApiDiffGenerator,
* iii) call computeApiDiff on the ApiDiffGenerator iv) call cleanApiDiff on the
* ApiDiffGenerator v) call printApiDiff on the ApiDiffGenerator
*
* An apicontainer object has a list of apiPackage objects. ApiPackage objects
* themselves are list of ApiClass objects. ApiClass objects contain list of
* ApiConstructor, ApiMethod, and JField objects.
*
* Each ApiDiffGenerator object has a list of intersecting and missing
* ApiPackageDiffGenerator objects. Each ApiPackageDiffGenerator object has a
* list of intersecting and missing ApiClassDiffGenerator objects. Each
* ApiClassDiffGenerator object has a list of intersecting and missing
* apiMembers, where these members are constructors, methods, and fields.
*
* For each intersecting apiMember, a list of ApiChange objects is stored. Each
* ApiChange object encodes a specific Api change like adding the 'final'
* keyword to an apiMethod.
*
*/
public class ApiCompatibilityChecker {
public static final boolean API_SOURCE_COMPATIBILITY = true;
public static final boolean REMOVE_ABSTRACT_CLASS_FROM_API = true;
public static final boolean IGNORE_WHITELIST = false;
public static final boolean PRINT_COMPATIBLE_WITH = false;
public static final boolean PRINT_INTERSECTIONS = false;
public static final boolean REMOVE_DUPLICATES = true;
public static final boolean DEBUG = false;
public static final boolean DISABLE_CHECKS = true;
private static final AbstractTreeLogger logger1 = createTreeLogger();
public static String getApiDiff(ApiDiffGenerator temp,
HashSet<String> whiteList, boolean removeDuplicates)
throws NotFoundException {
temp.computeApiDiff();
if (removeDuplicates) {
temp.cleanApiDiff();
}
String apiDifferences = temp.printApiDiff();
return removeWhiteListMatches(apiDifferences, whiteList);
}
// Call APIBuilders for each of the 2 source trees
public static void main(String args[]) {
try {
ApiContainer newApi = null, existingApi = null;
boolean processNewApi = true;
boolean processExistingApi = true;
if (args.length < 1) {
printHelp();
System.exit(-1);
}
if (processNewApi) {
newApi = new ApiContainer(args[0], "_new", logger1);
}
if (processExistingApi) {
existingApi = new ApiContainer(args[0], "_old", logger1);
}
if ((processNewApi && processExistingApi)
&& (newApi != null && existingApi != null)) {
HashSet<String> whiteList = new HashSet<String>();
if (!IGNORE_WHITELIST) {
whiteList = readStringFromFile(args[0]);
}
ApiDiffGenerator apiDiff = new ApiDiffGenerator(newApi, existingApi);
String apiDifferences = getApiDiff(apiDiff, whiteList,
REMOVE_DUPLICATES);
System.out.println(apiDifferences);
System.out.println("\t\t\t\tApi Compatibility Checker tool, Copyright Google Inc. 2008");
System.exit(apiDifferences.length() == 0 ? 0 : 1);
}
} catch (Exception e) {
System.err.println("Exception " + e.getMessage()
+ ", printing stacktrace");
e.printStackTrace();
System.exit(-1);
}
}
public static void printHelp() {
System.out.println("java ApiCompatibilityChecker configFile\n");
System.out.println("The ApiCompatibilityChecker tool requires a config file as an argument. "
+ "The config file must specify two repositories of java source files "
+ "that must be compared for API source compatibility. Each repository "
+ "must specify three properties: 'name', 'sourceFiles', and 'excludeFiles.' "
+ "A suffix of '_old' is attached to properties of the first repository, "
+ "while a suffix of '_new' is attached to properties of the second "
+ "repository. An optional whitelist can also be present at the end of "
+ "the config file. The format of the whitelist is same as the output of "
+ "the tool without the whitelist.");
System.out.println();
System.out.println("The name property specifies the api name that should "
+ "be used in the output. The sourceFiles property, a colon-separated "
+ "list of files/directories, specifies the roots of the the filesystem "
+ "trees that must be included. The excludeFiles property, "
+ "a colon-separated lists of files/directories specifies the roots of "
+ "the filesystem trees that must be excluded.");
System.out.println();
System.out.println();
System.out.println("Example api.conf file:\n"
+ "name_old gwtEmulator\n"
+ "sourceFiles_old dev/core/super/com/google/gwt/dev/jjs/intrinsic/:user/super/com/google/gwt/emul/:user/src/com/google/gwt/core/client\n"
+ "excludeFiles_old \n\n"
+ "name_new gwtEmulatorCopy\n"
+ "sourceFiles_new dev/core/super/com/google/gwt/dev/jjs/intrinsic/:user/super/com/google/gwt/emul/:user/src/com/google/gwt/core/client\n"
+ "excludeFiles_new \n\n");
}
/**
* Tweak this for the log output.
*/
private static AbstractTreeLogger createTreeLogger() {
AbstractTreeLogger logger = new PrintWriterTreeLogger();
int choice = 3; // 1, 2, 3
switch (choice) {
case 1:
logger.setMaxDetail(TreeLogger.ALL);
break;
case 2:
logger.setMaxDetail(null);
break;
default:
logger.setMaxDetail(TreeLogger.ERROR);
}
return logger;
}
private static HashSet<String> readStringFromFile(String fileName)
throws IOException {
if (fileName == null) {
throw new IllegalArgumentException("fileName is null");
}
HashSet<String> hashSet = new HashSet<String>();
FileReader fr = new FileReader(fileName);
BufferedReader br = new BufferedReader(fr);
String str = null;
while ((str = br.readLine()) != null) {
str = str.trim();
// ignore comments
if (str.startsWith("#")) {
continue;
}
String splits[] = str.split(" ");
if (splits.length > 1) {
hashSet.add(splits[0] + " " + splits[1]);
}
}
return hashSet;
}
private static String removeWhiteListMatches(String apiDifferences,
HashSet<String> whiteList) {
String apiDifferencesArray[] = apiDifferences.split("\n");
String whiteListArray[] = whiteList.toArray(new String[0]);
for (int i = 0; i < apiDifferencesArray.length; i++) {
String temp = apiDifferencesArray[i].trim();
for (String whiteListElement : whiteListArray) {
if (temp.startsWith(whiteListElement)) {
apiDifferencesArray[i] = "";
}
}
}
StringBuffer sb = new StringBuffer();
for (String temp : apiDifferencesArray) {
if (temp.length() > 0) {
sb.append(temp);
sb.append("\n");
}
}
return sb.toString();
}
}