1 /* 2 * Copyright (C) 2017 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 17 package libcore; 18 19 import java.io.File; 20 import java.io.IOException; 21 import java.nio.file.Path; 22 import java.nio.file.Paths; 23 import java.util.ArrayList; 24 import java.util.Arrays; 25 import java.util.Collections; 26 import java.util.HashMap; 27 import java.util.List; 28 import java.util.Map; 29 import java.util.Objects; 30 import java.util.Set; 31 import java.util.TreeSet; 32 import java.util.regex.Matcher; 33 import java.util.regex.Pattern; 34 35 /** 36 * A set of .java files (either from ojluni or from an upstream). 37 */ 38 abstract class Repository { 39 40 /** 41 * Maps from a file's (current) relPath to the corresponding OpenJDK relPath from 42 * which it has been, and still remains, renamed. 43 */ 44 static final Map<Path, Path> OPENJDK_REL_PATH = historicRenames(); 45 historicRenames()46 static Map<Path, Path> historicRenames() { 47 Map<Path, Path> result = new HashMap<>(); 48 // renamed in libcore commit 583eb0e4738456f0547014a4857a14456be267ee 49 result.put(Paths.get("native/linux_close.cpp"), Paths.get("native/linux_close.c")); 50 // Map ByteBufferAs*Buffer.java to an upstream file, even though there is 51 // not a 1:1 correspondence. This isn't perfect, but allows some rough 52 // comparison. See http://b/111583940 53 // 54 // More detail: 55 // The RI has four different generated files ...Buffer{B,L,RB,RL}.java 56 // for each of these six files specializing on big endian, little endian, 57 // read-only big endian, and read-only little endian, respectively. Those 58 // 6 x 4 files are generated from a single template: 59 // java/nio/ByteBufferAs-X-Buffer.java.template 60 // 61 // On Android, the four variants {B,L,RB,RL} for each of the six types 62 // are folded into a single class with behavior configured via additional 63 // constructor arguments. 64 // 65 // For now, we map to upstream's "B" variant; "B" is more similar to 66 // Android's files than "RB" or "RL"; the choice of "B" vs. "L" is arbitrary. 67 for (String s : Arrays.asList("Char", "Double", "Float", "Int", "Long", "Short")) { 68 Path ojluniPath = Paths.get("java/nio/ByteBufferAs" + s + "Buffer.java"); 69 Path upstreamPath = 70 Paths.get("java/nio/ByteBufferAs" + s + "BufferB.java"); 71 result.put(ojluniPath, upstreamPath); 72 } 73 return Collections.unmodifiableMap(result); 74 } 75 76 protected final Path rootPath; 77 protected final String name; 78 protected final List<String> sourceDirs; 79 Repository(Path rootPath, String name, List<String> sourceDirs)80 protected Repository(Path rootPath, String name, List<String> sourceDirs) { 81 this.rootPath = Objects.requireNonNull(rootPath); 82 this.name = Objects.requireNonNull(name); 83 this.sourceDirs = Objects.requireNonNull(sourceDirs); 84 if (!rootPath.toFile().isDirectory()) { 85 throw new IllegalArgumentException("Missing or not a directory: " + rootPath); 86 } 87 } 88 89 /** 90 * @param relPath a relative path of a .java file in the repository, e.g. 91 * "java/util/ArrayList.java". 92 * @return the path of the indicated file (either absolute, or relative to the current 93 * working directory), or null if the file does not exist in this Repository. 94 */ absolutePath(Path relPath)95 public final Path absolutePath(Path relPath) { 96 Path p = pathFromRepository(relPath); 97 return p == null ? null : rootPath.resolve(p).toAbsolutePath(); 98 } 99 pathFromRepository(Path relPath)100 public Path pathFromRepository(Path relPath) { 101 // Search across all sourceDirs for the indicated file. 102 for (String sourceDir : sourceDirs) { 103 Path repositoryRelativePath = Paths.get(sourceDir).resolve(relPath); 104 File file = rootPath.resolve(repositoryRelativePath).toFile(); 105 if (file.exists()) { 106 return repositoryRelativePath; 107 } 108 } 109 return null; 110 } 111 rootPath()112 public final Path rootPath() { 113 return rootPath; 114 } 115 116 @Override hashCode()117 public int hashCode() { 118 return rootPath.hashCode(); 119 } 120 121 @Override equals(Object obj)122 public boolean equals(Object obj) { 123 return (obj instanceof Repository) && rootPath.equals(((Repository) obj).rootPath); 124 } 125 126 /** 127 * @return A human readable name to identify this repository, suitable for use as a 128 * directory name. 129 */ name()130 public final String name() { 131 return name; 132 } 133 134 @Override toString()135 public String toString() { 136 return name() + " repository"; 137 } 138 139 /** 140 * A checkout of the hg repository of OpenJDK 9 or higher, located in the 141 * subdirectory {@code upstreamName} under the directory {@code upstreamRoot}. 142 */ openJdk9(Path upstreamRoot, String upstreamName)143 public static Repository openJdk9(Path upstreamRoot, String upstreamName) { 144 List<String> sourceDirs = Arrays.asList( 145 "jdk/src/java.base/share/classes", 146 "jdk/src/java.logging/share/classes", 147 "jdk/src/java.prefs/share/classes", 148 "jdk/src/java.sql/share/classes", 149 "jdk/src/java.desktop/share/classes", 150 "jdk/src/java.base/solaris/classes", 151 "jdk/src/java.base/unix/classes", 152 "jdk/src/java.prefs/unix/classes", 153 "jdk/src/jdk.unsupported/share/classes", 154 "jdk/src/jdk.net/share/classes", 155 "jdk/src/java.base/linux/classes", 156 "build/linux-x86_64-normal-server-release/support/gensrc/java.base", 157 158 // Native (.c) files 159 "jdk/src/java.base/unix/native/libjava", 160 "jdk/src/java.base/share/native/libjava", 161 "jdk/src/java.base/unix/native/libnio", 162 "jdk/src/java.base/unix/native/libnio/ch", 163 "jdk/src/java.base/unix/native/libnio/fs", 164 "jdk/src/java.base/unix/native/libnet" 165 ); 166 return new OpenJdkRepository(upstreamRoot, upstreamName, sourceDirs); 167 } 168 169 /** 170 * A checkout of the hg repository of OpenJDK 11 or higher, located in the 171 * subdirectory {@code upstreamName} under the directory {@code upstreamRoot}. 172 */ openJdk11(Path upstreamRoot, String upstreamName)173 public static Repository openJdk11(Path upstreamRoot, String upstreamName) { 174 List<String> sourceDirs = Arrays.asList( 175 "src/java.base/share/classes", 176 "src/java.logging/share/classes", 177 "src/java.prefs/share/classes", 178 "src/java.sql/share/classes", 179 "src/java.desktop/share/classes", 180 "src/java.base/solaris/classes", 181 "src/java.base/unix/classes", 182 "src/java.prefs/unix/classes", 183 "src/jdk.unsupported/share/classes", 184 "src/jdk.net/share/classes", 185 "src/java.base/linux/classes", 186 "build/linux-x86_64-normal-server-release/support/gensrc/java.base", 187 188 // Native (.c) files 189 "src/java.base/unix/native/libjava", 190 "src/java.base/share/native/libjava", 191 "src/java.base/unix/native/libnio", 192 "src/java.base/unix/native/libnio/ch", 193 "src/java.base/unix/native/libnio/fs", 194 "src/java.base/unix/native/libnet" 195 ); 196 return new OpenJdkRepository(upstreamRoot, upstreamName, sourceDirs); 197 } 198 199 /** 200 * A checkout of the hg repository of OpenJDK 8 or earlier, located in the 201 * subdirectory {@code upstreamName} under the directory {@code upstreamRoot}. 202 */ openJdkLegacy(Path upstreamRoot, String upstreamName)203 public static Repository openJdkLegacy(Path upstreamRoot, String upstreamName) { 204 List<String> sourceDirs = new ArrayList<>(); 205 sourceDirs.addAll(Arrays.asList( 206 "jdk/src/share/classes", 207 "jdk/src/solaris/classes", 208 "build/linux-x86_64-normal-server-release/jdk/gensrc" 209 )); 210 211 // In legacy OpenJDK versions, the source files are organized into a subfolder 212 // hierarchy based on package name, whereas in Android and OpenJDK 9+ they're in 213 // a flat folder. We work around this by just searching through all of the 214 // applicable folders (from which we have sources) in legacy OpenJDK versions. 215 List<String> nativeSourceDirs = new ArrayList<>(); 216 List<String> pkgPaths = Arrays.asList("", "java/io", "java/lang", "java/net", "java/nio", 217 "java/util", "java/util/zip", "sun/nio/ch", "sun/nio/fs"); 218 for (String pkgPath : pkgPaths) { 219 nativeSourceDirs.add("jdk/src/solaris/native/" + pkgPath); 220 nativeSourceDirs.add("jdk/src/share/native/" + pkgPath); 221 nativeSourceDirs.add("jdk/src/solaris/native/common/" + pkgPath); 222 nativeSourceDirs.add("jdk/src/share/native/common/" + pkgPath); 223 } 224 sourceDirs.addAll(nativeSourceDirs); 225 226 return new OpenJdkRepository(upstreamRoot, upstreamName, sourceDirs); 227 } 228 229 /** 230 * Checkouts of hg repositories of OpenJDK 8 or earlier, located in the 231 * respective {@code upstreamNames} subdirectories under the join parent 232 * directory {@code upstreamRoot}. 233 */ openJdkLegacy(Path upstreamRoot, List<String> upstreamNames)234 public static List<Repository> openJdkLegacy(Path upstreamRoot, List<String> upstreamNames) { 235 List<Repository> result = new ArrayList<>(); 236 for (String upstreamName : upstreamNames) { 237 result.add(openJdkLegacy(upstreamRoot, upstreamName)); 238 } 239 return Collections.unmodifiableList(result); 240 } 241 242 static class OjluniRepository extends Repository { 243 /** 244 * The repository of ojluni java files belonging to the Android sources under 245 * {@code buildTop}. 246 * 247 * @param buildTop The root path of an Android checkout, as identified by the 248 * {@quote ANDROID_BUILD_TOP} environment variable. 249 */ OjluniRepository(Path buildTop)250 public OjluniRepository(Path buildTop) { 251 super(buildTop.resolve("libcore"), "ojluni", 252 /* sourceDirs */ Arrays.asList("ojluni/src/main/java", "ojluni/src/main/native")); 253 } 254 255 256 @Override pathFromRepository(Path relPath)257 public Path pathFromRepository(Path relPath) { 258 // Enforce that the file exists in ojluni 259 return Objects.requireNonNull(super.pathFromRepository(relPath)); 260 } 261 262 /** 263 * Returns the list of relative paths to files parsed from blueprint files. 264 */ loadRelPathsFromBlueprint()265 public List<Path> loadRelPathsFromBlueprint() throws IOException { 266 List<Path> result = new ArrayList<>(); 267 result.addAll(loadOrderedRelPathsSetFromBlueprint( 268 "openjdk_java_files.bp", "\"ojluni/src/main/java/(.+\\.java)\"")); 269 result.addAll(loadOrderedRelPathsSetFromBlueprint( 270 "ojluni/src/main/native/Android.bp", "\\s+\"(.+\\.(?:c|cpp))\",")); 271 return result; 272 } 273 loadOrderedRelPathsSetFromBlueprint( String blueprintPathString, String patternString)274 private Set<Path> loadOrderedRelPathsSetFromBlueprint( 275 String blueprintPathString, String patternString) throws IOException { 276 Path blueprintPath = rootPath.resolve(blueprintPathString); 277 Pattern pattern = Pattern.compile(patternString); 278 // Use TreeSet to sort and de-duplicate the result. 279 Set<Path> result = new TreeSet<>(); 280 for (String line : Util.readLines(blueprintPath)) { 281 Matcher matcher = pattern.matcher(line); 282 while (matcher.find()) { 283 Path relPath = Paths.get(matcher.group(1)); 284 result.add(relPath); 285 } 286 } 287 return result; 288 } 289 290 @Override toString()291 public String toString() { 292 return "libcore ojluni"; 293 } 294 } 295 296 static class OpenJdkRepository extends Repository { 297 OpenJdkRepository(Path upstreamRoot, String name, List<String> sourceDirs)298 public OpenJdkRepository(Path upstreamRoot, String name, List<String> sourceDirs) { 299 super(upstreamRoot.resolve(name), name, sourceDirs); 300 } 301 302 @Override pathFromRepository(Path relPath)303 public Path pathFromRepository(Path relPath) { 304 if (OPENJDK_REL_PATH.containsKey(relPath)) { 305 relPath = OPENJDK_REL_PATH.get(relPath); 306 } 307 return super.pathFromRepository(relPath); 308 } 309 310 @Override toString()311 public String toString() { 312 return "OpenJDK " + name; 313 } 314 } 315 316 } 317