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