1*90c8c64dSAndroid Build Coastguard Worker /* 2*90c8c64dSAndroid Build Coastguard Worker * Copyright (C) 2008 The Android Open Source Project 3*90c8c64dSAndroid Build Coastguard Worker * 4*90c8c64dSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License"); 5*90c8c64dSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License. 6*90c8c64dSAndroid Build Coastguard Worker * You may obtain a copy of the License at 7*90c8c64dSAndroid Build Coastguard Worker * 8*90c8c64dSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0 9*90c8c64dSAndroid Build Coastguard Worker * 10*90c8c64dSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software 11*90c8c64dSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS, 12*90c8c64dSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*90c8c64dSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and 14*90c8c64dSAndroid Build Coastguard Worker * limitations under the License. 15*90c8c64dSAndroid Build Coastguard Worker */ 16*90c8c64dSAndroid Build Coastguard Worker 17*90c8c64dSAndroid Build Coastguard Worker import java.io.File; 18*90c8c64dSAndroid Build Coastguard Worker import java.io.IOException; 19*90c8c64dSAndroid Build Coastguard Worker import java.util.SortedSet; 20*90c8c64dSAndroid Build Coastguard Worker import java.util.TreeMap; 21*90c8c64dSAndroid Build Coastguard Worker import java.util.TreeSet; 22*90c8c64dSAndroid Build Coastguard Worker import java.util.Collection; 23*90c8c64dSAndroid Build Coastguard Worker import java.util.ArrayList; 24*90c8c64dSAndroid Build Coastguard Worker import java.util.List; 25*90c8c64dSAndroid Build Coastguard Worker import java.util.regex.Pattern; 26*90c8c64dSAndroid Build Coastguard Worker 27*90c8c64dSAndroid Build Coastguard Worker /** 28*90c8c64dSAndroid Build Coastguard Worker * Generates an Eclipse project. 29*90c8c64dSAndroid Build Coastguard Worker */ 30*90c8c64dSAndroid Build Coastguard Worker public class Eclipse { 31*90c8c64dSAndroid Build Coastguard Worker 32*90c8c64dSAndroid Build Coastguard Worker /** 33*90c8c64dSAndroid Build Coastguard Worker * Generates an Eclipse .classpath file from the given configuration. 34*90c8c64dSAndroid Build Coastguard Worker */ generateFrom(Configuration c)35*90c8c64dSAndroid Build Coastguard Worker public static void generateFrom(Configuration c) throws IOException { 36*90c8c64dSAndroid Build Coastguard Worker StringBuilder classpath = new StringBuilder(); 37*90c8c64dSAndroid Build Coastguard Worker 38*90c8c64dSAndroid Build Coastguard Worker classpath.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" 39*90c8c64dSAndroid Build Coastguard Worker + "<classpath>\n"); 40*90c8c64dSAndroid Build Coastguard Worker 41*90c8c64dSAndroid Build Coastguard Worker /* 42*90c8c64dSAndroid Build Coastguard Worker * If the user has a file named "path-precedence" in their project's 43*90c8c64dSAndroid Build Coastguard Worker * root directory, we'll order source roots based on how they match 44*90c8c64dSAndroid Build Coastguard Worker * regular expressions in that file. Source roots that match earlier 45*90c8c64dSAndroid Build Coastguard Worker * patterns will come sooner in configuration file. 46*90c8c64dSAndroid Build Coastguard Worker */ 47*90c8c64dSAndroid Build Coastguard Worker List<Pattern> patterns = new ArrayList<Pattern>(); 48*90c8c64dSAndroid Build Coastguard Worker 49*90c8c64dSAndroid Build Coastguard Worker File precedence = new File("path-precedence"); 50*90c8c64dSAndroid Build Coastguard Worker if (precedence.exists()) { 51*90c8c64dSAndroid Build Coastguard Worker Configuration.parseFile(precedence, patterns); 52*90c8c64dSAndroid Build Coastguard Worker } else { 53*90c8c64dSAndroid Build Coastguard Worker // Put ./out at the bottom by default. 54*90c8c64dSAndroid Build Coastguard Worker patterns.add(Pattern.compile("^(?!out/)")); 55*90c8c64dSAndroid Build Coastguard Worker } 56*90c8c64dSAndroid Build Coastguard Worker 57*90c8c64dSAndroid Build Coastguard Worker // Everything not matched by the user's precedence spec. 58*90c8c64dSAndroid Build Coastguard Worker patterns.add(Pattern.compile(".*")); 59*90c8c64dSAndroid Build Coastguard Worker 60*90c8c64dSAndroid Build Coastguard Worker 61*90c8c64dSAndroid Build Coastguard Worker List<Bucket> buckets = new ArrayList<Bucket>(patterns.size()); 62*90c8c64dSAndroid Build Coastguard Worker for (Pattern pattern : patterns) { 63*90c8c64dSAndroid Build Coastguard Worker buckets.add(new Bucket(pattern)); 64*90c8c64dSAndroid Build Coastguard Worker } 65*90c8c64dSAndroid Build Coastguard Worker 66*90c8c64dSAndroid Build Coastguard Worker // Put source roots in respective buckets. 67*90c8c64dSAndroid Build Coastguard Worker OUTER: for (File sourceRoot : c.sourceRoots) { 68*90c8c64dSAndroid Build Coastguard Worker // Trim preceding "./" from path. 69*90c8c64dSAndroid Build Coastguard Worker String path = sourceRoot.getPath().substring(2); 70*90c8c64dSAndroid Build Coastguard Worker 71*90c8c64dSAndroid Build Coastguard Worker for (Bucket bucket : buckets) { 72*90c8c64dSAndroid Build Coastguard Worker if (bucket.matches(path)) { 73*90c8c64dSAndroid Build Coastguard Worker bucket.sourceRoots.add(sourceRoot); 74*90c8c64dSAndroid Build Coastguard Worker continue OUTER; 75*90c8c64dSAndroid Build Coastguard Worker } 76*90c8c64dSAndroid Build Coastguard Worker } 77*90c8c64dSAndroid Build Coastguard Worker } 78*90c8c64dSAndroid Build Coastguard Worker 79*90c8c64dSAndroid Build Coastguard Worker // Output source roots to configuration file. 80*90c8c64dSAndroid Build Coastguard Worker for (Bucket bucket : buckets) { 81*90c8c64dSAndroid Build Coastguard Worker for (File sourceRoot : bucket.sourceRoots) { 82*90c8c64dSAndroid Build Coastguard Worker classpath.append(" <classpathentry kind=\"src\""); 83*90c8c64dSAndroid Build Coastguard Worker CharSequence excluding = constructExcluding(sourceRoot, c); 84*90c8c64dSAndroid Build Coastguard Worker if (excluding.length() > 0) { 85*90c8c64dSAndroid Build Coastguard Worker classpath.append(" excluding=\"") 86*90c8c64dSAndroid Build Coastguard Worker .append(excluding).append("\""); 87*90c8c64dSAndroid Build Coastguard Worker } 88*90c8c64dSAndroid Build Coastguard Worker classpath.append(" path=\"") 89*90c8c64dSAndroid Build Coastguard Worker .append(trimmed(sourceRoot)).append("\"/>\n"); 90*90c8c64dSAndroid Build Coastguard Worker } 91*90c8c64dSAndroid Build Coastguard Worker 92*90c8c64dSAndroid Build Coastguard Worker } 93*90c8c64dSAndroid Build Coastguard Worker 94*90c8c64dSAndroid Build Coastguard Worker // Output .jar entries. 95*90c8c64dSAndroid Build Coastguard Worker for (File jar : c.jarFiles) { 96*90c8c64dSAndroid Build Coastguard Worker classpath.append(" <classpathentry kind=\"lib\" path=\"") 97*90c8c64dSAndroid Build Coastguard Worker .append(trimmed(jar)).append("\"/>\n"); 98*90c8c64dSAndroid Build Coastguard Worker } 99*90c8c64dSAndroid Build Coastguard Worker 100*90c8c64dSAndroid Build Coastguard Worker /* 101*90c8c64dSAndroid Build Coastguard Worker * Output directory. Unfortunately, Eclipse forces us to put it 102*90c8c64dSAndroid Build Coastguard Worker * somewhere under the project directory. 103*90c8c64dSAndroid Build Coastguard Worker */ 104*90c8c64dSAndroid Build Coastguard Worker classpath.append(" <classpathentry kind=\"output\" path=\"" 105*90c8c64dSAndroid Build Coastguard Worker + "out/eclipse\"/>\n"); 106*90c8c64dSAndroid Build Coastguard Worker 107*90c8c64dSAndroid Build Coastguard Worker classpath.append("</classpath>\n"); 108*90c8c64dSAndroid Build Coastguard Worker 109*90c8c64dSAndroid Build Coastguard Worker Files.toFile(classpath.toString(), new File(".classpath")); 110*90c8c64dSAndroid Build Coastguard Worker } 111*90c8c64dSAndroid Build Coastguard Worker 112*90c8c64dSAndroid Build Coastguard Worker 113*90c8c64dSAndroid Build Coastguard Worker /** 114*90c8c64dSAndroid Build Coastguard Worker * Constructs the "excluding" argument for a given source root. 115*90c8c64dSAndroid Build Coastguard Worker */ constructExcluding(File sourceRoot, Configuration c)116*90c8c64dSAndroid Build Coastguard Worker private static CharSequence constructExcluding(File sourceRoot, 117*90c8c64dSAndroid Build Coastguard Worker Configuration c) { 118*90c8c64dSAndroid Build Coastguard Worker StringBuilder classpath = new StringBuilder(); 119*90c8c64dSAndroid Build Coastguard Worker String path = sourceRoot.getPath(); 120*90c8c64dSAndroid Build Coastguard Worker 121*90c8c64dSAndroid Build Coastguard Worker // Exclude nested source roots. 122*90c8c64dSAndroid Build Coastguard Worker SortedSet<File> nextRoots = c.sourceRoots.tailSet(sourceRoot); 123*90c8c64dSAndroid Build Coastguard Worker int count = 0; 124*90c8c64dSAndroid Build Coastguard Worker for (File nextRoot : nextRoots) { 125*90c8c64dSAndroid Build Coastguard Worker // The first root is this root. 126*90c8c64dSAndroid Build Coastguard Worker if (count == 0) { 127*90c8c64dSAndroid Build Coastguard Worker count++; 128*90c8c64dSAndroid Build Coastguard Worker continue; 129*90c8c64dSAndroid Build Coastguard Worker } 130*90c8c64dSAndroid Build Coastguard Worker 131*90c8c64dSAndroid Build Coastguard Worker String nextPath = nextRoot.getPath(); 132*90c8c64dSAndroid Build Coastguard Worker if (!nextPath.startsWith(path)) { 133*90c8c64dSAndroid Build Coastguard Worker break; 134*90c8c64dSAndroid Build Coastguard Worker } 135*90c8c64dSAndroid Build Coastguard Worker 136*90c8c64dSAndroid Build Coastguard Worker if (count > 1) { 137*90c8c64dSAndroid Build Coastguard Worker classpath.append('|'); 138*90c8c64dSAndroid Build Coastguard Worker } 139*90c8c64dSAndroid Build Coastguard Worker classpath.append(nextPath.substring(path.length() + 1)) 140*90c8c64dSAndroid Build Coastguard Worker .append('/'); 141*90c8c64dSAndroid Build Coastguard Worker 142*90c8c64dSAndroid Build Coastguard Worker count++; 143*90c8c64dSAndroid Build Coastguard Worker } 144*90c8c64dSAndroid Build Coastguard Worker 145*90c8c64dSAndroid Build Coastguard Worker // Exclude excluded directories under this source root. 146*90c8c64dSAndroid Build Coastguard Worker SortedSet<File> excludedDirs = c.excludedDirs.tailSet(sourceRoot); 147*90c8c64dSAndroid Build Coastguard Worker for (File excludedDir : excludedDirs) { 148*90c8c64dSAndroid Build Coastguard Worker String excludedPath = excludedDir.getPath(); 149*90c8c64dSAndroid Build Coastguard Worker if (!excludedPath.startsWith(path)) { 150*90c8c64dSAndroid Build Coastguard Worker break; 151*90c8c64dSAndroid Build Coastguard Worker } 152*90c8c64dSAndroid Build Coastguard Worker 153*90c8c64dSAndroid Build Coastguard Worker if (count > 1) { 154*90c8c64dSAndroid Build Coastguard Worker classpath.append('|'); 155*90c8c64dSAndroid Build Coastguard Worker } 156*90c8c64dSAndroid Build Coastguard Worker classpath.append(excludedPath.substring(path.length() + 1)) 157*90c8c64dSAndroid Build Coastguard Worker .append('/'); 158*90c8c64dSAndroid Build Coastguard Worker 159*90c8c64dSAndroid Build Coastguard Worker count++; 160*90c8c64dSAndroid Build Coastguard Worker } 161*90c8c64dSAndroid Build Coastguard Worker 162*90c8c64dSAndroid Build Coastguard Worker return classpath; 163*90c8c64dSAndroid Build Coastguard Worker } 164*90c8c64dSAndroid Build Coastguard Worker 165*90c8c64dSAndroid Build Coastguard Worker /** 166*90c8c64dSAndroid Build Coastguard Worker * Returns the trimmed path. 167*90c8c64dSAndroid Build Coastguard Worker */ trimmed(File file)168*90c8c64dSAndroid Build Coastguard Worker private static String trimmed(File file) { 169*90c8c64dSAndroid Build Coastguard Worker return file.getPath().substring(2); 170*90c8c64dSAndroid Build Coastguard Worker } 171*90c8c64dSAndroid Build Coastguard Worker 172*90c8c64dSAndroid Build Coastguard Worker /** 173*90c8c64dSAndroid Build Coastguard Worker * A precedence bucket for source roots. 174*90c8c64dSAndroid Build Coastguard Worker */ 175*90c8c64dSAndroid Build Coastguard Worker private static class Bucket { 176*90c8c64dSAndroid Build Coastguard Worker 177*90c8c64dSAndroid Build Coastguard Worker private final Pattern pattern; 178*90c8c64dSAndroid Build Coastguard Worker private final List<File> sourceRoots = new ArrayList<File>(); 179*90c8c64dSAndroid Build Coastguard Worker Bucket(Pattern pattern)180*90c8c64dSAndroid Build Coastguard Worker private Bucket(Pattern pattern) { 181*90c8c64dSAndroid Build Coastguard Worker this.pattern = pattern; 182*90c8c64dSAndroid Build Coastguard Worker } 183*90c8c64dSAndroid Build Coastguard Worker matches(String path)184*90c8c64dSAndroid Build Coastguard Worker private boolean matches(String path) { 185*90c8c64dSAndroid Build Coastguard Worker return pattern.matcher(path).find(); 186*90c8c64dSAndroid Build Coastguard Worker } 187*90c8c64dSAndroid Build Coastguard Worker } 188*90c8c64dSAndroid Build Coastguard Worker } 189