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