1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 | package Torello.Java.Build; import static Torello.Java.C.BYELLOW; import static Torello.Java.C.BGREEN; import static Torello.Java.C.BBLUE_BKGND; import static Torello.Java.C.BCYAN; import static Torello.Java.C.BRED; import static Torello.Java.C.RESET; import Torello.Java.ReadOnly.ReadOnlyList; import Torello.Java.ReadOnly.ReadOnlyArrayList; import Torello.Java.FileNode; import Torello.Java.RTC; import Torello.Java.Shell; import Torello.Java.OSResponse; import Torello.Java.StringParse; import Torello.Java.FileTransfer; import java.io.IOException; import java.io.File; import java.util.stream.Stream; // This handles the CLI Option that may be entered by a User which requests that the '.java' // Test-Files in a users 'some/package/test-files/' directory be complied in order to be run later // on. // // This scans each '../test-files/' directory for any and all '.java' Files, and then invokes the // 'javac' command. The binary used to perform the compilation may be supplied via the method // parameter 'JAVAC_BIN'. If that parameter is null, then whatever 'javac' binary which is found // within the Operating-System's "path" Environment-Variable is used. Note that if 'JAVAC_BIN' is // supplied null, and the O/S cannot find 'javac' in the path *ANYWHERE*, then this method will // throw an exception. // // The Compilation is performed in one single fell-swoop. The '.java' File-Names are supplied to // the 'javac' command via Method-Parameters / Switches. Before the compilation takes place, any // and all '.class'-Files which are present in the '../test-files/' directory are summarily // removed first. This is done just to guarantee that outdated classes which have names that are // not overwritten are removed & eliminated first. class CompileTests { static void compile( final ReadOnlyList<BuildPackage> packages, final ReadOnlyList<String> additionalCompilerArgs, final String JAVAC_BIN ) throws IOException { System.out.println(); // It starts to seem a little bit "Ridiculous" to get so "into" Read-Only Lists and // Stream.Builder. However, I just get used to using these concepts, and in the end there // is nothing particularly inefficient about this construct at all. It would probably be // easier to just use a "List<String>", but I'm going to leave it thsi way. // // I am very accustomed to using ROArrayListBuilder and Stream.Builder final Stream.Builder<String> dirNamesB = Stream.builder(); final Stream.Builder<Integer> dirSizesB = Stream.builder(); final Stream.Builder<String> javacB = Stream.builder(); if (additionalCompilerArgs != null) for (String arg : additionalCompilerArgs) javacB.accept(arg); /* b.accept("--release"); b.accept("11"); b.accept("-encoding"); b.accept("UTF-8"); b.accept("-classpath"); b.accept(".:Torello/etc/External/"); b.accept("-Xlint:all,-processing"); b.accept("-Xdiags:verbose"); */ int maxDirNameLen = 0; int totalFileCount = 0; for (BuildPackage bp : packages) { final String testFilesDirName = bp.pkgRootDirectory + "test-files" + File.separator; dirNamesB.accept(testFilesDirName); if (testFilesDirName.length() > maxDirNameLen) maxDirNameLen = testFilesDirName.length(); final File dir = new File(testFilesDirName); if ((! dir.exists()) || (! dir.isDirectory())) { dirSizesB.accept(-1); continue; } final FileNode fn = FileNode .createRoot(testFilesDirName) .loadTree(-1, FileNode.JAVA_FILES, null); final int numJavaFiles = fn.countJustFiles(); dirSizesB.accept(numJavaFiles); /* System.out.println( "Dir: [" + BYELLOW + testFilesDirName + RESET + "]\n" + " Contains " + BGREEN + numJavaFiles + RESET + " '.java' Files.\n" ); */ if (numJavaFiles == 0) continue; totalFileCount += numJavaFiles; fn.flattenJustFiles(RTC.FULLPATH_STREAM_BUILDER(javacB)); clearClassFiles(testFilesDirName); } if (totalFileCount == 0) { System.out.println(BGREEN + "\nNo Files to Compile... Exiting...\n" + RESET); return; } // If I had used a "List" instead of a Stream.Builder, then the following 6 lines of code // wouldn't be here at all. As I said, I am so used to typing this crap, that there is // not an easy way to undo that. final ReadOnlyList<String> dirNamesROL = dirNamesB .build() .collect(ReadOnlyArrayList.streamCollector()); final ReadOnlyList<Integer> dirSizesROL = dirSizesB .build() .collect(ReadOnlyArrayList.streamCollector()); printSummary(dirNamesROL, dirSizesROL, maxDirNameLen); printTextBar(" Invoking 'javac': "); final Shell shell = new Shell(System.out, System.out, null, null); final OSResponse r = (JAVAC_BIN == null) ? shell.JAVAC(javacB.build().toArray(String[]::new)) : null; if (r.response != 0) System.out.println (BRED + "\nERROR CODE: " + r.response + '\n' + RESET); } // Currently, all '.class' Files are first removed from any '../test-files/' directories, and // any sub-directories of that tree. This may seem a little bit excessive, however it is also // a little bit over-zealous to begin adding a "-WCFF" (Wipe Class Files First) switch to the // CLI. // // As a result of this decision, this class immediately deletes any and all class files it // locates in the directory tree first. Perhaps later on this will become an "Optional" part // of the exercise, but for now it is mandatory. // // Unless the user has written some kind of "Class File Editing" Package, and is writing // test-files for editing class-files, it just seems simply incomprehensible to me that this // is going to matter in any way shape or form. // // Later on, this should be converted to a "-WCFF" Option (which is what the Main-Build CLI // does) private static void clearClassFiles(final String testFilesDirName) throws IOException { printTextBar(" Clearing Class Files First: "); final FileNode fn = FileNode .createRoot(testFilesDirName) .loadTree(-1, FileNode.CLASS_FILES, null); FileTransfer.deleteFilesRecursive(fn, null, null, System.out); } // All this does is print up a cute little summary of how many '.java' Files were sucessfully // compiled when building the test-suite for a particular Java-Package. // // The Menu itself just lists the packages, and prints the number of files found. private static void printSummary( final ReadOnlyList<String> dirNames, final ReadOnlyList<Integer> dirSizes, final int maxDirNameLen ) { printTextBar(BYELLOW + " '../test-files/'" + RESET + " Directory-Summary: " + RESET); if (dirNames.size() != dirSizes.size()) throw new BuildError ( "The 'dirNames' Array has length " + dirNames.size() + ", while the 'dirSizes' Array" + "has length " + dirSizes.size() + ", but these arrays should be parallel." ); final int LEN = dirNames.size(); for (int i=0; i < LEN; i++) { final int count = dirSizes.get(i); final String name = dirNames.get(i); final String spaces = StringParse.nChars(' ', maxDirNameLen - name.length()); final String dirSizeStr = (count == -1) ? ("No Such '" + BYELLOW + "../test-files/" + RESET + " Directory") : (count == 0) ? (BRED + "No" + RESET + " '.java' Files") : (BCYAN + StringParse.zeroPad10e4(count) + RESET + " '.java' Files"); System.out.println ("Dir [" + BYELLOW + name + RESET + "]: " + spaces + dirSizeStr); } } private static void printTextBar(final String text) { System.out.println( "\n\n" + BBLUE_BKGND + " " + RESET + '\n' + BBLUE_BKGND + ' ' + RESET + text + BBLUE_BKGND + ' ' + RESET + '\n' + BBLUE_BKGND + " " + RESET + '\n' ); } } |