1 /* 2 * Copyright (C) 2011 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 util.build; 18 19 import java.io.File; 20 import java.io.IOException; 21 import java.util.ArrayList; 22 import java.util.Collection; 23 import java.util.Collections; 24 import java.util.Comparator; 25 import java.util.List; 26 import java.util.Set; 27 import java.util.TreeSet; 28 29 /** 30 * Main class to generate data from the test suite to later run from a shell 31 * script. the project's home folder.<br> 32 * <project-home>/src must contain the java sources<br> 33 * <project-home>/src/<for-each-package>/Main_testN1.java will be generated<br> 34 * (one Main class for each test method in the Test_... class 35 */ 36 public class BuildDalvikSuite extends BuildUtilBase { 37 38 public static final String TARGET_MAIN_FILE = "mains.jar"; 39 40 // the folder for the generated junit-files for the cts host (which in turn 41 // execute the real vm tests using adb push/shell etc) 42 private String OUTPUT_FOLDER = ""; 43 private String COMPILED_CLASSES_FOLDER = ""; 44 45 private String JAVASRC_FOLDER; 46 47 /** 48 * @param args 49 * args 0 must be the project root folder (where src, lib etc. 50 * resides) 51 * @throws IOException 52 */ main(String[] args)53 public static void main(String[] args) throws IOException { 54 BuildDalvikSuite cat = new BuildDalvikSuite(); 55 if (!cat.parseArgs(args)) { 56 printUsage(); 57 System.exit(-1); 58 } 59 60 long start = System.currentTimeMillis(); 61 cat.run(null); 62 long end = System.currentTimeMillis(); 63 64 System.out.println("elapsed seconds: " + (end - start) / 1000); 65 } 66 parseArgs(String[] args)67 private boolean parseArgs(String[] args) { 68 if (args.length == 3) { 69 JAVASRC_FOLDER = args[0]; 70 OUTPUT_FOLDER = args[1]; 71 COMPILED_CLASSES_FOLDER = args[2]; 72 return true; 73 } else { 74 return false; 75 } 76 } 77 printUsage()78 private static void printUsage() { 79 System.out.println("usage: $(JAVA) -cp $(CLASSPATH) util.build.BuildDalvikSuite" + 80 " JAVASRC_FOLDER OUTPUT_FOLDER COMPILED_CLASSES_FOLDER"); 81 } 82 83 class MyTestHandler implements TestHandler { 84 public String datafileContent = ""; 85 Set<BuildStep> targets = new TreeSet<BuildStep>(); 86 87 @Override handleTest(String fqcn, List<String> methods)88 public void handleTest(String fqcn, List<String> methods) { 89 int lastDotPos = fqcn.lastIndexOf('.'); 90 String pName = fqcn.substring(0, lastDotPos); 91 String classOnlyName = fqcn.substring(lastDotPos + 1); 92 93 Collections.sort(methods, new Comparator<String>() { 94 @Override 95 public int compare(String s1, String s2) { 96 // TODO sort according: test ... N, B, E, VFE 97 return s1.compareTo(s2); 98 } 99 }); 100 for (String method : methods) { 101 // e.g. testN1 102 if (!method.startsWith("test")) { 103 throw new RuntimeException("no test method: " + method); 104 } 105 106 // generate the Main_xx java class 107 108 // a Main_testXXX.java contains: 109 // package <packagenamehere>; 110 // public class Main_testxxx { 111 // public static void main(String[] args) { 112 // new dxc.junit.opcodes.aaload.Test_aaload().testN1(); 113 // } 114 // } 115 MethodData md = parseTestMethod(pName, classOnlyName, method); 116 String methodContent = md.methodBody; 117 118 List<String> dependentTestClassNames = parseTestClassName(pName, 119 classOnlyName, methodContent); 120 121 if (dependentTestClassNames.isEmpty()) { 122 continue; 123 } 124 125 // prepare the entry in the data file for the bash script. 126 // e.g. 127 // main class to execute; opcode/constraint; test purpose 128 // dxc.junit.opcodes.aaload.Main_testN1;aaload;normal case test 129 // (#1) 130 131 char ca = method.charAt("test".length()); // either N,B,E, 132 // or V (VFE) 133 String comment; 134 switch (ca) { 135 case 'N': 136 comment = "Normal #" + method.substring(5); 137 break; 138 case 'B': 139 comment = "Boundary #" + method.substring(5); 140 break; 141 case 'E': 142 comment = "Exception #" + method.substring(5); 143 break; 144 case 'V': 145 comment = "Verifier #" + method.substring(7); 146 break; 147 default: 148 throw new RuntimeException("unknown test abbreviation:" 149 + method + " for " + fqcn); 150 } 151 152 String line = pName + ".Main_" + method + ";"; 153 for (String className : dependentTestClassNames) { 154 line += className + " "; 155 } 156 157 158 // test description 159 String[] pparts = pName.split("\\."); 160 // detail e.g. add_double 161 String detail = pparts[pparts.length-1]; 162 // type := opcode | verify 163 String type = pparts[pparts.length-2]; 164 165 String description; 166 if ("format".equals(type)) { 167 description = "format"; 168 } else if ("opcodes".equals(type)) { 169 // Beautify name, so it matches the actual mnemonic 170 detail = detail.replaceAll("_", "-"); 171 detail = detail.replace("-from16", "/from16"); 172 detail = detail.replace("-high16", "/high16"); 173 detail = detail.replace("-lit8", "/lit8"); 174 detail = detail.replace("-lit16", "/lit16"); 175 detail = detail.replace("-4", "/4"); 176 detail = detail.replace("-16", "/16"); 177 detail = detail.replace("-32", "/32"); 178 detail = detail.replace("-jumbo", "/jumbo"); 179 detail = detail.replace("-range", "/range"); 180 detail = detail.replace("-2addr", "/2addr"); 181 182 // Unescape reserved words 183 detail = detail.replace("opc-", ""); 184 185 description = detail; 186 } else if ("verify".equals(type)) { 187 description = "verifier"; 188 } else { 189 description = type + " " + detail; 190 } 191 192 String details = (md.title != null ? md.title : ""); 193 if (md.constraint != null) { 194 details = " Constraint " + md.constraint + ", " + details; 195 } 196 if (details.length() != 0) { 197 details = details.substring(0, 1).toUpperCase() 198 + details.substring(1); 199 } 200 201 line += ";" + description + ";" + comment + ";" + details; 202 203 datafileContent += line + "\n"; 204 generateBuildStepFor(dependentTestClassNames, targets); 205 } 206 } 207 } 208 209 @Override handleTests(JUnitTestCollector tests, TestHandler ignored)210 protected void handleTests(JUnitTestCollector tests, TestHandler ignored) { 211 MyTestHandler handler = new MyTestHandler(); 212 super.handleTests(tests, handler); 213 214 File scriptDataDir = new File(OUTPUT_FOLDER + "/data/"); 215 scriptDataDir.mkdirs(); 216 writeToFile(new File(scriptDataDir, "scriptdata"), handler.datafileContent); 217 218 for (BuildStep buildStep : handler.targets) { 219 if (!buildStep.build()) { 220 System.out.println("building failed. buildStep: " + 221 buildStep.getClass().getName() + ", " + buildStep); 222 System.exit(1); 223 } 224 } 225 } 226 generateBuildStepFor(Collection<String> dependentTestClassNames, Set<BuildStep> targets)227 private void generateBuildStepFor(Collection<String> dependentTestClassNames, 228 Set<BuildStep> targets) { 229 for (String dependentTestClassName : dependentTestClassNames) { 230 generateBuildStepForDependant(dependentTestClassName, targets); 231 } 232 } 233 generateBuildStepForDependant(String dependentTestClassName, Set<BuildStep> targets)234 private void generateBuildStepForDependant(String dependentTestClassName, 235 Set<BuildStep> targets) { 236 237 File sourceFolder = new File(JAVASRC_FOLDER); 238 String fileName = dependentTestClassName.replace('.', '/').trim(); 239 240 if (new File(sourceFolder, fileName + ".dfh").exists()) { 241 // Handled in vmtests-dfh-dex-generated build rule. 242 return; 243 } 244 245 if (new File(sourceFolder, fileName + ".d").exists()) { 246 // Handled in vmtests-dasm-dex-generated build rule. 247 return; 248 } 249 250 { 251 // Build dex from a single '*.smali' file or a *.smalis' dir 252 // containing multiple smali files. 253 File dexFile = null; 254 SmaliBuildStep buildStep = null; 255 File smaliFile = new File(sourceFolder, fileName + ".smali"); 256 File smalisDir = new File(sourceFolder, fileName + ".smalis"); 257 258 if (smaliFile.exists()) { 259 dexFile = new File(OUTPUT_FOLDER, fileName + ".dex"); 260 buildStep = new SmaliBuildStep( 261 Collections.singletonList(smaliFile.getAbsolutePath()), dexFile); 262 } else if (smalisDir.exists() && smalisDir.isDirectory()) { 263 List<String> inputFiles = new ArrayList<>(); 264 for (File f: smalisDir.listFiles()) { 265 inputFiles.add(f.getAbsolutePath()); 266 } 267 dexFile = new File(OUTPUT_FOLDER, fileName + ".dex"); 268 buildStep = new SmaliBuildStep(inputFiles, dexFile); 269 } 270 271 if (buildStep != null) { 272 BuildStep.BuildFile jarFile = new BuildStep.BuildFile( 273 OUTPUT_FOLDER, fileName + ".jar"); 274 JarBuildStep jarBuildStep = new JarBuildStep(new BuildStep.BuildFile(dexFile), 275 "classes.dex", jarFile, true); 276 jarBuildStep.addChild(buildStep); 277 targets.add(jarBuildStep); 278 return; 279 } 280 } 281 282 File srcFile = new File(sourceFolder, fileName + ".java"); 283 if (srcFile.exists()) { 284 BuildStep dexBuildStep; 285 dexBuildStep = generateDexBuildStep( 286 COMPILED_CLASSES_FOLDER, fileName); 287 targets.add(dexBuildStep); 288 return; 289 } 290 291 try { 292 if (Class.forName(dependentTestClassName) != null) { 293 BuildStep dexBuildStep = generateDexBuildStep( 294 COMPILED_CLASSES_FOLDER, fileName); 295 targets.add(dexBuildStep); 296 return; 297 } 298 } catch (ClassNotFoundException e) { 299 // do nothing 300 } 301 302 throw new RuntimeException("neither .dfh,.d,.java file of dependant test class found : " + 303 dependentTestClassName + ";" + fileName); 304 } 305 generateDexBuildStep(String classFileFolder, String classFileName)306 private BuildStep generateDexBuildStep(String classFileFolder, 307 String classFileName) { 308 BuildStep.BuildFile classFile = new BuildStep.BuildFile( 309 classFileFolder, classFileName + ".class"); 310 311 BuildStep.BuildFile tmpJarFile = new BuildStep.BuildFile( 312 OUTPUT_FOLDER, 313 classFileName + "_tmp.jar"); 314 315 JarBuildStep jarBuildStep = new JarBuildStep(classFile, 316 classFileName + ".class", tmpJarFile, false); 317 318 BuildStep.BuildFile outputFile = new BuildStep.BuildFile( 319 OUTPUT_FOLDER, 320 classFileName + ".jar"); 321 322 D8BuildStep dexBuildStep = new D8BuildStep(tmpJarFile, 323 outputFile, 324 true); 325 326 dexBuildStep.addChild(jarBuildStep); 327 return dexBuildStep; 328 } 329 } 330