001package Torello.Java.Build; 002 003import Torello.Java.*; 004 005import Torello.Java.Additional.BiAppendable; 006import Torello.Java.Additional.Counter; 007 008import Torello.Java.ReadOnly.ReadOnlyList; 009import Torello.Java.ReadOnly.ReadOnlyArrayList; 010import Torello.Java.ReadOnly.ROArrayListBuilder; 011 012import java.io.FilenameFilter; 013import java.io.File; 014import java.io.IOException; 015 016import java.util.stream.Stream; 017import java.util.function.Predicate; 018 019import static Torello.Java.C.*; 020 021/** 022 * This is the first Build-Stage, and it runs the Java-Compiler - using {@code 'java'} and 023 * {@link Shell Torello.Java.Shell}. This class also relies heavily on the Java-HTML Tools 024 * {@link FileNode} and {@link FileRW}. 025 * 026 * <EMBED CLASS=external-html DATA-FILE-ID=STAGE_PRIVATE_NOTE> 027 * <EMBED CLASS='external-html' DATA-FILE-ID=S01_JAVA_COMPILER> 028 */ 029@Torello.JavaDoc.StaticFunctional 030public class S01_JavaCompiler 031{ 032 // Completely irrelevant, and the 'private' modifier keeps it off of JavaDoc 033 private S01_JavaCompiler() { } 034 035 036 // ******************************************************************************************** 037 // ******************************************************************************************** 038 // buildCommand Helper Method 039 // ******************************************************************************************** 040 // ******************************************************************************************** 041 042 043 private static ReadOnlyList<String> buildCommand 044 (final BuilderRecord brec, final ReadOnlyList<String> filesToCompile) 045 { 046 final ROArrayListBuilder<String> roab = new ROArrayListBuilder<>(); 047 048 if (brec.JAVA_RELEASE_NUM_SWITCH > 0) 049 roab.add("--release"); 050 roab.add("" + brec.JAVA_RELEASE_NUM_SWITCH); 051 052 roab.add("-encoding"); 053 roab.add("UTF-8"); 054 055 roab.add("-processor"); 056 roab.add("Torello.JDUInternal.Annotations.JDUAnnotationProcessorDispatch"); 057 058 roab.add("-classpath"); 059 roab.add(brec.CLASS_PATH_STR); 060 061 if (brec.USE_XLINT_SWITCH) roab.add("-Xlint:all,-processing"); 062 063 if (brec.USE_XDIAGS_SWITCH) roab.add("-Xdiags:verbose"); 064 065 if ((brec.extraSwitchesJAVAC != null) && (brec.extraSwitchesJAVAC.size() > 0)) 066 for (String switchStr : brec.extraSwitchesJAVAC) 067 roab.add(switchStr); 068 069 for (String fileName : filesToCompile) roab.add(fileName); 070 071 return roab.build(); 072 }; 073 074 075 // ******************************************************************************************** 076 // ******************************************************************************************** 077 // Print the 'javac' Command 078 // ******************************************************************************************** 079 // ******************************************************************************************** 080 081 082 static void printCommandText( 083 final BuilderRecord brec, 084 final ReadOnlyList<String> javacCommand, 085 final Appendable logAndScreen, 086 final Appendable logOnly, 087 final ReadOnlyList<String> filesToCompile 088 ) 089 throws IOException 090 { 091 // 'javac' Command-Path 092 logAndScreen.append( 093 BCYAN + "Running Java Compiler: Command Switches\n" + RESET + 094 BGREEN + "INVOKING: " + RESET + 095 BYELLOW + brec.JAVAC_BIN + RESET + "\n\n" 096 ); 097 098 099 // All of the Command-Line Arguments which *ARE NOT* '.java' files SHOULD BE PRINTED to 100 // 'javac'. If a few of the '.java' Files are also printed, it isn't that big of a deal... 101 // 102 // The "--release" switch may or may not have been used. 103 104 final int NUM_TWO_ARGUMENT_JAVAC_ARGS = 105 4 - ((brec.JAVA_RELEASE_NUM_SWITCH <= 0) ? 1 : 0); 106 107 final int NUM_ONE_ARGUMENT_JAVAC_ARGS = 108 (brec.USE_XLINT_SWITCH ? 1 : 0) + (brec.USE_XDIAGS_SWITCH ? 1 : 0); 109 110 // First the two-argument command line arguments are printed 111 for (int i=0; i < NUM_TWO_ARGUMENT_JAVAC_ARGS; i++) logAndScreen.append 112 (" " + javacCommand.get(2 * i) + " " + javacCommand.get(2 * i + 1) + '\n'); 113 114 // Make sure to skip over the arguments that have already been pritned 115 final int SPOS = 2 * NUM_TWO_ARGUMENT_JAVAC_ARGS; 116 117 // Print the single-argument command-line arguments 118 for (int i=SPOS; i < (SPOS + NUM_ONE_ARGUMENT_JAVAC_ARGS); i++) 119 logAndScreen.append(" " + javacCommand.get(i) + '\n'); 120 121 122 // Java-HTML doens't use this yet (as of January 2024), but if "extra" / User-Added `javac` 123 // Command-Line Arguments have been configured into the "BuilderRecord" instance, then (at 124 // this point in the code), those switch-arguments will have already been added/inserted 125 // into the Command. Make sure to print them out, as below: 126 127 if ((brec.extraSwitchesJAVAC != null) && (brec.extraSwitchesJAVAC.size() > 0)) 128 for (String switchStr : brec.extraSwitchesJAVAC) 129 logAndScreen.append(switchStr + '\n'); 130 131 // The last thing to print is this stuff... 132 /* Screen Only */ System.out.println( 133 "\n [Source-Files List Omitted, Total Number of Files to Compile: " + 134 BRED + filesToCompile.size() + RESET + "]\n" 135 ); 136 137 for (String fileName : filesToCompile) logOnly.append(" " + fileName + '\n'); 138 } 139 140 141 // ******************************************************************************************** 142 // ******************************************************************************************** 143 // Compile 144 // ******************************************************************************************** 145 // ******************************************************************************************** 146 147 148 private static final String FS = File.separator; 149 150 public static void compile(final BuilderRecord brec) throws IOException 151 { 152 // Initialize the logs, and get ready to start-up 153 Printing.startStep(1); 154 155 StringBuilder logOnly = new StringBuilder(); 156 Appendable logAndScreen = new BiAppendable(logOnly, System.out); 157 158 logOnly.append("\nPackages Included in this Builder:\n\n"); 159 for (BuildPackage bp : brec.packageList) logOnly.append(bp.fullName + '\n'); 160 161 // List all Packages to be Compiled 162 ReadOnlyList<BuildPackage> packagesToCompile = Packages.packagesToCompile(brec); 163 164 logOnly.append("\nPackages Considered for Compilation:\n\n"); 165 for (BuildPackage bp : brec.packageList) logOnly.append(bp.fullName + '\n'); 166 167 168 // The user has the option of having all '.class' files removed from all packages which 169 // are currently being re-compiled. 170 171 if (brec.cli.aor.WIPE_CLASS_FILES_FIRST) 172 deleteClassFilesFirst(packagesToCompile, logAndScreen); 173 174 175 // Convert the List of Packages to a list of '.java'-Files. 176 // 177 // NOTE: This method invokes the Printing-Method, and actually prints out a count of the 178 // number of '.java' Files in each package that is going to be compiled. It sort of 179 // **HAS TO** be done inside this method, because otherwise a "Count Array" would 180 // have to be returned from this method, making this much uglier. 181 // 182 // SEE: 'logAndScreen' is passed as ap parameter to this method. 183 184 ReadOnlyList<String> filesToCompile = Files.filesToCompile 185 (packagesToCompile, logAndScreen); 186 187 // Build the javac Command 188 ReadOnlyList<String> javacCommand = buildCommand(brec, filesToCompile); 189 190 // Print this to the log and to terminal 191 printCommandText(brec, javacCommand, logAndScreen, logOnly, filesToCompile); 192 193 // Execute 'javac' 194 OSResponse osr = new Shell(logOnly) 195 .COMMAND(brec.JAVAC_BIN, javacCommand.toArray(new String[0])); 196 197 198 // If there were errors, print them out and exit 199 // 200 // NOTE: This absolutely sucks, but in Java-17, the "Error Output" isn't actually published 201 // to the OS Standard-Error! It all appears to be going to Standard-Out. 202 203 if (osr.errorOutput.length() > 0) 204 { 205 System.err.println 206 (BRED + "\nTEXT PRINTED TO STANDARD ERROR:\n" + RESET + osr.errorOutput); 207 208 Util.ERROR_EXIT("javac"); // Calls System.exit 209 } 210 211 212 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 213 // Move '../package-source/' CLASS-FILES to their parent/proper location 214 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 215 216 int max = 0; 217 218 for (BuildPackage pkg : packagesToCompile) 219 if (pkg.hasPackageSourceDir && (pkg.pkgRootDirectory.length() > max)) 220 max = pkg.pkgRootDirectory.length(); 221 222 // max += "package-source/".length(); 223 max += 15; 224 225 boolean printed = false; 226 227 for (final BuildPackage pkg : packagesToCompile) if (pkg.hasPackageSourceDir) 228 { 229 if (! printed) 230 { 231 logAndScreen.append(BCYAN + "\nRelocating Class Files:\n\n" + RESET); 232 printed = true; 233 } 234 235 final String dir = pkg.pkgRootDirectory; 236 237 int numClassFiles = movePackageSourceClassFiles(dir, logOnly); 238 239 logAndScreen.append( 240 " Moved " + BRED + StringParse.zeroPad(numClassFiles) + RESET + 241 " *.class File(s) From: " + 242 BYELLOW + StringParse.rightSpacePad(dir + "package-source" + FS, max) + RESET + 243 " To: " + BYELLOW + dir + RESET + '\n' 244 ); 245 } 246 247 248 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 249 // Write the log data to the log files 250 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 251 // 252 // OLD CODE: IS_FULL_COMPILE = (brec.cli.userSpecifiedPackages == null) 253 // 254 // brec: BuilderRecord - Top-of-Tree Data-Record for executing a build, Contains everything 255 // cli: (Command-Line-Interface) - All info gathered from "String[] argv" 256 // sor: SelectedOptionsRecord - Information related to the Main-Menu Options 257 258 if (brec.cli.sor.userSpecifiedPackages == null) 259 brec.logs.write_S01_LOGS(logOnly.toString(), osr.standardOutput, osr.errorOutput); 260 } 261 262 263 // ******************************************************************************************** 264 // ******************************************************************************************** 265 // Util 266 // ******************************************************************************************** 267 // ******************************************************************************************** 268 269 270 private static void deleteClassFilesFirst( 271 final ReadOnlyList<BuildPackage> packagesToCompile, 272 final Appendable logAndScreen 273 ) 274 throws IOException 275 { 276 // Convert the List of Packages to a list of '.class'-Files 277 // (These files are to be deleted). 278 // 279 // NOTE: This method invokes the Printing-Method, and actually prints out a count of the 280 // number of '.class' Files in each package. 281 // 282 // SEE: 'logAndScreen' is passed as ap parameter to this method. 283 284 ReadOnlyList<String> filesToCompile = Files.classFilesToDeleteBeforeCompilation 285 (packagesToCompile, logAndScreen); 286 287 logAndScreen.append( 288 BCYAN + "Deleting " + RESET + BYELLOW + "'.class'" + RESET + 289 BCYAN + " Files, before javac\n\n" + RESET 290 ); 291 292 FileRW.deleteFiles(filesToCompile.toArray(String[]:: new)); 293 } 294 295 private static int movePackageSourceClassFiles(final String dir, final Appendable a) 296 { 297 Counter c = new Counter(); 298 299 FileNode 300 .createRoot(dir + "package-source" + FS) 301 .loadTree(-1, FileNode.CLASS_FILES, null) 302 .flattenJustFiles(RTC.FULLPATH_VECTOR()) 303 .forEach((String srcFileName) -> 304 { 305 c.addOne(); 306 307 try 308 { 309 FileRW.moveFile(srcFileName, dir, false); 310 a.append("Moved " + srcFileName + " to " + dir + '\n'); 311 } 312 313 catch (IOException ioe) 314 { 315 System.err.println( 316 EXCC.toString(ioe) + '\n' + 317 "Fatal Error, Exiting Build\n" 318 ); 319 320 System.exit(1); 321 } 322 }); 323 324 return c.get(); 325 } 326}