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=S01_JAVA_COMPILER> 027 */ 028@Torello.JavaDoc.StaticFunctional 029public class S01_JavaCompiler 030{ 031 // Completely irrelevant, and the 'private' modifier keeps it off of JavaDoc 032 private S01_JavaCompiler() { } 033 034 035 // ******************************************************************************************** 036 // ******************************************************************************************** 037 // buildCommand Helper Method 038 // ******************************************************************************************** 039 // ******************************************************************************************** 040 041 042 private static ReadOnlyList<String> buildCommand 043 (Builder builder, ReadOnlyList<String> filesToCompile) 044 { 045 final ROArrayListBuilder<String> roab = new ROArrayListBuilder<>(); 046 047 if (builder.JAVA_RELEASE_NUM_SWITCH > 0) 048 roab.add("--release"); 049 roab.add("" + builder.JAVA_RELEASE_NUM_SWITCH); 050 051 roab.add("-encoding"); 052 roab.add("UTF-8"); 053 054 roab.add("-processor"); 055 roab.add("Torello.JDUInternal.Annotations.JDUAnnotationProcessorDispatch"); 056 057 roab.add("-classpath"); 058 roab.add(builder.CLASS_PATH_STR); 059 060 if (builder.USE_XLINT_SWITCH) roab.add("-Xlint:all,-processing"); 061 062 if (builder.USE_XDIAGS_SWITCH) roab.add("-Xdiags:verbose"); 063 064 if ((builder.extraSwitchesJAVAC != null) && (builder.extraSwitchesJAVAC.size() > 0)) 065 for (String switchStr : builder.extraSwitchesJAVAC) 066 roab.add(switchStr); 067 068 for (String fileName : filesToCompile) roab.add(fileName); 069 070 return roab.build(); 071 }; 072 073 074 // ******************************************************************************************** 075 // ******************************************************************************************** 076 // Print the 'javac' Command 077 // ******************************************************************************************** 078 // ******************************************************************************************** 079 080 081 static void printCommandText( 082 Builder builder, 083 ReadOnlyList<String> javacCommand, 084 Appendable logAndScreen, 085 Appendable logOnly, 086 ReadOnlyList<String> filesToCompile 087 ) 088 throws IOException 089 { 090 // 'javac' Command-Path 091 logAndScreen.append( 092 BCYAN + "Running Java Compiler: Command Switches\n" + RESET + 093 BGREEN + "INVOKING: " + RESET + 094 BYELLOW + builder.JAVAC_BIN + RESET + "\n\n" 095 ); 096 097 // All of the Command-Line Arguments which *ARE NOT* '.java' files SHOULD BE PRINTED to 098 // 'javac'. If a few of the '.java' Files are also printed, it isn't that big of a deal... 099 // 100 // The "--release" switch may or may not have been used. 101 102 final int NUM_TWO_ARGUMENT_JAVAC_ARGS = 103 4 - ((builder.JAVA_RELEASE_NUM_SWITCH <= 0) ? 1 : 0); 104 105 final int NUM_ONE_ARGUMENT_JAVAC_ARGS = 106 (builder.USE_XLINT_SWITCH ? 1 : 0) + (builder.USE_XDIAGS_SWITCH ? 1 : 0); 107 108 // First the two-argument command line arguments are printed 109 for (int i=0; i < NUM_TWO_ARGUMENT_JAVAC_ARGS; i++) logAndScreen.append 110 (" " + javacCommand.get(2 * i) + " " + javacCommand.get(2 * i + 1) + '\n'); 111 112 // Make sure to skip over the arguments that have already been pritned 113 final int SPOS = 2 * NUM_TWO_ARGUMENT_JAVAC_ARGS; 114 115 // Print the single-argument command-line arguments 116 for (int i=SPOS; i < (SPOS + NUM_ONE_ARGUMENT_JAVAC_ARGS); i++) 117 logAndScreen.append(" " + javacCommand.get(i) + '\n'); 118 119 // Java-HTML doens't use this yet (as of January 2024), but if "extra" / User-Added `javac` 120 // Command-Line Arguments have been configured into the "Builder" instance, then (at this 121 // point in the code), those switch-arguments will have already been added/inserted into 122 // the Command. Make sure to print them out, as below: 123 124 if ((builder.extraSwitchesJAVAC != null) && (builder.extraSwitchesJAVAC.size() > 0)) 125 for (String switchStr : builder.extraSwitchesJAVAC) 126 logAndScreen.append(switchStr + '\n'); 127 128 // The last thing to print is this stuff... 129 /* Screen Only */ System.out.println( 130 "\n [Source-Files List Omitted, Total Number of Files to Compile: " + 131 BRED + filesToCompile.size() + RESET + "]\n" 132 ); 133 134 for (String fileName : filesToCompile) logOnly.append(" " + fileName + '\n'); 135 } 136 137 138 // ******************************************************************************************** 139 // ******************************************************************************************** 140 // Compile 141 // ******************************************************************************************** 142 // ******************************************************************************************** 143 144 145 private static final String FS = File.separator; 146 147 public static void compile(Builder builder) throws IOException 148 { 149 // Initialize the logs, and get ready to start-up 150 Printing.startStep(1); 151 152 StringBuilder logOnly = new StringBuilder(); 153 Appendable logAndScreen = new BiAppendable(logOnly, System.out); 154 155 logOnly.append("\nPackages Included in this Builder:\n\n"); 156 for (BuildPackage bp : builder.packageList) logOnly.append(bp.fullName + '\n'); 157 158 // List all Packages to be Compiled 159 ReadOnlyList<BuildPackage> packagesToCompile = Packages.packagesToCompile(builder); 160 161 logOnly.append("\nPackages Considered for Compilation:\n\n"); 162 for (BuildPackage bp : builder.packageList) logOnly.append(bp.fullName + '\n'); 163 164 // Convert the List of Packages to a list of files 165 ReadOnlyList<String> filesToCompile = Files.filesToCompile 166 (packagesToCompile, logAndScreen); 167 168 // Build the javadoc Command, Print the Command-Line Arguments to Screen 169 ReadOnlyList<String> javacCommand = buildCommand(builder, filesToCompile); 170 171 // Print this to the log and to terminal 172 printCommandText(builder, javacCommand, logAndScreen, logOnly, filesToCompile); 173 174 // Execute 'javac' 175 OSResponse osr = new Shell(logOnly) 176 .COMMAND(builder.JAVAC_BIN, javacCommand.toArray(new String[0])); 177 178 // If there were errors, print them out and exit 179 // 180 // NOTE: This absolutely sucks, but in Java-17, the "Error Output" isn't actually published 181 // to the OS Standard-Error! It all appears to be going to Standard-Out. 182 183 if (osr.errorOutput.length() > 0) 184 { 185 System.err.println 186 (BRED + "\nTEXT PRINTED TO STANDARD ERROR:\n" + RESET + osr.errorOutput); 187 188 Util.ERROR_EXIT("javac"); // Calls System.exit 189 } 190 191 192 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 193 // Move '../package-source/' CLASS-FILES to their parent/proper location 194 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 195 196 int max = 0; 197 198 for (BuildPackage pkg : packagesToCompile) 199 if (pkg.hasPackageSourceDir && (pkg.pkgRootDirectory.length() > max)) 200 max = pkg.pkgRootDirectory.length(); 201 202 // max += "package-source/".length(); 203 max += 15; 204 205 boolean printed = false; 206 207 for (final BuildPackage pkg : packagesToCompile) if (pkg.hasPackageSourceDir) 208 { 209 if (! printed) 210 { 211 logAndScreen.append(BCYAN + "\nRelocating Class Files:\n\n" + RESET); 212 printed = true; 213 } 214 215 final String dir = pkg.pkgRootDirectory; 216 217 int numClassFiles = movePackageSourceClassFiles(dir, logOnly); 218 219 logAndScreen.append( 220 " Moved " + BRED + StringParse.zeroPad(numClassFiles) + RESET + 221 " *.class File(s) From: " + 222 BYELLOW + StringParse.rightSpacePad(dir + "package-source" + FS, max) + RESET + 223 " To: " + BYELLOW + dir + RESET + '\n' 224 ); 225 } 226 227 228 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 229 // Write the log data to the log files 230 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 231 232 // OLD CODE: IS_FULL_COMPILE = (builder.cli.userSpecifiedPackages == null) 233 if (builder.cli.userSpecifiedPackages == null) 234 builder.logs.write_S01_LOGS(logOnly.toString(), osr.standardOutput, osr.errorOutput); 235 } 236 237 private static int movePackageSourceClassFiles(final String dir, Appendable a) 238 { 239 Counter c = new Counter(); 240 241 FileNode 242 .createRoot(dir + "package-source" + FS) 243 .loadTree(-1, FileNode.CLASS_FILES, null) 244 .flattenJustFiles(RTC.FULLPATH_VECTOR()) 245 .forEach((String srcFileName) -> 246 { 247 c.addOne(); 248 249 try 250 { 251 FileRW.moveFile(srcFileName, dir, false); 252 a.append("Moved " + srcFileName + " to " + dir + '\n'); 253 } 254 255 catch (IOException ioe) 256 { 257 System.err.println( 258 EXCC.toString(ioe) + '\n' + 259 "Fatal Error, Exiting Build\n" 260 ); 261 262 System.exit(1); 263 } 264 }); 265 266 return c.get(); 267 } 268}