• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 com.android.dex.util.FileUtils;
20 
21 import dot.junit.AllTests;
22 
23 import junit.framework.TestCase;
24 import junit.framework.TestResult;
25 import junit.framework.TestSuite;
26 import junit.textui.TestRunner;
27 
28 import java.io.BufferedWriter;
29 import java.io.File;
30 import java.io.FileNotFoundException;
31 import java.io.FileOutputStream;
32 import java.io.FileReader;
33 import java.io.IOException;
34 import java.io.OutputStreamWriter;
35 import java.util.ArrayList;
36 import java.util.Collections;
37 import java.util.Comparator;
38 import java.util.HashSet;
39 import java.util.Iterator;
40 import java.util.LinkedHashMap;
41 import java.util.List;
42 import java.util.Scanner;
43 import java.util.Set;
44 import java.util.TreeSet;
45 import java.util.Map.Entry;
46 import java.util.regex.MatchResult;
47 import java.util.regex.Matcher;
48 import java.util.regex.Pattern;
49 
50 /**
51  * Main class to generate data from the test suite to later run from a shell
52  * script. the project's home folder.<br>
53  * <project-home>/src must contain the java sources<br>
54  * <project-home>/src/<for-each-package>/Main_testN1.java will be generated<br>
55  * (one Main class for each test method in the Test_... class
56  */
57 public class BuildDalvikSuite {
58 
59     public static boolean DEBUG = true;
60 
61     private static String JAVASRC_FOLDER = "";
62     private static String MAIN_SRC_OUTPUT_FOLDER = "";
63 
64     // the folder for the generated junit-files for the cts host (which in turn
65     // execute the real vm tests using adb push/shell etc)
66     private static String HOSTJUNIT_SRC_OUTPUT_FOLDER = "";
67     private static String OUTPUT_FOLDER = "";
68     private static String COMPILED_CLASSES_FOLDER = "";
69 
70     private static String CLASSES_OUTPUT_FOLDER = "";
71     private static String HOSTJUNIT_CLASSES_OUTPUT_FOLDER = "";
72 
73     private static String CLASS_PATH = "";
74 
75     private static String restrictTo = null; // e.g. restrict to "opcodes.add_double"
76 
77     private static final String TARGET_JAR_ROOT_PATH = "/data/local/tmp/vm-tests";
78 
79     private int testClassCnt = 0;
80     private int testMethodsCnt = 0;
81 
82     /*
83      * using a linked hashmap to keep the insertion order for iterators.
84      * the junit suite/tests adding order is used to generate the order of the
85      * report.
86      * a map. key: fully qualified class name, value: a list of test methods for
87      * the given class
88      */
89     private LinkedHashMap<String, List<String>> map = new LinkedHashMap<String,
90     List<String>>();
91 
92     private class MethodData {
93         String methodBody, constraint, title;
94     }
95 
96     /**
97      * @param args
98      *            args 0 must be the project root folder (where src, lib etc.
99      *            resides)
100      * @throws IOException
101      */
main(String[] args)102     public static void main(String[] args) throws IOException {
103 
104         if (args.length > 5) {
105             JAVASRC_FOLDER = args[0];
106             OUTPUT_FOLDER = args[1];
107             CLASS_PATH = args[2];
108             MAIN_SRC_OUTPUT_FOLDER = args[3];
109             CLASSES_OUTPUT_FOLDER = MAIN_SRC_OUTPUT_FOLDER + "/classes";
110 
111             COMPILED_CLASSES_FOLDER = args[4];
112 
113             HOSTJUNIT_SRC_OUTPUT_FOLDER = args[5];
114             HOSTJUNIT_CLASSES_OUTPUT_FOLDER = HOSTJUNIT_SRC_OUTPUT_FOLDER + "/classes";
115 
116             if (args.length > 6) {
117                 // optional: restrict to e.g. "opcodes.add_double"
118                 restrictTo = args[6];
119                 System.out.println("restricting build to: " + restrictTo);
120             }
121 
122         } else {
123             System.out.println("usage: java-src-folder output-folder classpath " +
124                     "generated-main-files compiled_output generated-main-files " +
125             "[restrict-to-opcode]");
126             System.exit(-1);
127         }
128 
129         long start = System.currentTimeMillis();
130         BuildDalvikSuite cat = new BuildDalvikSuite();
131         cat.compose();
132         long end = System.currentTimeMillis();
133 
134         System.out.println("elapsed seconds: " + (end - start) / 1000);
135     }
136 
compose()137     public void compose() throws IOException {
138         System.out.println("Collecting all junit tests...");
139         new TestRunner() {
140             @Override
141             protected TestResult createTestResult() {
142                 return new TestResult() {
143                     @Override
144                     protected void run(TestCase test) {
145                         addToTests(test);
146                     }
147 
148                 };
149             }
150         }.doRun(AllTests.suite());
151 
152         // for each combination of TestClass and method, generate a Main_testN1
153         // class in the respective package.
154         // for the report make sure all N... tests are called first, then B,
155         // then E, then VFE test methods.
156         // e.g. dxc.junit.opcodes.aaload.Test_aaload - testN1() ->
157         // File Main_testN1.java in package dxc.junit.opcodes.aaload.
158         //
159         handleTests();
160     }
161 
162     private void addToTests(TestCase test) {
163 
164         String packageName = test.getClass().getPackage().getName();
165         packageName = packageName.substring(packageName.lastIndexOf('.'));
166 
167 
168         String method = test.getName(); // e.g. testVFE2
169         String fqcn = test.getClass().getName(); // e.g.
170         // dxc.junit.opcodes.iload_3.Test_iload_3
171 
172         // ignore all tests not belonging to the given restriction
173         if (restrictTo != null && !fqcn.contains(restrictTo)) return;
174 
175         testMethodsCnt++;
176         List<String> li = map.get(fqcn);
177         if (li == null) {
178             testClassCnt++;
179             li = new ArrayList<String>();
180             map.put(fqcn, li);
181         }
182         li.add(method);
183     }
184     private String curJunitFileName = null;
185     private String curJunitFileData = "";
186 
187     private JavacBuildStep javacHostJunitBuildStep;
188 
189     private void flushHostJunitFile() {
190         if (curJunitFileName != null) {
191             File toWrite = new File(curJunitFileName);
192             String absPath = toWrite.getAbsolutePath();
193             // add to java source files for later compilation
194             javacHostJunitBuildStep.addSourceFile(absPath);
195             // write file
196             curJunitFileData += "\n}\n";
197             writeToFileMkdir(toWrite, curJunitFileData);
198             curJunitFileName = null;
199             curJunitFileData = "";
200         }
201     }
202 
203     private void openCTSHostFileFor(String pName, String classOnlyName) {
204         // flush previous JunitFile
205         flushHostJunitFile();
206         String sourceName = "JUnit_" + classOnlyName;
207 
208         // prepare current testcase-file
209         curJunitFileName = HOSTJUNIT_SRC_OUTPUT_FOLDER + "/" + pName.replaceAll("\\.","/") + "/" +
210         sourceName + ".java";
211         curJunitFileData = getWarningMessage() +
212         "package " + pName + ";\n" +
213         "import java.io.IOException;\n" +
214         "import com.android.tradefed.testtype.DeviceTestCase;\n" +
215         "\n" +
216         "public class " + sourceName + " extends DeviceTestCase {\n";
217     }
218 
219     private String getShellExecJavaLine(String classpath, String mainclass) {
220       String cmd = String.format("ANDROID_DATA=%s dalvikvm -Xint:portable -Xmx512M -Xss32K " +
221               "-Djava.io.tmpdir=%s -classpath %s %s", TARGET_JAR_ROOT_PATH, TARGET_JAR_ROOT_PATH,
222               classpath, mainclass);
223       return "String res = getDevice().executeShellCommand(\""+ cmd + "\");\n" +
224              "// A sucessful adb shell command returns an empty string.\n" +
225              "assertEquals(\"" + cmd + "\", \"\", res);";
226     }
227 
228     private String getWarningMessage() {
229         return "//Autogenerated code by " + this.getClass().getName() + "; do not edit.\n";
230     }
231 
232     private void addCTSHostMethod(String pName, String method, MethodData md,
233             Set<String> dependentTestClassNames) {
234         curJunitFileData += "public void " + method + "() throws Exception {\n";
235         final String targetCoreJarPath = String.format("%s/dot/junit/dexcore.jar",
236                 TARGET_JAR_ROOT_PATH);
237 
238         // push class with Main jar.
239         String mjar = "Main_" + method + ".jar";
240         String pPath = pName.replaceAll("\\.","/");
241         String mainJar = String.format("%s/%s/%s", TARGET_JAR_ROOT_PATH, pPath, mjar);
242 
243         String cp = String.format("%s:%s", targetCoreJarPath, mainJar);
244         for (String depFqcn : dependentTestClassNames) {
245             int lastDotPos = depFqcn.lastIndexOf('.');
246             String sourceName = depFqcn.replaceAll("\\.", "/") + ".jar";
247             String targetName= String.format("%s/%s", TARGET_JAR_ROOT_PATH,
248                     sourceName);
249             cp += ":" + targetName;
250             // dot.junit.opcodes.invoke_interface_range.ITest
251             // -> dot/junit/opcodes/invoke_interface_range/ITest.jar
252         }
253 
254         //"dot.junit.opcodes.add_double_2addr.Main_testN2";
255         String mainclass = pName + ".Main_" + method;
256         curJunitFileData += "    " + getShellExecJavaLine(cp, mainclass);
257         curJunitFileData += "}\n\n";
258     }
259 
260     private void handleTests() throws IOException {
261         System.out.println("collected " + testMethodsCnt + " test methods in " +
262                 testClassCnt + " junit test classes");
263         Set<BuildStep> targets = new TreeSet<BuildStep>();
264 
265         javacHostJunitBuildStep = new JavacBuildStep(HOSTJUNIT_CLASSES_OUTPUT_FOLDER, CLASS_PATH);
266 
267 
268         JavacBuildStep javacBuildStep = new JavacBuildStep(
269                 CLASSES_OUTPUT_FOLDER, CLASS_PATH);
270 
271         for (Entry<String, List<String>> entry : map.entrySet()) {
272 
273             String fqcn = entry.getKey();
274             int lastDotPos = fqcn.lastIndexOf('.');
275             String pName = fqcn.substring(0, lastDotPos);
276             String classOnlyName = fqcn.substring(lastDotPos + 1);
277             String instPrefix = "new " + classOnlyName + "()";
278 
279             openCTSHostFileFor(pName, classOnlyName);
280 
281             List<String> methods = entry.getValue();
282             Collections.sort(methods, new Comparator<String>() {
283                 public int compare(String s1, String s2) {
284                     // TODO sort according: test ... N, B, E, VFE
285                     return s1.compareTo(s2);
286                 }
287             });
288             for (String method : methods) {
289                 // e.g. testN1
290                 if (!method.startsWith("test")) {
291                     throw new RuntimeException("no test method: " + method);
292                 }
293 
294                 // generate the Main_xx java class
295 
296                 // a Main_testXXX.java contains:
297                 // package <packagenamehere>;
298                 // public class Main_testxxx {
299                 // public static void main(String[] args) {
300                 // new dxc.junit.opcodes.aaload.Test_aaload().testN1();
301                 // }
302                 // }
303                 MethodData md = parseTestMethod(pName, classOnlyName, method);
304                 String methodContent = md.methodBody;
305 
306                 Set<String> dependentTestClassNames = parseTestClassName(pName,
307                         classOnlyName, methodContent);
308 
309                 addCTSHostMethod(pName, method, md, dependentTestClassNames);
310 
311 
312                 if (dependentTestClassNames.isEmpty()) {
313                     continue;
314                 }
315 
316 
317                 String content = getWarningMessage() +
318                 "package " + pName + ";\n" +
319                 "import " + pName + ".d.*;\n" +
320                 "import dot.junit.*;\n" +
321                 "public class Main_" + method + " extends DxAbstractMain {\n" +
322                 "    public static void main(String[] args) throws Exception {" +
323                 methodContent + "\n}\n";
324 
325                 String fileName = getFileName(pName, method, ".java");
326                 File sourceFile = getFileFromPackage(pName, method);
327 
328                 File classFile = new File(CLASSES_OUTPUT_FOLDER + "/" +
329                         getFileName(pName, method, ".class"));
330                 // if (sourceFile.lastModified() > classFile.lastModified()) {
331                 writeToFile(sourceFile, content);
332                 javacBuildStep.addSourceFile(sourceFile.getAbsolutePath());
333 
334                 BuildStep dexBuildStep = generateDexBuildStep(
335                         CLASSES_OUTPUT_FOLDER, getFileName(pName, method, ""));
336                 targets.add(dexBuildStep);
337                 // }
338 
339                 generateBuildStepFor(pName, method, dependentTestClassNames,
340                         targets);
341             }
342 
343 
344         }
345 
346         // write latest HOSTJUNIT generated file.
347         flushHostJunitFile();
348 
349         if (!javacHostJunitBuildStep.build()) {
350             System.out.println("main javac cts-host-hostjunit-classes build step failed");
351             System.exit(1);
352         }
353 
354         if (javacBuildStep.build()) {
355             for (BuildStep buildStep : targets) {
356                 if (!buildStep.build()) {
357                     System.out.println("building failed. buildStep: " +
358                             buildStep.getClass().getName() + ", " + buildStep);
359                     System.exit(1);
360                 }
361             }
362         } else {
363             System.out.println("main javac dalvik-cts-buildutil build step failed");
364             System.exit(1);
365         }
366     }
367 
368     private void generateBuildStepFor(String pName, String method,
369             Set<String> dependentTestClassNames, Set<BuildStep> targets) {
370 
371 
372         for (String dependentTestClassName : dependentTestClassNames) {
373             generateBuildStepForDependant(dependentTestClassName, targets);
374         }
375     }
376 
377     private void generateBuildStepForDependant(String dependentTestClassName,
378             Set<BuildStep> targets) {
379 
380         File sourceFolder = new File(JAVASRC_FOLDER);
381         String fileName = dependentTestClassName.replace('.', '/').trim();
382 
383         if (new File(sourceFolder, fileName + ".dfh").exists()) {
384 
385             BuildStep.BuildFile inputFile = new BuildStep.BuildFile(
386                     JAVASRC_FOLDER, fileName + ".dfh");
387             BuildStep.BuildFile dexFile = new BuildStep.BuildFile(
388                     OUTPUT_FOLDER, fileName + ".dex");
389 
390             DFHBuildStep buildStep = new DFHBuildStep(inputFile, dexFile);
391 
392             BuildStep.BuildFile jarFile = new BuildStep.BuildFile(
393                     OUTPUT_FOLDER, fileName + ".jar");
394             JarBuildStep jarBuildStep = new JarBuildStep(dexFile,
395                     "classes.dex", jarFile, true);
396             jarBuildStep.addChild(buildStep);
397 
398             targets.add(jarBuildStep);
399             return;
400         }
401 
402         if (new File(sourceFolder, fileName + ".d").exists()) {
403 
404             BuildStep.BuildFile inputFile = new BuildStep.BuildFile(
405                     JAVASRC_FOLDER, fileName + ".d");
406             BuildStep.BuildFile dexFile = new BuildStep.BuildFile(
407                     OUTPUT_FOLDER, fileName + ".dex");
408 
409             DasmBuildStep buildStep = new DasmBuildStep(inputFile, dexFile);
410 
411             BuildStep.BuildFile jarFile = new BuildStep.BuildFile(
412                     OUTPUT_FOLDER, fileName + ".jar");
413 
414             JarBuildStep jarBuildStep = new JarBuildStep(dexFile,
415                     "classes.dex", jarFile, true);
416             jarBuildStep.addChild(buildStep);
417             targets.add(jarBuildStep);
418             return;
419         }
420 
421         if (new File(sourceFolder, fileName + ".java").exists()) {
422 
423             BuildStep dexBuildStep = generateDexBuildStep(
424                     COMPILED_CLASSES_FOLDER, fileName);
425             targets.add(dexBuildStep);
426             return;
427         }
428 
429         try {
430             if (Class.forName(dependentTestClassName) != null) {
431 
432                 BuildStep dexBuildStep = generateDexBuildStep(
433                         COMPILED_CLASSES_FOLDER, fileName);
434                 targets.add(dexBuildStep);
435                 return;
436             }
437         } catch (ClassNotFoundException e) {
438             // do nothing
439         }
440 
441         throw new RuntimeException("neither .dfh,.d,.java file of dependant test class found : " +
442                 dependentTestClassName + ";" + fileName);
443     }
444 
445     private BuildStep generateDexBuildStep(String classFileFolder,
446             String classFileName) {
447         BuildStep.BuildFile classFile = new BuildStep.BuildFile(
448                 classFileFolder, classFileName + ".class");
449 
450         BuildStep.BuildFile tmpJarFile = new BuildStep.BuildFile(OUTPUT_FOLDER,
451                 classFileName + "_tmp.jar");
452 
453         JarBuildStep jarBuildStep = new JarBuildStep(classFile, classFileName +
454                 ".class", tmpJarFile, false);
455 
456         BuildStep.BuildFile outputFile = new BuildStep.BuildFile(OUTPUT_FOLDER,
457                 classFileName + ".jar");
458 
459         DexBuildStep dexBuildStep = new DexBuildStep(tmpJarFile, outputFile,
460                 true);
461 
462         dexBuildStep.addChild(jarBuildStep);
463         return dexBuildStep;
464 
465     }
466 
467     /**
468      * @param pName
469      * @param classOnlyName
470      * @param methodSource
471      * @return testclass names
472      */
473     private Set<String> parseTestClassName(String pName, String classOnlyName,
474             String methodSource) {
475         Set<String> entries = new HashSet<String>();
476         String opcodeName = classOnlyName.substring(5);
477 
478         Scanner scanner = new Scanner(methodSource);
479 
480         String[] patterns = new String[] {"new\\s(T_" + opcodeName + "\\w*)",
481                 "(T_" + opcodeName + "\\w*)", "new\\s(T\\w*)"};
482 
483         String token = null;
484         for (String pattern : patterns) {
485             token = scanner.findWithinHorizon(pattern, methodSource.length());
486             if (token != null) {
487                 break;
488             }
489         }
490 
491         if (token == null) {
492             System.err.println("warning: failed to find dependent test class name: " + pName +
493                     ", " + classOnlyName + " in methodSource:\n" + methodSource);
494             return entries;
495         }
496 
497         MatchResult result = scanner.match();
498 
499         entries.add((pName + ".d." + result.group(1)).trim());
500 
501         // search additional @uses directives
502         Pattern p = Pattern.compile("@uses\\s+(.*)\\s+", Pattern.MULTILINE);
503         Matcher m = p.matcher(methodSource);
504         while (m.find()) {
505             String res = m.group(1);
506             entries.add(res.trim());
507         }
508 
509         // lines with the form @uses
510         // dot.junit.opcodes.add_double.jm.T_add_double_2
511         // one dependency per one @uses
512         // TODO
513 
514         return entries;
515     }
516 
517     private MethodData parseTestMethod(String pname, String classOnlyName,
518             String method) {
519 
520         String path = pname.replaceAll("\\.", "/");
521         String absPath = JAVASRC_FOLDER + "/" + path + "/" + classOnlyName + ".java";
522         File f = new File(absPath);
523 
524         Scanner scanner;
525         try {
526             scanner = new Scanner(f);
527         } catch (FileNotFoundException e) {
528             throw new RuntimeException("error while reading to file: " + e.getClass().getName() +
529                     ", msg:" + e.getMessage());
530         }
531 
532         String methodPattern = "public\\s+void\\s+" + method + "[^\\{]+\\{";
533 
534         String token = scanner.findWithinHorizon(methodPattern, (int) f.length());
535         if (token == null) {
536             throw new RuntimeException("cannot find method source of 'public void " + method +
537                     "' in file '" + absPath + "'");
538         }
539 
540         MatchResult result = scanner.match();
541         result.start();
542         result.end();
543 
544         StringBuilder builder = new StringBuilder();
545         //builder.append(token);
546 
547         try {
548             FileReader reader = new FileReader(f);
549             reader.skip(result.end());
550 
551             char currentChar;
552             int blocks = 1;
553             while ((currentChar = (char) reader.read()) != -1 && blocks > 0) {
554                 switch (currentChar) {
555                     case '}': {
556                         blocks--;
557                         builder.append(currentChar);
558                         break;
559                     }
560                     case '{': {
561                         blocks++;
562                         builder.append(currentChar);
563                         break;
564                     }
565                     default: {
566                         builder.append(currentChar);
567                         break;
568                     }
569                 }
570             }
571             if (reader != null) {
572                 reader.close();
573             }
574         } catch (Exception e) {
575             throw new RuntimeException("failed to parse", e);
576         }
577 
578         // find the @title/@constraint in javadoc comment for this method
579         // using platform's default charset
580         String all = new String(FileUtils.readFile(f));
581         // System.out.println("grepping javadoc found for method " + method +
582         // " in " + pname + "," + classOnlyName);
583         String commentPattern = "/\\*\\*([^{]*)\\*/\\s*" + methodPattern;
584         Pattern p = Pattern.compile(commentPattern, Pattern.DOTALL);
585         Matcher m = p.matcher(all);
586         String title = null, constraint = null;
587         if (m.find()) {
588             String res = m.group(1);
589             // System.out.println("res: " + res);
590             // now grep @title and @constraint
591             Matcher titleM = Pattern.compile("@title (.*)", Pattern.DOTALL)
592             .matcher(res);
593             if (titleM.find()) {
594                 title = titleM.group(1).replaceAll("\\n     \\*", "");
595                 title = title.replaceAll("\\n", " ");
596                 title = title.trim();
597                 // System.out.println("title: " + title);
598             } else {
599                 System.err.println("warning: no @title found for method " + method + " in " + pname +
600                         "," + classOnlyName);
601             }
602             // constraint can be one line only
603             Matcher constraintM = Pattern.compile("@constraint (.*)").matcher(
604                     res);
605             if (constraintM.find()) {
606                 constraint = constraintM.group(1);
607                 constraint = constraint.trim();
608                 // System.out.println("constraint: " + constraint);
609             } else if (method.contains("VFE")) {
610                 System.err
611                 .println("warning: no @constraint for for a VFE method:" + method + " in " +
612                         pname + "," + classOnlyName);
613             }
614         } else {
615             System.err.println("warning: no javadoc found for method " + method + " in " + pname +
616                     "," + classOnlyName);
617         }
618         MethodData md = new MethodData();
619         md.methodBody = builder.toString();
620         md.constraint = constraint;
621         md.title = title;
622         if (scanner != null) {
623             scanner.close();
624         }
625         return md;
626     }
627 
628     private void writeToFileMkdir(File file, String content) {
629         File parent = file.getParentFile();
630         if (!parent.exists() && !parent.mkdirs()) {
631             throw new RuntimeException("failed to create directory: " + parent.getAbsolutePath());
632         }
633         writeToFile(file, content);
634     }
635 
636     private void writeToFile(File file, String content) {
637         try {
638             if (file.length() == content.length()) {
639                 FileReader reader = new FileReader(file);
640                 char[] charContents = new char[(int) file.length()];
641                 reader.read(charContents);
642                 reader.close();
643                 String contents = new String(charContents);
644                 if (contents.equals(content)) {
645                     // System.out.println("skipping identical: "
646                     // + file.getAbsolutePath());
647                     return;
648                 }
649             }
650 
651             //System.out.println("writing file " + file.getAbsolutePath());
652 
653             BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
654                     new FileOutputStream(file), "utf-8"));
655             bw.write(content);
656             bw.close();
657         } catch (Exception e) {
658             throw new RuntimeException("error while writing to file: " + e.getClass().getName() +
659                     ", msg:" + e.getMessage());
660         }
661     }
662 
663     private File getFileFromPackage(String pname, String methodName)
664     throws IOException {
665         // e.g. dxc.junit.argsreturns.pargsreturn
666         String path = getFileName(pname, methodName, ".java");
667         String absPath = MAIN_SRC_OUTPUT_FOLDER + "/" + path;
668         File dirPath = new File(absPath);
669         File parent = dirPath.getParentFile();
670         if (!parent.exists() && !parent.mkdirs()) {
671             throw new IOException("failed to create directory: " + absPath);
672         }
673         return dirPath;
674     }
675 
676     private String getFileName(String pname, String methodName,
677             String extension) {
678         String path = pname.replaceAll("\\.", "/");
679         return new File(path, "Main_" + methodName + extension).getPath();
680     }
681 }
682