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 8 or earlier, located in the 171 * subdirectory {@code upstreamName} under the directory {@code upstreamRoot}. 172 */ openJdkLegacy(Path upstreamRoot, String upstreamName)173 public static Repository openJdkLegacy(Path upstreamRoot, String upstreamName) { 174 List<String> sourceDirs = new ArrayList<>(); 175 sourceDirs.addAll(Arrays.asList( 176 "jdk/src/share/classes", 177 "jdk/src/solaris/classes", 178 "build/linux-x86_64-normal-server-release/jdk/gensrc" 179 )); 180 181 // In legacy OpenJDK versions, the source files are organized into a subfolder 182 // hierarchy based on package name, whereas in Android and OpenJDK 9+ they're in 183 // a flat folder. We work around this by just searching through all of the 184 // applicable folders (from which we have sources) in legacy OpenJDK versions. 185 List<String> nativeSourceDirs = new ArrayList<>(); 186 List<String> pkgPaths = Arrays.asList("", "java/io", "java/lang", "java/net", "java/nio", 187 "java/util", "java/util/zip", "sun/nio/ch", "sun/nio/fs"); 188 for (String pkgPath : pkgPaths) { 189 nativeSourceDirs.add("jdk/src/solaris/native/" + pkgPath); 190 nativeSourceDirs.add("jdk/src/share/native/" + pkgPath); 191 nativeSourceDirs.add("jdk/src/solaris/native/common/" + pkgPath); 192 nativeSourceDirs.add("jdk/src/share/native/common/" + pkgPath); 193 } 194 sourceDirs.addAll(nativeSourceDirs); 195 196 return new OpenJdkRepository(upstreamRoot, upstreamName, sourceDirs); 197 } 198 199 /** 200 * Checkouts of hg repositories of OpenJDK 8 or earlier, located in the 201 * respective {@code upstreamNames} subdirectories under the join parent 202 * directory {@code upstreamRoot}. 203 */ openJdkLegacy(Path upstreamRoot, List<String> upstreamNames)204 public static List<Repository> openJdkLegacy(Path upstreamRoot, List<String> upstreamNames) { 205 List<Repository> result = new ArrayList<>(); 206 for (String upstreamName : upstreamNames) { 207 result.add(openJdkLegacy(upstreamRoot, upstreamName)); 208 } 209 return Collections.unmodifiableList(result); 210 } 211 212 static class OjluniRepository extends Repository { 213 /** 214 * The repository of ojluni java files belonging to the Android sources under 215 * {@code buildTop}. 216 * 217 * @param buildTop The root path of an Android checkout, as identified by the 218 * {@quote ANDROID_BUILD_TOP} environment variable. 219 */ OjluniRepository(Path buildTop)220 public OjluniRepository(Path buildTop) { 221 super(buildTop.resolve("libcore"), "ojluni", 222 /* sourceDirs */ Arrays.asList("ojluni/src/main/java", "ojluni/src/main/native")); 223 } 224 225 226 @Override pathFromRepository(Path relPath)227 public Path pathFromRepository(Path relPath) { 228 // Enforce that the file exists in ojluni 229 return Objects.requireNonNull(super.pathFromRepository(relPath)); 230 } 231 232 /** 233 * Returns the list of relative paths to files parsed from blueprint files. 234 */ loadRelPathsFromBlueprint()235 public List<Path> loadRelPathsFromBlueprint() throws IOException { 236 List<Path> result = new ArrayList<>(); 237 result.addAll(loadOrderedRelPathsSetFromBlueprint( 238 "openjdk_java_files.bp", "\"ojluni/src/main/java/(.+\\.java)\"")); 239 result.addAll(loadOrderedRelPathsSetFromBlueprint( 240 "ojluni/src/main/native/Android.bp", "\\s+\"(.+\\.(?:c|cpp))\",")); 241 return result; 242 } 243 loadOrderedRelPathsSetFromBlueprint( String blueprintPathString, String patternString)244 private Set<Path> loadOrderedRelPathsSetFromBlueprint( 245 String blueprintPathString, String patternString) throws IOException { 246 Path blueprintPath = rootPath.resolve(blueprintPathString); 247 Pattern pattern = Pattern.compile(patternString); 248 // Use TreeSet to sort and de-duplicate the result. 249 Set<Path> result = new TreeSet<>(); 250 for (String line : Util.readLines(blueprintPath)) { 251 Matcher matcher = pattern.matcher(line); 252 while (matcher.find()) { 253 Path relPath = Paths.get(matcher.group(1)); 254 result.add(relPath); 255 } 256 } 257 return result; 258 } 259 260 @Override toString()261 public String toString() { 262 return "libcore ojluni"; 263 } 264 } 265 266 static class OpenJdkRepository extends Repository { 267 OpenJdkRepository(Path upstreamRoot, String name, List<String> sourceDirs)268 public OpenJdkRepository(Path upstreamRoot, String name, List<String> sourceDirs) { 269 super(upstreamRoot.resolve(name), name, sourceDirs); 270 } 271 272 @Override pathFromRepository(Path relPath)273 public Path pathFromRepository(Path relPath) { 274 if (OPENJDK_REL_PATH.containsKey(relPath)) { 275 relPath = OPENJDK_REL_PATH.get(relPath); 276 } 277 return super.pathFromRepository(relPath); 278 } 279 280 @Override toString()281 public String toString() { 282 return "OpenJDK " + name; 283 } 284 } 285 286 } 287