JarListSanitizer.java revision 81c5fb5448a6342cb3bb29ea501fccf95573288c
144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet/* 244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * Copyright (C) 2012 The Android Open Source Project 344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * 444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * Licensed under the Apache License, Version 2.0 (the "License"); 544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * you may not use this file except in compliance with the License. 644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * You may obtain a copy of the License at 744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * 844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * http://www.apache.org/licenses/LICENSE-2.0 944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * 1044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * Unless required by applicable law or agreed to in writing, software 1144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * distributed under the License is distributed on an "AS IS" BASIS, 1244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * See the License for the specific language governing permissions and 1444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * limitations under the License. 1544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet */ 1644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 1744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetpackage com.android.sdklib.build; 1844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 1944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetimport java.io.BufferedReader; 2044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetimport java.io.File; 2144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetimport java.io.FileInputStream; 2244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetimport java.io.FileNotFoundException; 2344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetimport java.io.FileOutputStream; 2444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetimport java.io.IOException; 2544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetimport java.io.InputStreamReader; 2644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetimport java.io.OutputStreamWriter; 2781c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohetimport java.io.PrintStream; 2844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetimport java.io.UnsupportedEncodingException; 2944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetimport java.security.MessageDigest; 3044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetimport java.util.ArrayList; 3181c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohetimport java.util.Collection; 3244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetimport java.util.Formatter; 3344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetimport java.util.HashMap; 3444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetimport java.util.List; 3544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetimport java.util.Map; 3644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetimport java.util.Map.Entry; 3744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetimport java.util.regex.Matcher; 3844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetimport java.util.regex.Pattern; 3944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 4044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet/** 4144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * A Class to handle a list of jar files, finding and removing duplicates. 4244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * 4344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * Right now duplicates are based on: 4444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * - same filename 4544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * - same length 4644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * - same content: using sha1 comparison. 4744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * 4844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * The length/sha1 are kept in a cache and only updated if the library is changed. 4944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet */ 5044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohetpublic class JarListSanitizer { 5144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 5244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet private static final byte[] sBuffer = new byte[4096]; 5344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet private static final String CACHE_FILENAME = "jarlist.cache"; 5444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet private static final Pattern READ_PATTERN = Pattern.compile("^(\\d+) (\\d+) ([0-9a-f]+) (.+)$"); 5544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 5644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet /** 5744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * Simple class holding the data regarding a jar dependency. 5844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * 5944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet */ 6044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet private static final class JarEntity { 6144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet private final File mFile; 6244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet private final long mLastModified; 6344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet private long mLength; 6444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet private String mSha1; 6544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 6644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet /** 6744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * Creates an entity from cached data. 6844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * @param path the file path 6944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * @param lastModified when it was last modified 7044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * @param length its length 7144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * @param sha1 its sha1 7244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet */ 7344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet private JarEntity(String path, long lastModified, long length, String sha1) { 7444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet mFile = new File(path); 7544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet mLastModified = lastModified; 7644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet mLength = length; 7744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet mSha1 = sha1; 7844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 7944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 8044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet /** 8144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * Creates an entity from a {@link File}. 8244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * @param file the file. 8344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet */ 8444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet private JarEntity(File file) { 8544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet mFile = file; 8644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet mLastModified = file.lastModified(); 8744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet mLength = file.length(); 8844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 8944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 9044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet /** 9144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * Checks whether the {@link File#lastModified()} matches the cached value. If not, length 9244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * is updated and the sha1 is reset (but not recomputed, this is done on demand). 9344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * @return return whether the file was changed. 9444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet */ 9544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet private boolean checkValidity() { 9644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet if (mLastModified != mFile.lastModified()) { 9744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet mLength = mFile.length(); 9844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet mSha1 = null; 9944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet return true; 10044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 10144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 10244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet return false; 10344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 10444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 10544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet private File getFile() { 10644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet return mFile; 10744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 10844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 10944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet private long getLastModified() { 11044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet return mLastModified; 11144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 11244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 11344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet private long getLength() { 11444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet return mLength; 11544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 11644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 11744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet /** 11844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * Returns the file's sha1, computing it if necessary. 11944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * @return the sha1 12044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * @throws Sha1Exception 12144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet */ 12244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet private String getSha1() throws Sha1Exception { 12344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet if (mSha1 == null) { 12444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet mSha1 = JarListSanitizer.getSha1(mFile); 12544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 12644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet return mSha1; 12744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 12844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 12944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet private boolean hasSha1() { 13044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet return mSha1 != null; 13144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 13244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 13344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 13444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet /** 13544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * Exception used to indicate the sanitized list of jar dependency cannot be computed due 13644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * to inconsistency in duplicate jar files. 13744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet */ 13844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet public static final class DifferentLibException extends Exception { 13944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet private static final long serialVersionUID = 1L; 14081c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet private final String[] mDetails; 14144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 14281c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet public DifferentLibException(String message, String[] details) { 14344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet super(message); 14481c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet mDetails = details; 14581c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet } 14681c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet 14781c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet public String[] getDetails() { 14881c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet return mDetails; 14944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 15044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 15144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 15244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet /** 15344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * Exception to indicate a failure to check a jar file's content. 15444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet */ 15544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet public static final class Sha1Exception extends Exception { 15644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet private static final long serialVersionUID = 1L; 15744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet private final File mJarFile; 15844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 15944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet public Sha1Exception(File jarFile, Throwable cause) { 16044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet super(cause); 16144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet mJarFile = jarFile; 16244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 16344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 16444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet public File getJarFile() { 16544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet return mJarFile; 16644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 16744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 16844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 16944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet private final File mOut; 17081c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet private final PrintStream mOutStream; 17144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 17244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet /** 17344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * Creates a sanitizer. 17444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * @param out the project output where the cache is to be stored. 17544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet */ 17644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet public JarListSanitizer(File out) { 17744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet mOut = out; 17881c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet mOutStream = System.out; 17981c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet } 18081c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet 18181c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet public JarListSanitizer(File out, PrintStream outStream) { 18281c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet mOut = out; 18381c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet mOutStream = outStream; 18444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 18544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 18644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet /** 18744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * Sanitize a given list of files 18844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * @param files the list to sanitize 18944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * @return a new list containing no duplicates. 19044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * @throws DifferentLibException 19144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * @throws Sha1Exception 19244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet */ 19381c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet public List<File> sanitize(Collection<File> files) throws DifferentLibException, Sha1Exception { 19444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet List<File> results = new ArrayList<File>(); 19544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 19644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet // get the cache list. 19744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet Map<String, JarEntity> jarList = getCachedJarList(); 19844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 19944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet boolean updateJarList = false; 20044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 20144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet // clean it up of removed files. 20244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet // use results as a temp storage to store the files to remove as we go through the map. 20344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet for (JarEntity entity : jarList.values()) { 20444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet if (entity.getFile().exists() == false) { 20544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet results.add(entity.getFile()); 20644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 20744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 20844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 20944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet // the actual clean up. 21044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet if (results.size() > 0) { 21144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet for (File f : results) { 21244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet jarList.remove(f.getAbsolutePath()); 21344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 21444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 21544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet results.clear(); 21644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet updateJarList = true; 21744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 21844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 21944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet Map<String, List<JarEntity>> nameMap = new HashMap<String, List<JarEntity>>(); 22044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 22144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet // update the current jar list if needed, while building a 2ndary map based on 22244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet // filename only. 22344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet for (File file : files) { 22444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet String path = file.getAbsolutePath(); 22544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet JarEntity entity = jarList.get(path); 22644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 22744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet if (entity == null) { 22844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet entity = new JarEntity(file); 22944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet jarList.put(path, entity); 23044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet updateJarList = true; 23144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } else { 23244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet updateJarList |= entity.checkValidity(); 23344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 23444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 23544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet String filename = file.getName(); 23644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet List<JarEntity> nameList = nameMap.get(filename); 23744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet if (nameList == null) { 23844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet nameList = new ArrayList<JarEntity>(); 23944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet nameMap.put(filename, nameList); 24044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 24144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet nameList.add(entity); 24244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 24344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 24444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet try { 24544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet // now look for dups. Each name list can have more than one file but they must 24644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet // have the same size/sha1 24744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet for (Entry<String, List<JarEntity>> entry : nameMap.entrySet()) { 24844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet List<JarEntity> list = entry.getValue(); 24944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet checkEntities(entry.getKey(), list); 25044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 25144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet // if we are here, there's no issue. Add the first of the list to the results. 25244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet results.add(list.get(0).getFile()); 25344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 25444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 25544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet // special case for android-support-v4/13 25644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet checkSupportLibs(nameMap, results); 25744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } finally { 25844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet if (updateJarList) { 25944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet writeJarList(nameMap); 26044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 26144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 26244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 26344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet return results; 26444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 26544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 26644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet /** 26744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * Checks whether a given list of duplicates can be replaced by a single one. 26844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * @param filename the filename of the files 26944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * @param list the list of dup files 27044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * @throws DifferentLibException 27144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * @throws Sha1Exception 27244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet */ 27344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet private void checkEntities(String filename, List<JarEntity> list) 27444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet throws DifferentLibException, Sha1Exception { 27544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet if (list.size() == 1) { 27644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet return; 27744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 27844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 27944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet JarEntity baseEntity = list.get(0); 28044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet long baseLength = baseEntity.getLength(); 28144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet String baseSha1 = baseEntity.getSha1(); 28244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 28344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet final int count = list.size(); 28444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet for (int i = 1; i < count ; i++) { 28544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet JarEntity entity = list.get(i); 28644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet if (entity.getLength() != baseLength || entity.getSha1().equals(baseSha1) == false) { 28781c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet throw new DifferentLibException("Jar mismatch! Fix your dependencies", 28881c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet getEntityDetails(filename, list)); 28944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 29044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 29144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 29244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 29344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 29444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet /** 29544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * Checks for present of both support libraries in v4 and v13. If both are detected, 29644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * v4 is removed from <var>results</var> 29744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * @param nameMap the list of jar as a map of (filename, list of files). 29844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * @param results the current list of jar file set to be used. it's already been cleaned of 29944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * duplicates. 30044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet */ 30144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet private void checkSupportLibs(Map<String, List<JarEntity>> nameMap, List<File> results) { 30244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet List<JarEntity> v4 = nameMap.get("android-support-v4.jar"); 30344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet List<JarEntity> v13 = nameMap.get("android-support-v13.jar"); 30444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 30544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet if (v13 != null && v4 != null) { 30681c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet mOutStream.println("WARNING: Found both android-support-v4 and android-support-v13 in the dependency list."); 30781c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet mOutStream.println("Because v13 includes v4, using only v13."); 30844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet results.remove(v4.get(0).getFile()); 30944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 31044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 31144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 31244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet private Map<String, JarEntity> getCachedJarList() { 31344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet Map<String, JarEntity> cache = new HashMap<String, JarListSanitizer.JarEntity>(); 31444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 31544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet File cacheFile = new File(mOut, CACHE_FILENAME); 31644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet if (cacheFile.exists() == false) { 31744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet return cache; 31844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 31944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 32044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet BufferedReader reader = null; 32144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet try { 32244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet reader = new BufferedReader(new InputStreamReader(new FileInputStream(cacheFile), 32344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet "UTF-8")); 32444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 32544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet String line = null; 32644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet while ((line = reader.readLine()) != null) { 32744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet // skip comments 32844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet if (line.charAt(0) == '#') { 32944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet continue; 33044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 33144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 33244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet // get the data with a regexp 33344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet Matcher m = READ_PATTERN.matcher(line); 33444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet if (m.matches()) { 33544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet String path = m.group(4); 33644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 33744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet JarEntity entity = new JarEntity( 33844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet path, 33944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet Long.parseLong(m.group(1)), 34044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet Long.parseLong(m.group(2)), 34144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet m.group(3)); 34244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 34344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet cache.put(path, entity); 34444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 34544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 34644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 34744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } catch (FileNotFoundException e) { 34844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet // won't happen, we check up front. 34944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } catch (UnsupportedEncodingException e) { 35044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet // shouldn't happen, but if it does, we just won't have a cache. 35144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } catch (IOException e) { 35244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet // shouldn't happen, but if it does, we just won't have a cache. 35344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } finally { 35444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet if (reader != null) { 35544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet try { 35644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet reader.close(); 35744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } catch (IOException e) { 35844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 35944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 36044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 36144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 36244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet return cache; 36344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 36444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 36544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet private void writeJarList(Map<String, List<JarEntity>> nameMap) { 36644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet File cacheFile = new File(mOut, CACHE_FILENAME); 36744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet try { 36844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet OutputStreamWriter writer = new OutputStreamWriter( 36944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet new FileOutputStream(cacheFile), "UTF-8"); 37044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 37144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet writer.write("# cache for current jar dependecy. DO NOT EDIT.\n"); 37244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet writer.write("# format is <lastModified> <length> <SHA-1> <path>\n"); 37344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet writer.write("# Encoding is UTF-8\n"); 37444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 37544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet for (List<JarEntity> list : nameMap.values()) { 37644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet // clean up the list of files that don't have a sha1. 37744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet for (int i = 0 ; i < list.size() ; ) { 37844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet JarEntity entity = list.get(i); 37944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet if (entity.hasSha1()) { 38044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet i++; 38144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } else { 38244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet list.remove(i); 38344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 38444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 38544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 38644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet if (list.size() > 1) { 38744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet for (JarEntity entity : list) { 38844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet writer.write(String.format("%d %d %s %s\n", 38944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet entity.getLastModified(), 39044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet entity.getLength(), 39144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet entity.getSha1(), 39244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet entity.getFile().getAbsolutePath())); 39344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 39444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 39544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 39644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 39744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet writer.close(); 39844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } catch (IOException e) { 39981c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet mOutStream.println("WARNING: unable to write jarlist cache file " + 40044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet cacheFile.getAbsolutePath()); 40144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } catch (Sha1Exception e) { 40244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet // shouldn't happen here since we check that the sha1 is present first, meaning it's 40344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet // already been computing. 40444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 40544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 40644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 40781c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet private String[] getEntityDetails(String filename, List<JarEntity> list) throws Sha1Exception { 40881c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet ArrayList<String> result = new ArrayList<String>(); 40981c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet result.add( 41044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet String.format("Found %d versions of %s in the dependency list,", 41144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet list.size(), filename)); 41281c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet result.add("but not all the versions are identical (check is based on SHA-1 only at this time)."); 41381c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet result.add("All versions of the libraries must be the same at this time."); 41481c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet result.add("Versions found are:"); 41544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet for (JarEntity entity : list) { 41681c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet result.add("Path: " + entity.getFile().getAbsolutePath()); 41781c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet result.add("\tLength: " + entity.getLength()); 41881c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet result.add("\tSHA-1: " + entity.getSha1()); 41944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 42081c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet 42181c5fb5448a6342cb3bb29ea501fccf95573288cXavier Ducrohet return result.toArray(new String[result.size()]); 42244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 42344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 42444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet /** 42544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * Computes the sha1 of a file and returns it. 42644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * @param f the file to compute the sha1 for. 42744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * @return the sha1 value 42844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet * @throws Sha1Exception if the sha1 value cannot be computed. 42944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet */ 43044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet private static String getSha1(File f) throws Sha1Exception { 43144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet synchronized (sBuffer) { 43244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet try { 43344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet MessageDigest md = MessageDigest.getInstance("SHA-1"); 43444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 43544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet FileInputStream fis = new FileInputStream(f); 43644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet while (true) { 43744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet int length = fis.read(sBuffer); 43844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet if (length > 0) { 43944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet md.update(sBuffer, 0, length); 44044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } else { 44144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet break; 44244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 44344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 44444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 44544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet return byteArray2Hex(md.digest()); 44644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 44744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } catch (Exception e) { 44844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet throw new Sha1Exception(f, e); 44944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 45044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 45144a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 45244a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet 45344a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet private static String byteArray2Hex(final byte[] hash) { 45444a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet Formatter formatter = new Formatter(); 45544a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet for (byte b : hash) { 45644a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet formatter.format("%02x", b); 45744a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 45844a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet return formatter.toString(); 45944a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet } 46044a21c237072fba5454732c74d1f7653523c4105Xavier Ducrohet} 461