Subversion Repository Public Repository

litesoft

Diff Revisions 958 vs 959 for /trunk/Java/ScarPlus/src/com/esotericsoftware/scar/Project.java

Diff revisions: vs.
  @@ -1,1214 +1,1030 @@
1 - package com.esotericsoftware.scar;
2 -
3 - import org.litesoft.logger.*;
4 -
5 - import com.esotericsoftware.filesystem.*;
6 - import com.esotericsoftware.scar.support.*;
7 - import com.esotericsoftware.utils.*;
8 -
9 - import javax.tools.*;
10 - import java.io.*;
11 - import java.util.*;
12 - import java.util.jar.*;
13 - import java.util.zip.*;
14 -
15 - /**
16 - * Generic Data structure that contains information needed to perform tasks.
17 - */
18 - @SuppressWarnings("UnusedDeclaration")
19 - public class Project extends ProjectParameters
20 - {
21 - private static final String JAVA_SOURCE_TARGET_VERSION = "1.7";
22 -
23 - protected static final Logger LOGGER = LoggerFactory.getLogger( Project.class );
24 -
25 - private static final String META_INF_MANIFEST_MF = "META-INF/MANIFEST.MF";
26 -
27 - private static final String VERSIONED_URL_PATTERN_PREFIX = "<url-pattern>/v";
28 - private static final String VERSIONED_SCRIPT_PREFIX = "<script src='v";
29 - private static final String VERSIONED_SCRIPT_SUFFIX = ".nocache.js'></script>";
30 - private static final String VERSIONED_MODULE_PREFIX = "<module rename-to=\"v";
31 - private static final String VERSIONED_MODULE_SUFFIX = "\">";
32 - private static final String JAVA_HOME = "JAVA_HOME";
33 -
34 - public Project( ProjectParameters pParameters )
35 - {
36 - super( pParameters.validate() );
37 - applyDefaults();
38 - }
39 -
40 - public String getSourceJavaVersion()
41 - {
42 - return JAVA_SOURCE_TARGET_VERSION;
43 - }
44 -
45 - public String getTargetJavaVersion()
46 - {
47 - return JAVA_SOURCE_TARGET_VERSION;
48 - }
49 -
50 - protected void packageClean()
51 - {
52 - delete( getPhoneGapDirPath() );
53 - delete( getAppDirPath() );
54 - delete( getOneJarPath() );
55 - delete( getWarPath() );
56 - }
57 -
58 - protected boolean packageIt()
59 - {
60 - return phoneGapDir() | appDir() | oneJAR() | war(); // Note: SINGLE '|' ORs to force full execution!
61 - }
62 -
63 - /**
64 - * Unzips all JARs in the classpath and creates a single JAR containing those files and this Project's JAR (which MUST exist).
65 - * The manifest from the project's JAR is used. Putting everything into a single JAR makes it harder to see what libraries are
66 - * being used, but makes it easier for end users to distribute the application.
67 - * <p/>
68 - * Note: Files with the same path in different JARs will be overwritten. Files in the project's JAR will never be overwritten,
69 - * but may overwrite other files.
70 - *
71 - * @param pExcludeJARs The names of any JARs to exclude.
72 - *
73 - * @return True if the "OneJAR" was created / updated or false if no OneJar is NOT requested for this project or it was not needed.
74 - */
75 - public boolean oneJAR( String... pExcludeJARs )
76 - {
77 - File zOneJarPath = getOneJarPathFile();
78 -
79 - if ( zOneJarPath == null )
80 - {
81 - return false;
82 - }
83 -
84 - File zJarPath = getJarPathFile();
85 - if ( !zJarPath.isFile() )
86 - {
87 - throw new IllegalStateException( "One JAR: " + this + " requested, BUT NO jar File produced at: " + zJarPath.getPath() );
88 - }
89 -
90 - if ( zOneJarPath.isFile() && (zOneJarPath.lastModified() >= zJarPath.lastModified()) )
91 - {
92 - progress( "One JAR: " + this + " NOT Needed!" );
93 - return false;
94 - }
95 -
96 - Paths zClasspath = classpath();
97 - if ( zClasspath.isEmpty() )
98 - {
99 - progress( "One JAR: " + this + " No supporting Jars! Simply Copying to: " + zOneJarPath.getPath() );
100 - copyFile( zJarPath, zOneJarPath );
101 - return true;
102 - }
103 - progress( "One JAR: " + this );
104 -
105 - File zOnejarDir = mkdir( new File( path( "$target$/onejar/" ) ) );
106 -
107 - List<String> zExcludeJARs = Arrays.asList( pExcludeJARs );
108 - for ( File jarFile : zClasspath.getFiles() ) // All our Class Path (dependant) JARS
109 - {
110 - if ( !zExcludeJARs.contains( jarFile.getName() ) )
111 - {
112 - unzip( jarFile, zOnejarDir );
113 - }
114 - }
115 -
116 - unzip( zJarPath, zOnejarDir ); // Our Jar! - Our Manifest will be "the" Manifest !!!!!! Need to remove class PATH!
117 - innerJar( "'ONE' JAR", zOneJarPath.getPath(), new Paths( zOnejarDir.getPath() ) );
118 - return true;
119 - }
120 -
121 - /**
122 - * Collects the distribution files using the "dist" property, the project's JAR file, and everything on the project's classpath
123 - * (including dependency project classpaths) and places them into the specified directory. This is also done for depenency projects,
124 - * recursively. This is everything the application needs to be run from JAR files.
125 - *
126 - * @return True if the PhoneGapDir was populated or false if no distribution (App Dir) is requested for this project.
127 - */
128 - public boolean phoneGapDir()
129 - {
130 - String zPhoneGapDir = getPhoneGapDirPath();
131 - if ( zPhoneGapDir == null )
132 - {
133 - return false;
134 - }
135 - Paths zPaths = new Paths( getGWTwarPath(), "**.js", "**.gif" );
136 - zPaths.add( getDist() );
137 -
138 - File zPhoneGapDirFile = new File( zPhoneGapDir );
139 - if ( zPhoneGapDirFile.exists() )
140 - {
141 - if ( zPhoneGapDirFile.lastModified() >= zPaths.getGreatestLastModified() )
142 - {
143 - progress( "PhoneGapDir: " + this + " NOT Needed!" );
144 - return false;
145 - }
146 - delete( zPhoneGapDirFile );
147 - }
148 -
149 - progress( "PhoneGapDir: " + this + " -> " + zPhoneGapDir );
150 - String distDir = mkdir( zPhoneGapDir ); // Give it a new Timestamp
151 - zPaths.copyTo( distDir );
152 - return true;
153 - }
154 -
155 - /**
156 - * Collects the distribution files using the "dist" property, the project's JAR file, and everything on the project's classpath
157 - * (including dependency project classpaths) and places them into the specified directory. This is also done for depenency projects,
158 - * recursively. This is everything the application needs to be run from JAR files.
159 - *
160 - * @return True if the AppDir was populated or false if no distribution (App Dir) is requested for this project.
161 - */
162 - public boolean appDir()
163 - {
164 - String zAppDir = getAppDirPath();
165 - if ( zAppDir == null )
166 - {
167 - return false;
168 - }
169 - File zJarPath = getJarPathFile();
170 - if ( !zJarPath.isFile() )
171 - {
172 - progress( "AppDir: " + this + " BUT NO jar File produced at: " + zJarPath.getPath() );
173 - return false;
174 - }
175 - Paths zPaths = new Paths();
176 - addDependantProjectsDistPaths( zPaths );
177 - zPaths.add( classpath() );
178 - zPaths.add( FilePath.canonicalize( getJarPathFile() ) );
179 -
180 - File zAppDirFile = new File( zAppDir );
181 - if ( zAppDirFile.exists() )
182 - {
183 - if ( zAppDirFile.lastModified() >= zPaths.getGreatestLastModified() )
184 - {
185 - progress( "AppDir: " + this + " NOT Needed!" );
186 - return false;
187 - }
188 - delete( zAppDirFile );
189 - }
190 -
191 - progress( "AppDir: " + this + " -> " + zAppDir );
192 - String distDir = mkdir( zAppDir ); // Give it a new Timestamp
193 - zPaths.copyTo( distDir );
194 - return true;
195 - }
196 -
197 - /**
198 - * Produce either a 'war' directory or a '.war' file, in theory ready to deploy to a servlet/web container.
199 - *
200 - * @return true if the 'war' was created.
201 - */
202 - public boolean war()
203 - {
204 - String zWar = getWar();
205 - if ( zWar == null )
206 - {
207 - return false;
208 - }
209 - File zJarPath = getJarPathFile();
210 - if ( !zJarPath.isFile() )
211 - {
212 - progress( "WAR: " + this + " BUT NO jar File produced at: " + zJarPath.getPath() );
213 - return false;
214 - }
215 -
216 - Paths zDistPaths = new Paths();
217 - addDependantProjectsDistPaths( zDistPaths );
218 - if ( null != getGWT() )
219 - {
220 - zDistPaths.add( new Paths( getGWTwarPath() ) );
221 - }
222 -
223 - Paths zClassPath = new Paths();
224 - zClassPath.add( FilePath.canonical( getGWTatDir(), GWT_SERVLET ) );
225 - zClassPath.add( classpath() );
226 - zClassPath.add( FilePath.canonicalize( getJarPathFile() ) );
227 -
228 - File zWarPathFile = getWarPathFile();
229 - if ( zWarPathFile.exists() )
230 - {
231 - long zWarLastModified = zWarPathFile.lastModified();
232 - if ( (zWarLastModified >= zClassPath.getGreatestLastModified()) && (zWarLastModified >= zDistPaths.getGreatestLastModified()) )
233 - {
234 - progress( "WAR: " + this + " NOT Needed!" );
235 - return false;
236 - }
237 - delete( zWarPathFile );
238 - }
239 -
240 - boolean zWarIt = zWar.endsWith( ".war" );
241 -
242 - File zWarDir = zWarPathFile;
243 - if ( zWarIt )
244 - {
245 - zWarDir = new File( path( "$target$/war/" ) );
246 - delete( zWarDir );
247 - }
248 -
249 - progress( "WAR: " + this + " -> " + zWarDir.getPath() );
250 -
251 - File zWarDirLibPath = new File( zWarDir, "WEB-INF/lib" );
252 - mkdir( zWarDirLibPath );
253 -
254 - zDistPaths.copyTo( zWarDir.getPath() );
255 - zClassPath.copyTo( zWarDirLibPath.getPath() );
256 -
257 - if ( zWarIt )
258 - {
259 - innerJar( "WAR", zWarPathFile.getPath(), new Paths( zWarDir.getPath() ) );
260 - }
261 - return true;
262 - }
263 -
264 - /**
265 - * Computes the classpath for all the dependencies of the specified project, recursively.
266 - */
267 - protected void addDependantProjectsDistPaths( Paths pPathsToAddTo )
268 - {
269 - for ( Project zProject : mDependantProjects )
270 - {
271 - zProject.addDependantProjectsDistPaths( pPathsToAddTo );
272 - }
273 - pPathsToAddTo.add( getDist() );
274 - }
275 -
276 - protected boolean GWTcompileIt()
277 - {
278 - String[] args = //
279 - { //
280 - getPathJavaJRE(), //
281 - "-Xmx" + getGWTmx(), //
282 - "-cp", //
283 - buildGWTcompileClassPath(), //
284 - "com.google.gwt.dev.Compiler", //
285 - "-logLevel", //
286 - getGWTlogging(), //
287 - "-war", //
288 - getGWTwarPath(), //
289 - "-style", //
290 - getGWTstyle(), //
291 - getGWT() //
292 - };
293 -
294 - Utils.shell( args );
295 - return true;
296 - }
297 -
298 - private String buildGWTcompileClassPath()
299 - {
300 - Paths zGWTclassPath = new Paths();
301 - File zGWTatDir = getGWTatDir();
302 - zGWTclassPath.add( FilePath.canonical( zGWTatDir, GWT_DEV ) );
303 - zGWTclassPath.add( FilePath.canonical( zGWTatDir, GWT_USER ) );
304 - zGWTclassPath.add( FilePath.canonical( zGWTatDir, GWT_VALIDATION ) );
305 - zGWTclassPath.add( FilePath.canonical( zGWTatDir, GWT_VALIDATION_SOURCE ) );
306 - if ( mSources )
307 - {
308 - zGWTclassPath.add( FilePath.canonicalize( getJarPathFile() ) );
309 - }
310 - zGWTclassPath.add( classpath() );
311 - return zGWTclassPath.toString( File.pathSeparator );
312 - }
313 -
314 - protected String getPathJavaJRE()
315 - {
316 - File zJavaHomeDir = assertIsDirectory( JAVA_HOME, new File( assertNotEmpty( JAVA_HOME, System.getenv( JAVA_HOME ) ) ) );
317 - File zJavaDir = new File( zJavaHomeDir, "jre/bin" );
318 - if ( !zJavaDir.isDirectory() )
319 - {
320 - if ( !(zJavaDir = new File( zJavaHomeDir, "bin" )).isDirectory() )
321 - {
322 - throw new IllegalStateException( "Unable to find JAVA_HOME bin directory under: " + zJavaHomeDir );
323 - }
324 - }
325 - File zJavaExecutable = new File( zJavaDir, isWindows ? "java.exe" : "java" );
326 - if ( zJavaExecutable.isFile() )
327 - {
328 - return getCanonicalFile( zJavaExecutable ).getPath();
329 - }
330 - throw new IllegalStateException( "Unable to find JAVA_HOME based executable at: " + zJavaExecutable );
331 - }
332 -
333 - protected File getGeneratedGWT_nocache_jsFile()
334 - {
335 - String zGWTxmlRelativeFilePath = getGWT().replace( '.', '/' ) + ".gwt.xml"; // e.g. org.litesoft.sandbox.csapp.CSapp
336 - File zFound = getGeneratedGWT_nocache_jsFile( zGWTxmlRelativeFilePath, getSource() );
337 - if ( zFound == null )
338 - {
339 - if ( null == (zFound = getGeneratedGWT_nocache_jsFile( zGWTxmlRelativeFilePath, getResources() )) )
340 - {
341 - throw new IllegalArgumentException( "Unable to locate GWT module file: " + getGWT() );
342 - }
343 - }
344 - return zFound;
345 - }
346 -
347 - private File getGeneratedGWT_nocache_jsFile( String pGWTxmlRelativeFilePath, Paths pPaths )
348 - {
349 - RootedPaths[] zRootedPaths = pPaths.getRootedPaths();
350 - for ( RootedPaths zPath : zRootedPaths )
351 - {
352 - File zFile = new File( zPath.getCanonicalRootDirectory(), pGWTxmlRelativeFilePath );
353 - if ( zFile.isFile() )
354 - {
355 - String moduleName = extractModuleNameFrom( getGWT(), fileContents( zFile ) );
356 - return new File( getGWTwarPath(), moduleName + "/" + moduleName + ".nocache.js" );
357 - }
358 - }
359 - return null;
360 - }
361 -
362 - private String extractModuleNameFrom( String pGWTmoduleReference, String pGWTmoduleFileContents )
363 - {
364 - int at = pGWTmoduleFileContents.indexOf( " rename-to" );
365 - if ( at != -1 ) // . . . . . . . . . . . .01234567890
366 - {
367 - int upTo = pGWTmoduleFileContents.indexOf( '>', at += 10 );
368 - if ( upTo != -1 )
369 - {
370 - String stuff = pGWTmoduleFileContents.substring( at, upTo ).trim();
371 - if ( stuff.startsWith( "=" ) )
372 - {
373 - if ( (stuff = stuff.substring( 1 ).trim()).length() > 2 )
374 - {
375 - char c = stuff.charAt( 0 );
376 - if ( (c == '"') || (c == '\'') )
377 - {
378 - if ( -1 != (at = stuff.indexOf( c, 1 )) )
379 - {
380 - return stuff.substring( 1, at );
381 - }
382 - }
383 - }
384 - }
385 - }
386 - }
387 - String s = "." + pGWTmoduleReference;
388 - return s.substring( s.lastIndexOf( '.' ) + 1 );
389 - }
390 -
391 - protected boolean needToCompileGWT()
392 - {
393 - File zGeneratedGWT_nocache_jsFile = getGeneratedGWT_nocache_jsFile();
394 - return needToBuild( ((zGeneratedGWT_nocache_jsFile != null) && zGeneratedGWT_nocache_jsFile.isFile()) ?
395 - zGeneratedGWT_nocache_jsFile.lastModified() : forceBuildLastModified() );
396 - }
397 -
398 - public boolean GWTcompile()
399 - {
400 - String zGWT = getGWT();
401 - if ( zGWT == null )
402 - {
403 - return false;
404 - }
405 - if ( !needToCompileGWT() )
406 - {
407 - progress( "GWT Compile: " + this + " NOT Needed!" );
408 - return false;
409 - }
410 - progress( "GWT Compile: " + this );
411 - return GWTcompileIt();
412 - }
413 -
414 - /**
415 - * Assert that this project is currently a 'Versioned' GWT project, and then rev the version number by 1
416 - */
417 - public void versionGWT()
418 - {
419 - File zWarWebXmlFile = new File( mCanonicalProjectDir, "war/WEB-INF/web.xml" );
420 - String zWarWebXml = fileContents( assertIsFile( "web.xml", zWarWebXmlFile ) );
421 -
422 - int zCurVersion = extractVersionFromUrlPattern( zWarWebXml );
423 -
424 - String zWarResourceRelativePathCurrent = "warResources/v" + zCurVersion;
425 -
426 - File zIndexHtmlFile = new File( mCanonicalProjectDir, zWarResourceRelativePathCurrent + "/index.html" );
427 - String zIndexHtml = assertVersionedIndexHtml( zIndexHtmlFile, zCurVersion );
428 -
429 - List<File> zVersionedGwtXmlFiles = findVersionedGwtXmlFiles( zCurVersion );
430 -
431 - int zNewVersion = zCurVersion + 1;
432 -
433 - String zWarResourceRelativePathNew = "warResources/v" + zNewVersion;
434 - if ( new File( mCanonicalProjectDir, zWarResourceRelativePathNew ).exists() )
435 - {
436 - throw new IllegalStateException( "Project already contains a 'warResources/v" + zNewVersion + "' directory?" );
437 - }
438 -
439 - progress( "versionGWT: " + this + " | " + zCurVersion + " -> " + (zCurVersion + 1) );
440 - progress( " " + zWarWebXmlFile.getPath() );
441 - progress( " " + zIndexHtmlFile.getPath() );
442 - for ( File zFile : zVersionedGwtXmlFiles )
443 - {
444 - progress( " " + zFile.getPath() );
445 - }
446 - progress( " " + zWarResourceRelativePathCurrent + " -> " + zWarResourceRelativePathNew );
447 -
448 - new Paths( zWarResourceRelativePathCurrent ).copyTo( zWarResourceRelativePathNew );
449 -
450 - updateFileContents( new File( mCanonicalProjectDir, zWarResourceRelativePathNew + "/index.html" ),
451 - updateVersionedIndexHtml( zIndexHtml, zCurVersion, zNewVersion ) );
452 -
453 - updateFileContents( zWarWebXmlFile, updateVersionedWebXml( zWarWebXml, zCurVersion, zNewVersion ) );
454 -
455 - for ( File zFile : zVersionedGwtXmlFiles )
456 - {
457 - updateFileContents( zFile, updateVersionedGwtXmlFile( fileContents( zFile ), zCurVersion, zNewVersion ) );
458 - }
459 - // Update/Create the Current Version's redirect JavaScript file
460 - String zCurPathVersion = "/v" + zCurVersion;
461 - String redirectScript = "var loc = window.location.href;\n" + //
462 - "var at = loc.indexOf( '" + zCurPathVersion + "' );\n" + //
463 - "window.location.href = loc.substring(0, at) + '/v" + zNewVersion + "' + loc.substring(at + " + zCurPathVersion.length() + ");\n";
464 - updateFileContents( new File( mCanonicalProjectDir, zWarResourceRelativePathCurrent + "/v" + zCurVersion + ".nocache.js" ), redirectScript );
465 -
466 - // Update the "root" html (if it exists)
467 - File zRootHtmlFile = new File( mCanonicalProjectDir, "warResources/index.html" );
468 - if ( zRootHtmlFile.isFile() )
469 - {
470 - updateFileContents( zRootHtmlFile, updateRootHTML( fileContents( zRootHtmlFile ), zCurVersion, zNewVersion ) );
471 - }
472 - }
473 -
474 - protected String updateRootHTML( String pFileContents, int pCurVersion, int pNewVersion )
475 - {
476 - // <!DOCTYPE html>
477 - // <html>
478 - // <head>
479 - // <meta http-equiv="Refresh" content="1; url=v1/">
480 - // </head>
481 - // <body>
482 - // <script>window.location.href = 'v1/';</script>
483 - // </body>
484 - // </html>
485 - String zCurPathVersion = "v" + pCurVersion + "/";
486 - String zNewPathVersion = "v" + pNewVersion + "/";
487 - for ( int at; -1 != (at = pFileContents.indexOf( zCurPathVersion )); )
488 - {
489 - pFileContents = pFileContents.substring( 0, at ) + zNewPathVersion + pFileContents.substring( at + zCurPathVersion.length() );
490 - }
491 - return pFileContents;
492 - }
493 -
494 - protected String updateVersionedGwtXmlFile( String pFileContents, int pCurVersion, int pNewVersion )
495 - {
496 - String zCurVersionedModule = VERSIONED_MODULE_PREFIX + pCurVersion + VERSIONED_MODULE_SUFFIX;
497 - String zNewVersionedModule = VERSIONED_MODULE_PREFIX + pNewVersion + VERSIONED_MODULE_SUFFIX;
498 - int at = pFileContents.indexOf( zCurVersionedModule );
499 - return pFileContents.substring( 0, at ) + zNewVersionedModule + pFileContents.substring( at + zCurVersionedModule.length() );
500 - }
501 -
502 - protected List<File> findVersionedGwtXmlFiles( int pVersion )
503 - {
504 - String zVersionedModule = VERSIONED_MODULE_PREFIX + pVersion + VERSIONED_MODULE_SUFFIX;
505 -
506 - ArrayList<File> zVersionedFiles = new ArrayList<File>();
507 -
508 - String zSourceString = get( SOURCE.getName() ) + "|";
509 - Paths zGwtXml = new Paths( zSourceString.substring( 0, zSourceString.indexOf( '|' ) ), "**.gwt.xml" );
510 - for ( File zFile : zGwtXml.getFiles() )
511 - {
512 - if ( fileContents( zFile ).contains( zVersionedModule ) )
513 - {
514 - zVersionedFiles.add( zFile );
515 - }
516 - }
517 - if ( zVersionedFiles.isEmpty() )
518 - {
519 - throw new IllegalStateException(
520 - "Project does not appear to contain a 'gwt.xml' file with the current version module definition of: " + zVersionedModule );
521 - }
522 - return zVersionedFiles;
523 - }
524 -
525 - protected String updateVersionedIndexHtml( String pFileContents, int pCurVersion, int pNewVersion )
526 - {
527 - String zCurVersionedScript = VERSIONED_SCRIPT_PREFIX + pCurVersion + VERSIONED_SCRIPT_SUFFIX;
528 - String zNewVersionedScript = VERSIONED_SCRIPT_PREFIX + pNewVersion + VERSIONED_SCRIPT_SUFFIX;
529 - int at = pFileContents.indexOf( zCurVersionedScript );
530 - return pFileContents.substring( 0, at ) + zNewVersionedScript + pFileContents.substring( at + zCurVersionedScript.length() );
531 - }
532 -
533 - protected String assertVersionedIndexHtml( File pIndexHtmlFile, int pVersion )
534 - {
535 - String zVersionedScript = VERSIONED_SCRIPT_PREFIX + pVersion + VERSIONED_SCRIPT_SUFFIX;
536 -
537 - String zContents = fileContents( assertIsFile( "Versioned index.html", pIndexHtmlFile ) );
538 - if ( !zContents.contains( zVersionedScript ) )
539 - {
540 - throw new IllegalStateException(
541 - "Project's current versioned index.html file (" + pIndexHtmlFile.getPath() + ") does not contain a 'versioned' script element of: " +
542 - zVersionedScript );
543 - }
544 - return zContents;
545 - }
546 -
547 - protected String updateVersionedWebXml( String pFileContents, int pCurVersion, int pNewVersion )
548 - {
549 - String zCurVersionedUrlPattern = VERSIONED_URL_PATTERN_PREFIX + pCurVersion + "/";
550 - String zNewVersionedUrlPattern = VERSIONED_URL_PATTERN_PREFIX + pNewVersion + "/";
551 - for ( int at; -1 != (at = pFileContents.indexOf( zCurVersionedUrlPattern )); )
552 - {
553 - pFileContents = pFileContents.substring( 0, at ) + zNewVersionedUrlPattern + pFileContents.substring( at + zCurVersionedUrlPattern.length() );
554 - }
555 - return pFileContents;
556 - }
557 -
558 - protected int extractVersionFromUrlPattern( String pWarWebXml )
559 - {
560 - for ( int at, from = 0; -1 != (at = pWarWebXml.indexOf( VERSIONED_URL_PATTERN_PREFIX, from )); from = at + 1 )
561 - {
562 - int slashAt = pWarWebXml.indexOf( '/', at += VERSIONED_URL_PATTERN_PREFIX.length() );
563 - if ( slashAt > 0 )
564 - {
565 - try
566 - {
567 - return Integer.parseInt( pWarWebXml.substring( at, slashAt ) );
568 - }
569 - catch ( NumberFormatException acceptable )
570 - {
571 - // path starts w/ a 'v' but is not of pattern "v####"
572 - }
573 - }
574 - }
575 - throw new IllegalStateException( "Project's war/WEB-INF/web.xml does not appear to contain a 'versioned' <url-pattern>." );
576 - }
577 -
578 - /**
579 - * Executes the buildDependencies, clean, compile, jar, [GWTcompile], and then "packageIt" utility methods.
580 - */
581 - public synchronized boolean build()
582 - {
583 - if ( mBuilt )
584 - {
585 - return false;
586 - }
587 - mBuilt = true;
588 - mSources = !getSource().isEmpty();
589 - boolean zAnythingBuilt = false;
590 - boolean zBuildIt;
591 - try
592 - {
593 - zBuildIt = buildDependencies() || needToBuild();
594 - }
595 - catch ( RuntimeException e )
596 - {
597 - progress( "Build: " + this );
598 - throw e;
599 - }
600 - if ( !zBuildIt )
601 - {
602 - progress( "Build: " + this + " NOT Needed!" );
603 - }
604 - else
605 - {
606 - progress( "Build: " + this );
607 - clean();
608 - if ( mSources )
609 - {
610 - compile();
611 - jar();
612 - zAnythingBuilt = true;
613 - }
614 - }
615 - zAnythingBuilt |= GWTcompile();
616 - zAnythingBuilt |= packageIt();
617 - return zAnythingBuilt;
618 - }
619 -
620 - protected boolean needToBuild()
621 - {
622 - return needToBuild( determineOutputLastModified() );
623 - }
624 -
625 - protected boolean needToBuild( long pOutputLastModified )
626 - {
627 - return (mProjectFileLastModified > pOutputLastModified) || //
628 - checkNewer( pOutputLastModified, "ClassPath", compileClasspath() ) || //
629 - checkNewer( pOutputLastModified, "Source", getSource() ) || //
630 - checkNewer( pOutputLastModified, "Resources", getResources() ) || //
631 - checkNewer( pOutputLastModified, "Dist", getDist() );
632 - }
633 -
634 - protected boolean checkNewer( long pOutputLastModified, String pWhat, Paths pPaths )
635 - {
636 - Long zLastModified = pPaths.getGreatestLastModified();
637 - if ( (zLastModified != null) && (zLastModified > pOutputLastModified) )
638 - {
639 - System.out.println( this + ": " + pWhat + " - " + new NewerBy( pOutputLastModified, zLastModified ) );
640 - return true;
641 - }
642 - return false;
643 - }
644 -
645 - protected long forceBuildLastModified()
646 - {
647 - return (mProjectFileLastModified - 1);
648 - }
649 -
650 - protected long determineOutputLastModified()
651 - {
652 - if ( mSources )
653 - {
654 - File zJarFile = getJarPathFile();
655 - if ( zJarFile.isFile() )
656 - {
657 - return zJarFile.lastModified();
658 - }
659 - }
660 - else
661 - {
662 - String zPhoneGapDir = getPhoneGapDirPath();
663 - if ( zPhoneGapDir != null )
664 - {
665 - File zPhoneGapDirFile = new File( zPhoneGapDir );
666 - if ( zPhoneGapDirFile.isDirectory() )
667 - {
668 - return zPhoneGapDirFile.lastModified();
669 - }
670 - }
671 - }
672 - return forceBuildLastModified();
673 - }
674 -
675 - /**
676 - * Deletes the "target" directory and all files and directories under it.
677 - */
678 - public void clean()
679 - {
680 - progress( "Clean: " + this );
681 - packageClean();
682 - delete( getGWTwarPath() );
683 - delete( getJarPath() );
684 - delete( getTargetPath() );
685 - }
686 -
687 - /**
688 - * Collects the source files using the "source" property and compiles them into a "classes" directory under the target
689 - * directory. It uses "classpath" and "dependencies" to find the libraries required to compile the source.
690 - * <p/>
691 - * Note: Each dependency project is not built automatically. Each needs to be built before the dependent project.
692 - *
693 - * @return The path to the "classes" directory or null if there was no sources to compile
694 - */
695 - public String compile()
696 - {
697 - Paths source = getSource();
698 - if ( source.isEmpty() )
699 - {
700 - return null;
701 - }
702 - Paths classpath = compileClasspath();
703 -
704 - String classesDir = mkdir( path( "$target$/classes/" ) );
705 -
706 - String zMessage = "Compile: " + this;
707 - if ( LOGGER.debug.isEnabled() )
708 - {
709 - zMessage += " | " + source.count() + " source files";
710 - if ( !classpath.isEmpty() )
711 - {
712 - zMessage += "\n Classpath: " + classpath;
713 - }
714 - }
715 - progress( zMessage );
716 -
717 - List<String> zCompileArgs = createCompileJavaArgs( classpath, source, classesDir );
718 -
719 - compileJava( classpath, source, zCompileArgs );
720 -
721 - return classesDir;
722 - }
723 -
724 - protected List<String> createCompileJavaArgs( Paths pClasspath, Paths pSource, String pClassesDir )
725 - {
726 - List<String> args = new ArrayList<String>();
727 - if ( LOGGER.trace.isEnabled() )
728 - {
729 - args.add( "-verbose" );
730 - }
731 - args.add( "-d" );
732 - args.add( pClassesDir );
733 - args.add( "-g:source,lines" );
734 - args.add( "-source" );
735 - args.add( getSourceJavaVersion() );
736 - args.addAll( pSource.getFullPaths() );
737 - if ( !pClasspath.isEmpty() )
738 - {
739 - args.add( "-classpath" );
740 - args.add( pClasspath.toString( File.pathSeparator ) );
741 - }
742 - return args;
743 - }
744 -
745 - protected void compileJava( Paths pClasspath, Paths pSource, List<String> pCompileArgs )
746 - {
747 - JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
748 -
749 - if ( compiler == null )
750 - {
751 - throw new RuntimeException( "No compiler available. Ensure you are running from a " + getTargetJavaVersion() +
752 - "+ JDK, and not a JRE *and* that your class path includes tools.jar." );
753 - }
754 - int zError = compiler.run( getCompile_in(), getCompile_out(), getCompile_err(), pCompileArgs.toArray( new String[pCompileArgs.size()] ) );
755 - if ( zError != 0 )
756 - {
757 - String zMessage = "Error (" + zError + ") during compilation of project: " + this +
758 - "\nSource: " + pSource.count() + " files\nCompilerArgs: " + pCompileArgs;
759 - if ( LOGGER.debug.isEnabled() )
760 - {
761 - zMessage += "\nClasspath: " + pClasspath + "\nSource: " + pSource.toString( " " );
762 - }
763 - throw new RuntimeException( zMessage );
764 - }
765 - try
766 - {
767 - Thread.sleep( 100 );
768 - }
769 - catch ( InterruptedException ex )
770 - {
771 - // Whatever
772 - }
773 - }
774 -
775 - protected InputStream getCompile_in()
776 - {
777 - return null;
778 - }
779 -
780 - protected OutputStream getCompile_out()
781 - {
782 - return null;
783 - }
784 -
785 - protected OutputStream getCompile_err()
786 - {
787 - return new OutputStream()
788 - {
789 - private StringBuilder mBuffer = new StringBuilder();
790 - private boolean mLastWasCRtreatedAsLF = false;
791 -
792 - private void dumpLine( int pByte )
793 - {
794 - if ( pByte == 13 )
795 - {
796 - mLastWasCRtreatedAsLF = true;
797 - pByte = 10;
798 - }
799 - else
800 - {
801 - boolean zLastWasCRtreatedAsLF = mLastWasCRtreatedAsLF;
802 - mLastWasCRtreatedAsLF = false;
803 - if ( (pByte == 10) && zLastWasCRtreatedAsLF )
804 - {
805 - return;
806 - }
807 - }
808 - mBuffer.append( (char) pByte ); // Assuming Ascii!
809 - String line = mBuffer.toString();
810 - mBuffer = new StringBuilder();
811 - if ( !line.startsWith( "Note: " ) )
812 - {
813 - System.err.print( line );
814 - }
815 - }
816 -
817 - @Override
818 - public void write( int pByte ) // Not a Unicode character, just a BYTE! --- Asumming Ascii ---
819 - throws IOException
820 - {
821 - if ( (10 <= pByte) && (pByte <= 13) ) // New Line Indicator
822 - { // LF: Line Feed, U+000A
823 - dumpLine( pByte ); // VT: Vertical Tab, U+000B
824 - return; // FF: Form Feed, U+000C
825 - } // CR: Carriage Return, U+000D
826 - mBuffer.append( (char) pByte ); // Assuming Ascii!
827 - }
828 - };
829 - }
830 -
831 - /**
832 - * Collects the class files from the "classes" directory and all the resource files using the "resources" property and encodes
833 - * them into a JAR file.
834 - * <p/>
835 - * If the resources don't contain a META-INF/MANIFEST.MF file, one is generated. If the project has a main property, the
836 - * generated manifest will include "Main-Class" and "Class-Path" entries to allow the main class to be run with "java -jar".
837 - *
838 - * @return The path to the created JAR file or null if No JAR created.
839 - */
840 - public String jar()
841 - {
842 - String zJarPath = getJarPath();
843 -
844 - Paths zClasses = new Paths( path( "$target$/classes/" ), "**.class" );
845 - Paths zResources = getResources();
846 - if ( zClasses.isEmpty() && zResources.isEmpty() )
847 - {
848 - delete( zJarPath );
849 - return null;
850 - }
851 - progress( "JAR: " + this + " -> " + zJarPath );
852 -
853 - String jarDir = mkdir( path( "$target$/jar/" ) );
854 -
855 - zClasses.copyTo( jarDir );
856 - zResources.copyTo( jarDir );
857 -
858 - File manifestFile = new File( jarDir, "META-INF/MANIFEST.MF" );
859 - if ( !manifestFile.exists() )
860 - {
861 - createDefaultManifestFile( zJarPath, manifestFile );
862 - }
863 -
864 - return jar( zJarPath, new Paths( jarDir ) );
865 - }
866 -
867 - protected void createDefaultManifestFile( String pJarFile, File pManifestFile )
868 - {
869 - LOGGER.debug.log( "Generating JAR manifest: ", pManifestFile );
870 - mkdir( pManifestFile.getParent() );
871 - Manifest manifest = new Manifest();
872 - manifest.getMainAttributes().putValue( Attributes.Name.MANIFEST_VERSION.toString(), "1.0" );
873 - if ( hasMain() )
874 - {
875 - LOGGER.debug.log( "Main class: ", getMain() );
876 - manifest.getMainAttributes().putValue( Attributes.Name.MAIN_CLASS.toString(), getMain() );
877 - StringBuilder buffer = new StringBuilder( 512 );
878 - buffer.append( Utils.fileName( pJarFile ) );
879 - buffer.append( " ." );
880 - Paths classpath = classpath();
881 - for ( String name : classpath.getRelativePaths( pJarFile ) )
882 - {
883 - buffer.append( ' ' );
884 - buffer.append( name );
885 - }
886 - manifest.getMainAttributes().putValue( Attributes.Name.CLASS_PATH.toString(), buffer.toString() );
887 - }
888 - OutputStream output = createFileOutputStream( pManifestFile );
889 - try
890 - {
891 - manifest.write( output );
892 - Closeable zCloseable = output;
893 - output = null;
894 - close( zCloseable );
895 - }
896 - catch ( IOException e )
897 - {
898 - throw new WrappedIOException( e );
899 - }
900 - finally
901 - {
902 - dispose( output );
903 - }
904 - }
905 -
906 - /**
907 - * Encodes the specified paths into a JAR file.
908 - *
909 - * @return The path to the JAR file.
910 - */
911 - public String jar( String jarFile, Paths paths )
912 - {
913 - return innerJar( "JAR", jarFile, paths );
914 - }
915 -
916 - /**
917 - * Encodes the specified paths into a JAR/WAR file.
918 - *
919 - * @return The path to the JAR/WAR file.
920 - */
921 - protected String innerJar( String pType, String jarFile, Paths paths )
922 - {
923 - Util.assertNotNull( "jarFile", jarFile );
924 - Util.assertNotNull( "paths", paths );
925 -
926 - progress( "Creating " + pType + " (" + paths.count() + " entries): " + jarFile );
927 -
928 - int zZipped = paths.zip( jarFile, new ZipFactory()
929 - {
930 - @Override
931 - public ZipOutputStream createZOS( String pFilePath, List<FilePath> pPaths )
932 - {
933 - putManifestFirst( pPaths );
934 - try
935 - {
936 - return new JarOutputStream( FileUtil.createBufferedFileOutputStream( pFilePath ) );
937 - }
938 - catch ( IOException e )
939 - {
940 - throw new WrappedIOException( e );
941 - }
942 - }
943 -
944 - @Override
945 - public ZipEntry createZE( String pRelativePath )
946 - {
947 - return new JarEntry( pRelativePath );
948 - }
949 -
950 - private void putManifestFirst( List<FilePath> pPaths )
951 - {
952 - int at = findManifest( pPaths );
953 - if ( at > 0 )
954 - {
955 - FilePath zManifest = pPaths.remove( at );
956 - pPaths.add( 0, zManifest );
957 - }
958 - }
959 -
960 - private int findManifest( List<FilePath> pPaths )
961 - {
962 - for ( int i = 0; i < pPaths.size(); i++ )
963 - {
964 - if ( META_INF_MANIFEST_MF.equals( pPaths.get( i ).getFileSubPath() ) )
965 - {
966 - return i;
967 - }
968 - }
969 - return -1;
970 - }
971 - } );
972 - return zZipped == 0 ? null : jarFile;
973 - }
974 -
975 - /**
976 - * Decodes the specified ZIP file.
977 - *
978 - * @return The path to the output directory.
979 - */
980 - protected void quiteUnzip( File zipFile, File outputDir )
981 - {
982 - ZipInputStream input = new ZipInputStream( createFileInputStream( zipFile ) );
983 - try
984 - {
985 - for ( ZipEntry entry; null != (entry = input.getNextEntry()); )
986 - {
987 - File file = new File( outputDir, entry.getName() );
988 - if ( entry.isDirectory() )
989 - {
990 - mkdir( file.getPath() );
991 - continue;
992 - }
993 - writeStream( input, createFileOutputStream( file ) );
994 - }
995 - }
996 - catch ( IOException e )
997 - {
998 - throw new WrappedIOException( e );
999 - }
1000 - finally
1001 - {
1002 - dispose( input );
1003 - }
1004 - }
1005 -
1006 - /**
1007 - * Decodes the specified ZIP file.
1008 - */
1009 - public void unzip( File zipFile, File outputDir )
1010 - {
1011 - Util.assertNotNull( "zipFile", zipFile );
1012 - Util.assertNotNull( "outputDir", outputDir );
1013 - progress( "ZIP decoding: " + zipFile.getPath() + " -> " + outputDir.getPath() );
1014 - quiteUnzip( zipFile, outputDir );
1015 - }
1016 -
1017 - /**
1018 - * Decodes the specified ZIP file.
1019 - *
1020 - * @return The path to the output directory.
1021 - */
1022 - public String unzip( String zipFile, String outputDir )
1023 - {
1024 - zipFile = assertNotEmpty( "zipFile", zipFile );
1025 - outputDir = assertNotEmpty( "outputDir", outputDir );
1026 - progress( "ZIP decoding: " + zipFile + " -> " + outputDir );
1027 - quiteUnzip( new File( zipFile ), new File( outputDir ) );
1028 - return outputDir;
1029 - }
1030 -
1031 - /**
1032 - * Computes the classpath for the specified project and all its dependency projects, recursively.
1033 - */
1034 - protected Paths compileClasspath()
1035 - {
1036 - Paths classpath = getCompileClasspath();
1037 - classpath.add( getClasspath() );
1038 - for ( Project zProject : mDependantProjects )
1039 - {
1040 - zProject.addDependantProjectsCompileClassPaths( classpath );
1041 - }
1042 - return classpath;
1043 - }
1044 -
1045 - /**
1046 - * Computes the classpath for all the dependencies of the specified project, recursively.
1047 - */
1048 - protected void addDependantProjectsCompileClassPaths( Paths pPathsToAddTo )
1049 - {
1050 - addDependentProjectJar( pPathsToAddTo );
1051 - pPathsToAddTo.add( compileClasspath() );
1052 - }
1053 -
1054 - /**
1055 - * Computes the classpath for the specified project and all its dependency projects, recursively.
1056 - */
1057 - protected Paths classpath()
1058 - {
1059 - Paths classpath = getClasspath();
1060 - for ( Project zProject : mDependantProjects )
1061 - {
1062 - zProject.addDependantProjectsClassPaths( classpath );
1063 - }
1064 - return classpath;
1065 - }
1066 -
1067 - /**
1068 - * Computes the classpath for all the dependencies of the specified project, recursively.
1069 - */
1070 - protected void addDependantProjectsClassPaths( Paths pPathsToAddTo )
1071 - {
1072 - addDependentProjectJar( pPathsToAddTo );
1073 - pPathsToAddTo.add( classpath() );
1074 - }
1075 -
1076 - protected void addDependentProjectJar( Paths pPathsToAddTo )
1077 - {
1078 - if ( mSources )
1079 - {
1080 - File zJarFile = getJarPathFile();
1081 - if ( !zJarFile.isFile() )
1082 - {
1083 - throw new RuntimeException( "Dependency (" + this + ") Jar not found, not built?" );
1084 - }
1085 - pPathsToAddTo.add( new FilePath( zJarFile.getParentFile(), zJarFile.getName() ) );
1086 - }
1087 - }
1088 -
1089 - /**
1090 - * Calls {@link #build(Project)} for each dependency project in the specified project.
1091 - */
1092 - public boolean buildDependencies()
1093 - {
1094 - boolean anyBuilt = false;
1095 - for ( Project zProject : mDependantProjects )
1096 - {
1097 - anyBuilt |= zProject.build();
1098 - }
1099 - return anyBuilt;
1100 - }
1101 -
1102 - public void set( Object key, Object object )
1103 - {
1104 - mManager.put( updatableKey( key ), object );
1105 - }
1106 -
1107 - public void remove( Object key )
1108 - {
1109 - set( key, null );
1110 - }
1111 -
1112 - /**
1113 - * Removes an item from a list or map. If the mData under the specified key is a list, the entry equal to the specified value is
1114 - * removed. If the mData under the specified key is a map, the entry with the key specified by value is removed.
1115 - */
1116 - public void remove( Object key, Object value )
1117 - {
1118 - mManager.remove( updatableKey( key ), value );
1119 - }
1120 -
1121 - private Object updatableKey( Object pKey )
1122 - {
1123 - if ( pKey instanceof String )
1124 - {
1125 - String zStrKey = noEmpty( pKey.toString().toLowerCase() );
1126 - if ( Parameter.reservedNames().contains( zStrKey ) )
1127 - {
1128 - throw new IllegalArgumentException( zStrKey + " not updatable!" );
1129 - }
1130 - pKey = zStrKey;
1131 - }
1132 - Util.assertNotNull( "key", pKey );
1133 - return pKey;
1134 - }
1135 -
1136 - public synchronized void initialize( ProjectFactory pProjectFactory )
1137 - {
1138 - List<String> zDependencies = getDependencies();
1139 - if ( zDependencies != null )
1140 - {
1141 - for ( String zDependency : zDependencies )
1142 - {
1143 - mDependantProjects.add( pProjectFactory.project( mCanonicalProjectDir, zDependency ) );
1144 - }
1145 - }
1146 -
1147 - // Project defaults = new Project();
1148 - //
1149 - // File file = new File( canonical( pPath ) );
1150 - // if ( file.isDirectory() )
1151 - // {
1152 - // String name = file.getName();
1153 - // defaults.set( "name", name );
1154 - // defaults.set( "target", file.getParent() + "/target/" + name + "/" );
1155 - // }
1156 - // else
1157 - // {
1158 - // String name = file.getParentFile().getName();
1159 - // defaults.set( "name", name );
1160 - // defaults.set( "target", file.getParentFile().getParent() + "/target/" + name + "/" );
1161 - // }
1162 - // defaults.set( "classpath", "lib|**/*.jar" );
1163 - // defaults.set( "dist", "dist" );
1164 - //
1165 - // List<String> source = new ArrayList<String>();
1166 - // source.add( "src|**/*.java" );
1167 - // source.add( "src/main/java|**/*.java" );
1168 - // defaults.set( "source", source );
1169 - //
1170 - // List<String> resources = new ArrayList<String>();
1171 - // resources.add( "resources" );
1172 - // resources.add( "src/main/resources" );
1173 - // defaults.set( "resources", resources );
1174 - //
1175 - // Project project = project( pPath, defaults );
1176 - //
1177 - // // Remove dependency if a JAR of the same name is on the classpath.
1178 - // Paths classpath = project.getPaths( "classpath" );
1179 - // classpath.add( dependencyClasspaths( project, classpath, false, false ) );
1180 - // for ( String dependency : project.getDependencies() )
1181 - // {
1182 - // String dependencyName = project( project.path( dependency ) ).getName();
1183 - // for ( String classpathFile : classpath )
1184 - // {
1185 - // String name = fileWithoutExtension( classpathFile );
1186 - // int dashIndex = name.lastIndexOf( '-' );
1187 - // if ( dashIndex != -1 )
1188 - // {
1189 - // name = name.substring( 0, dashIndex );
1190 - // }
1191 - // if ( name.equals( dependencyName ) )
1192 - // {
1193 - // if ( DEBUG )
1194 - // {
1195 - // debug( "Ignoring " + project + " dependency: " + dependencyName + " (already on classpath: " + classpathFile + ")" );
1196 - // }
1197 - // project.remove( "dependencies", dependency );
1198 - // break;
1199 - // }
1200 - // }
1201 - // }
1202 - //
1203 - // if ( TRACE )
1204 - // {
1205 - // trace( "scar", "Project: " + project + "\n" + project );
1206 - // }
1207 - //
1208 - // return project;
1209 - }
1210 -
1211 - protected boolean mBuilt = false;
1212 - protected boolean mSources = false;
1213 - protected List<Project> mDependantProjects = new ArrayList<Project>();
1214 - }
1 + package com.esotericsoftware.scar;
2 +
3 + import org.litesoft.logger.*;
4 +
5 + import com.esotericsoftware.filesystem.*;
6 + import com.esotericsoftware.scar.support.*;
7 + import com.esotericsoftware.utils.*;
8 +
9 + import javax.tools.*;
10 + import java.io.*;
11 + import java.util.*;
12 + import java.util.jar.*;
13 + import java.util.zip.*;
14 +
15 + /**
16 + * Generic Data structure that contains information needed to perform tasks.
17 + */
18 + @SuppressWarnings("UnusedDeclaration")
19 + public class Project extends ProjectParameters {
20 + private static final String JAVA_SOURCE_TARGET_VERSION = "1.7";
21 +
22 + protected static final Logger LOGGER = LoggerFactory.getLogger( Project.class );
23 +
24 + private static final String META_INF_MANIFEST_MF = "META-INF/MANIFEST.MF";
25 +
26 + private static final String VERSIONED_URL_PATTERN_PREFIX = "<url-pattern>/v";
27 + private static final String VERSIONED_SCRIPT_PREFIX = "<script src='v";
28 + private static final String VERSIONED_SCRIPT_SUFFIX = ".nocache.js'></script>";
29 + private static final String VERSIONED_MODULE_PREFIX = "<module rename-to=\"v";
30 + private static final String VERSIONED_MODULE_SUFFIX = "\">";
31 + private static final String JAVA_HOME = "JAVA_HOME";
32 +
33 + public Project( ProjectParameters pParameters ) {
34 + super( pParameters.validate() );
35 + applyDefaults();
36 + }
37 +
38 + public String getSourceJavaVersion() {
39 + return JAVA_SOURCE_TARGET_VERSION;
40 + }
41 +
42 + public String getTargetJavaVersion() {
43 + return JAVA_SOURCE_TARGET_VERSION;
44 + }
45 +
46 + protected void packageClean() {
47 + delete( getPhoneGapDirPath() );
48 + delete( getAppDirPath() );
49 + delete( getOneJarPath() );
50 + delete( getWarPath() );
51 + }
52 +
53 + protected boolean packageIt() {
54 + return phoneGapDir() | appDir() | oneJAR() | war(); // Note: SINGLE '|' ORs to force full execution!
55 + }
56 +
57 + /**
58 + * Unzips all JARs in the classpath and creates a single JAR containing those files and this Project's JAR (which MUST exist).
59 + * The manifest from the project's JAR is used. Putting everything into a single JAR makes it harder to see what libraries are
60 + * being used, but makes it easier for end users to distribute the application.
61 + * <p/>
62 + * Note: Files with the same path in different JARs will be overwritten. Files in the project's JAR will never be overwritten,
63 + * but may overwrite other files.
64 + *
65 + * @param pExcludeJARs The names of any JARs to exclude.
66 + *
67 + * @return True if the "OneJAR" was created / updated or false if no OneJar is NOT requested for this project or it was not needed.
68 + */
69 + public boolean oneJAR( String... pExcludeJARs ) {
70 + File zOneJarPath = getOneJarPathFile();
71 +
72 + if ( zOneJarPath == null ) {
73 + return false;
74 + }
75 +
76 + File zJarPath = getJarPathFile();
77 + if ( !zJarPath.isFile() ) {
78 + throw new IllegalStateException( "One JAR: " + this + " requested, BUT NO jar File produced at: " + zJarPath.getPath() );
79 + }
80 +
81 + if ( zOneJarPath.isFile() && (zOneJarPath.lastModified() >= zJarPath.lastModified()) ) {
82 + progress( "One JAR: " + this + " NOT Needed!" );
83 + return false;
84 + }
85 +
86 + Paths zClasspath = classpath();
87 + if ( zClasspath.isEmpty() ) {
88 + progress( "One JAR: " + this + " No supporting Jars! Simply Copying to: " + zOneJarPath.getPath() );
89 + copyFile( zJarPath, zOneJarPath );
90 + return true;
91 + }
92 + progress( "One JAR: " + this );
93 +
94 + File zOnejarDir = mkdir( new File( path( "$target$/onejar/" ) ) );
95 +
96 + List<String> zExcludeJARs = Arrays.asList( pExcludeJARs );
97 + for ( File jarFile : zClasspath.getFiles() ) // All our Class Path (dependant) JARS
98 + {
99 + if ( !zExcludeJARs.contains( jarFile.getName() ) ) {
100 + unzip( jarFile, zOnejarDir );
101 + }
102 + }
103 +
104 + unzip( zJarPath, zOnejarDir ); // Our Jar! - Our Manifest will be "the" Manifest !!!!!! Need to remove class PATH!
105 + innerJar( "'ONE' JAR", zOneJarPath.getPath(), new Paths( zOnejarDir.getPath() ) );
106 + return true;
107 + }
108 +
109 + /**
110 + * Collects the distribution files using the "dist" property, the project's JAR file, and everything on the project's classpath
111 + * (including dependency project classpaths) and places them into the specified directory. This is also done for depenency projects,
112 + * recursively. This is everything the application needs to be run from JAR files.
113 + *
114 + * @return True if the PhoneGapDir was populated or false if no distribution (App Dir) is requested for this project.
115 + */
116 + public boolean phoneGapDir() {
117 + String zPhoneGapDir = getPhoneGapDirPath();
118 + if ( zPhoneGapDir == null ) {
119 + return false;
120 + }
121 + Paths zPaths = new Paths( getGWTwarPath(), "**.js", "**.gif" );
122 + zPaths.add( getDist() );
123 +
124 + File zPhoneGapDirFile = new File( zPhoneGapDir );
125 + if ( zPhoneGapDirFile.exists() ) {
126 + if ( zPhoneGapDirFile.lastModified() >= zPaths.getGreatestLastModified() ) {
127 + progress( "PhoneGapDir: " + this + " NOT Needed!" );
128 + return false;
129 + }
130 + delete( zPhoneGapDirFile );
131 + }
132 +
133 + progress( "PhoneGapDir: " + this + " -> " + zPhoneGapDir );
134 + String distDir = mkdir( zPhoneGapDir ); // Give it a new Timestamp
135 + zPaths.copyTo( distDir );
136 + return true;
137 + }
138 +
139 + /**
140 + * Collects the distribution files using the "dist" property, the project's JAR file, and everything on the project's classpath
141 + * (including dependency project classpaths) and places them into the specified directory. This is also done for depenency projects,
142 + * recursively. This is everything the application needs to be run from JAR files.
143 + *
144 + * @return True if the AppDir was populated or false if no distribution (App Dir) is requested for this project.
145 + */
146 + public boolean appDir() {
147 + String zAppDir = getAppDirPath();
148 + if ( zAppDir == null ) {
149 + return false;
150 + }
151 + File zJarPath = getJarPathFile();
152 + if ( !zJarPath.isFile() ) {
153 + progress( "AppDir: " + this + " BUT NO jar File produced at: " + zJarPath.getPath() );
154 + return false;
155 + }
156 + Paths zPaths = new Paths();
157 + addDependantProjectsDistPaths( zPaths );
158 + zPaths.add( classpath() );
159 + zPaths.add( FilePath.canonicalize( getJarPathFile() ) );
160 +
161 + File zAppDirFile = new File( zAppDir );
162 + if ( zAppDirFile.exists() ) {
163 + if ( zAppDirFile.lastModified() >= zPaths.getGreatestLastModified() ) {
164 + progress( "AppDir: " + this + " NOT Needed!" );
165 + return false;
166 + }
167 + delete( zAppDirFile );
168 + }
169 +
170 + progress( "AppDir: " + this + " -> " + zAppDir );
171 + String distDir = mkdir( zAppDir ); // Give it a new Timestamp
172 + zPaths.copyTo( distDir );
173 + return true;
174 + }
175 +
176 + /**
177 + * Produce either a 'war' directory or a '.war' file, in theory ready to deploy to a servlet/web container.
178 + *
179 + * @return true if the 'war' was created.
180 + */
181 + public boolean war() {
182 + String zWar = getWar();
183 + if ( zWar == null ) {
184 + return false;
185 + }
186 + File zJarPath = getJarPathFile();
187 + if ( !zJarPath.isFile() ) {
188 + progress( "WAR: " + this + " BUT NO jar File produced at: " + zJarPath.getPath() );
189 + return false;
190 + }
191 +
192 + Paths zDistPaths = new Paths();
193 + addDependantProjectsDistPaths( zDistPaths );
194 + if ( null != getGWT() ) {
195 + zDistPaths.add( new Paths( getGWTwarPath() ) );
196 + }
197 +
198 + Paths zClassPath = new Paths();
199 + zClassPath.add( FilePath.canonical( getGWTatDir(), GWT_SERVLET ) );
200 + zClassPath.add( classpath() );
201 + zClassPath.add( FilePath.canonicalize( getJarPathFile() ) );
202 +
203 + File zWarPathFile = getWarPathFile();
204 + if ( zWarPathFile.exists() ) {
205 + long zWarLastModified = zWarPathFile.lastModified();
206 + if ( (zWarLastModified >= zClassPath.getGreatestLastModified()) && (zWarLastModified >= zDistPaths.getGreatestLastModified()) ) {
207 + progress( "WAR: " + this + " NOT Needed!" );
208 + return false;
209 + }
210 + delete( zWarPathFile );
211 + }
212 +
213 + boolean zWarIt = zWar.endsWith( ".war" );
214 +
215 + File zWarDir = zWarPathFile;
216 + if ( zWarIt ) {
217 + zWarDir = new File( path( "$target$/war/" ) );
218 + delete( zWarDir );
219 + }
220 +
221 + progress( "WAR: " + this + " -> " + zWarDir.getPath() );
222 +
223 + File zWarDirLibPath = new File( zWarDir, "WEB-INF/lib" );
224 + mkdir( zWarDirLibPath );
225 +
226 + zDistPaths.copyTo( zWarDir.getPath() );
227 + zClassPath.copyTo( zWarDirLibPath.getPath() );
228 +
229 + if ( zWarIt ) {
230 + innerJar( "WAR", zWarPathFile.getPath(), new Paths( zWarDir.getPath() ) );
231 + }
232 + return true;
233 + }
234 +
235 + /**
236 + * Computes the classpath for all the dependencies of the specified project, recursively.
237 + */
238 + protected void addDependantProjectsDistPaths( Paths pPathsToAddTo ) {
239 + for ( Project zProject : mDependantProjects ) {
240 + zProject.addDependantProjectsDistPaths( pPathsToAddTo );
241 + }
242 + pPathsToAddTo.add( getDist() );
243 + }
244 +
245 + protected boolean GWTcompileIt() {
246 + Utils.shell( new ShellArgs()
247 + .add( getPathJavaJRE() )
248 + .add( "-Xmx" + getGWTmx() )
249 + .add( "-cp", buildGWTcompileClassPath() )
250 + .add( "com.google.gwt.dev.Compiler" )
251 + .add( getGWTcompileReport() )
252 + .add( "-logLevel", getGWTlogging() )
253 + .add( "-war", getGWTwarPath() )
254 + .add( "-style", getGWTstyle() )
255 + .add( getGWT() )
256 + .toArray() );
257 + return true;
258 + }
259 +
260 + private String buildGWTcompileClassPath() {
261 + Paths zGWTclassPath = new Paths();
262 + File zGWTatDir = getGWTatDir();
263 + zGWTclassPath.add( FilePath.canonical( zGWTatDir, GWT_DEV ) );
264 + zGWTclassPath.add( FilePath.canonical( zGWTatDir, GWT_USER ) );
265 + zGWTclassPath.add( FilePath.canonical( zGWTatDir, GWT_VALIDATION ) );
266 + zGWTclassPath.add( FilePath.canonical( zGWTatDir, GWT_VALIDATION_SOURCE ) );
267 + if ( mSources ) {
268 + zGWTclassPath.add( FilePath.canonicalize( getJarPathFile() ) );
269 + }
270 + zGWTclassPath.add( classpath() );
271 + return zGWTclassPath.toString( File.pathSeparator );
272 + }
273 +
274 + protected String getPathJavaJRE() {
275 + File zJavaHomeDir = assertIsDirectory( JAVA_HOME, new File( assertNotEmpty( JAVA_HOME, System.getenv( JAVA_HOME ) ) ) );
276 + File zJavaDir = new File( zJavaHomeDir, "jre/bin" );
277 + if ( !zJavaDir.isDirectory() ) {
278 + if ( !(zJavaDir = new File( zJavaHomeDir, "bin" )).isDirectory() ) {
279 + throw new IllegalStateException( "Unable to find JAVA_HOME bin directory under: " + zJavaHomeDir );
280 + }
281 + }
282 + File zJavaExecutable = new File( zJavaDir, isWindows ? "java.exe" : "java" );
283 + if ( zJavaExecutable.isFile() ) {
284 + return getCanonicalFile( zJavaExecutable ).getPath();
285 + }
286 + throw new IllegalStateException( "Unable to find JAVA_HOME based executable at: " + zJavaExecutable );
287 + }
288 +
289 + protected File getGeneratedGWT_nocache_jsFile() {
290 + String zGWTxmlRelativeFilePath = getGWT().replace( '.', '/' ) + ".gwt.xml"; // e.g. org.litesoft.sandbox.csapp.CSapp
291 + File zFound = getGeneratedGWT_nocache_jsFile( zGWTxmlRelativeFilePath, getSource() );
292 + if ( zFound == null ) {
293 + if ( null == (zFound = getGeneratedGWT_nocache_jsFile( zGWTxmlRelativeFilePath, getResources() )) ) {
294 + throw new IllegalArgumentException( "Unable to locate GWT module file: " + getGWT() );
295 + }
296 + }
297 + return zFound;
298 + }
299 +
300 + private File getGeneratedGWT_nocache_jsFile( String pGWTxmlRelativeFilePath, Paths pPaths ) {
301 + RootedPaths[] zRootedPaths = pPaths.getRootedPaths();
302 + for ( RootedPaths zPath : zRootedPaths ) {
303 + File zFile = new File( zPath.getCanonicalRootDirectory(), pGWTxmlRelativeFilePath );
304 + if ( zFile.isFile() ) {
305 + String moduleName = extractModuleNameFrom( getGWT(), fileContents( zFile ) );
306 + return new File( getGWTwarPath(), moduleName + "/" + moduleName + ".nocache.js" );
307 + }
308 + }
309 + return null;
310 + }
311 +
312 + private String extractModuleNameFrom( String pGWTmoduleReference, String pGWTmoduleFileContents ) {
313 + int at = pGWTmoduleFileContents.indexOf( " rename-to" );
314 + if ( at != -1 ) // . . . . . . . . . . . .01234567890
315 + {
316 + int upTo = pGWTmoduleFileContents.indexOf( '>', at += 10 );
317 + if ( upTo != -1 ) {
318 + String stuff = pGWTmoduleFileContents.substring( at, upTo ).trim();
319 + if ( stuff.startsWith( "=" ) ) {
320 + if ( (stuff = stuff.substring( 1 ).trim()).length() > 2 ) {
321 + char c = stuff.charAt( 0 );
322 + if ( (c == '"') || (c == '\'') ) {
323 + if ( -1 != (at = stuff.indexOf( c, 1 )) ) {
324 + return stuff.substring( 1, at );
325 + }
326 + }
327 + }
328 + }
329 + }
330 + }
331 + String s = "." + pGWTmoduleReference;
332 + return s.substring( s.lastIndexOf( '.' ) + 1 );
333 + }
334 +
335 + protected boolean needToCompileGWT() {
336 + File zGeneratedGWT_nocache_jsFile = getGeneratedGWT_nocache_jsFile();
337 + return needToBuild( ((zGeneratedGWT_nocache_jsFile != null) && zGeneratedGWT_nocache_jsFile.isFile()) ?
338 + zGeneratedGWT_nocache_jsFile.lastModified() : forceBuildLastModified() );
339 + }
340 +
341 + public boolean GWTcompile() {
342 + String zGWT = getGWT();
343 + if ( zGWT == null ) {
344 + return false;
345 + }
346 + if ( !needToCompileGWT() ) {
347 + progress( "GWT Compile: " + this + " NOT Needed!" );
348 + return false;
349 + }
350 + progress( "GWT Compile: " + this );
351 + return GWTcompileIt();
352 + }
353 +
354 + /**
355 + * Assert that this project is currently a 'Versioned' GWT project, and then rev the version number by 1
356 + */
357 + public void versionGWT() {
358 + File zWarWebXmlFile = new File( mCanonicalProjectDir, "war/WEB-INF/web.xml" );
359 + String zWarWebXml = fileContents( assertIsFile( "web.xml", zWarWebXmlFile ) );
360 +
361 + int zCurVersion = extractVersionFromUrlPattern( zWarWebXml );
362 +
363 + String zWarResourceRelativePathCurrent = "warResources/v" + zCurVersion;
364 +
365 + File zIndexHtmlFile = new File( mCanonicalProjectDir, zWarResourceRelativePathCurrent + "/index.html" );
366 + String zIndexHtml = assertVersionedIndexHtml( zIndexHtmlFile, zCurVersion );
367 +
368 + List<File> zVersionedGwtXmlFiles = findVersionedGwtXmlFiles( zCurVersion );
369 +
370 + int zNewVersion = zCurVersion + 1;
371 +
372 + String zWarResourceRelativePathNew = "warResources/v" + zNewVersion;
373 + if ( new File( mCanonicalProjectDir, zWarResourceRelativePathNew ).exists() ) {
374 + throw new IllegalStateException( "Project already contains a 'warResources/v" + zNewVersion + "' directory?" );
375 + }
376 +
377 + progress( "versionGWT: " + this + " | " + zCurVersion + " -> " + (zCurVersion + 1) );
378 + progress( " " + zWarWebXmlFile.getPath() );
379 + progress( " " + zIndexHtmlFile.getPath() );
380 + for ( File zFile : zVersionedGwtXmlFiles ) {
381 + progress( " " + zFile.getPath() );
382 + }
383 + progress( " " + zWarResourceRelativePathCurrent + " -> " + zWarResourceRelativePathNew );
384 +
385 + new Paths( zWarResourceRelativePathCurrent ).copyTo( zWarResourceRelativePathNew );
386 +
387 + updateFileContents( new File( mCanonicalProjectDir, zWarResourceRelativePathNew + "/index.html" ),
388 + updateVersionedIndexHtml( zIndexHtml, zCurVersion, zNewVersion ) );
389 +
390 + updateFileContents( zWarWebXmlFile, updateVersionedWebXml( zWarWebXml, zCurVersion, zNewVersion ) );
391 +
392 + for ( File zFile : zVersionedGwtXmlFiles ) {
393 + updateFileContents( zFile, updateVersionedGwtXmlFile( fileContents( zFile ), zCurVersion, zNewVersion ) );
394 + }
395 + // Update/Create the Current Version's redirect JavaScript file
396 + String zCurPathVersion = "/v" + zCurVersion;
397 + String redirectScript = "var loc = window.location.href;\n" + //
398 + "var at = loc.indexOf( '" + zCurPathVersion + "' );\n" + //
399 + "window.location.href = loc.substring(0, at) + '/v" + zNewVersion + "' + loc.substring(at + " + zCurPathVersion.length() + ");\n";
400 + updateFileContents( new File( mCanonicalProjectDir, zWarResourceRelativePathCurrent + "/v" + zCurVersion + ".nocache.js" ), redirectScript );
401 +
402 + // Update the "root" html (if it exists)
403 + File zRootHtmlFile = new File( mCanonicalProjectDir, "warResources/index.html" );
404 + if ( zRootHtmlFile.isFile() ) {
405 + updateFileContents( zRootHtmlFile, updateRootHTML( fileContents( zRootHtmlFile ), zCurVersion, zNewVersion ) );
406 + }
407 + }
408 +
409 + protected String updateRootHTML( String pFileContents, int pCurVersion, int pNewVersion ) {
410 + // <!DOCTYPE html>
411 + // <html>
412 + // <head>
413 + // <meta http-equiv="Refresh" content="1; url=v1/">
414 + // </head>
415 + // <body>
416 + // <script>window.location.href = 'v1/';</script>
417 + // </body>
418 + // </html>
419 + String zCurPathVersion = "v" + pCurVersion + "/";
420 + String zNewPathVersion = "v" + pNewVersion + "/";
421 + for ( int at; -1 != (at = pFileContents.indexOf( zCurPathVersion )); ) {
422 + pFileContents = pFileContents.substring( 0, at ) + zNewPathVersion + pFileContents.substring( at + zCurPathVersion.length() );
423 + }
424 + return pFileContents;
425 + }
426 +
427 + protected String updateVersionedGwtXmlFile( String pFileContents, int pCurVersion, int pNewVersion ) {
428 + String zCurVersionedModule = VERSIONED_MODULE_PREFIX + pCurVersion + VERSIONED_MODULE_SUFFIX;
429 + String zNewVersionedModule = VERSIONED_MODULE_PREFIX + pNewVersion + VERSIONED_MODULE_SUFFIX;
430 + int at = pFileContents.indexOf( zCurVersionedModule );
431 + return pFileContents.substring( 0, at ) + zNewVersionedModule + pFileContents.substring( at + zCurVersionedModule.length() );
432 + }
433 +
434 + protected List<File> findVersionedGwtXmlFiles( int pVersion ) {
435 + String zVersionedModule = VERSIONED_MODULE_PREFIX + pVersion + VERSIONED_MODULE_SUFFIX;
436 +
437 + ArrayList<File> zVersionedFiles = new ArrayList<File>();
438 +
439 + String zSourceString = get( SOURCE.getName() ) + "|";
440 + Paths zGwtXml = new Paths( zSourceString.substring( 0, zSourceString.indexOf( '|' ) ), "**.gwt.xml" );
441 + for ( File zFile : zGwtXml.getFiles() ) {
442 + if ( fileContents( zFile ).contains( zVersionedModule ) ) {
443 + zVersionedFiles.add( zFile );
444 + }
445 + }
446 + if ( zVersionedFiles.isEmpty() ) {
447 + throw new IllegalStateException(
448 + "Project does not appear to contain a 'gwt.xml' file with the current version module definition of: " + zVersionedModule );
449 + }
450 + return zVersionedFiles;
451 + }
452 +
453 + protected String updateVersionedIndexHtml( String pFileContents, int pCurVersion, int pNewVersion ) {
454 + String zCurVersionedScript = VERSIONED_SCRIPT_PREFIX + pCurVersion + VERSIONED_SCRIPT_SUFFIX;
455 + String zNewVersionedScript = VERSIONED_SCRIPT_PREFIX + pNewVersion + VERSIONED_SCRIPT_SUFFIX;
456 + int at = pFileContents.indexOf( zCurVersionedScript );
457 + return pFileContents.substring( 0, at ) + zNewVersionedScript + pFileContents.substring( at + zCurVersionedScript.length() );
458 + }
459 +
460 + protected String assertVersionedIndexHtml( File pIndexHtmlFile, int pVersion ) {
461 + String zVersionedScript = VERSIONED_SCRIPT_PREFIX + pVersion + VERSIONED_SCRIPT_SUFFIX;
462 +
463 + String zContents = fileContents( assertIsFile( "Versioned index.html", pIndexHtmlFile ) );
464 + if ( !zContents.contains( zVersionedScript ) ) {
465 + throw new IllegalStateException(
466 + "Project's current versioned index.html file (" + pIndexHtmlFile.getPath() + ") does not contain a 'versioned' script element of: " +
467 + zVersionedScript );
468 + }
469 + return zContents;
470 + }
471 +
472 + protected String updateVersionedWebXml( String pFileContents, int pCurVersion, int pNewVersion ) {
473 + String zCurVersionedUrlPattern = VERSIONED_URL_PATTERN_PREFIX + pCurVersion + "/";
474 + String zNewVersionedUrlPattern = VERSIONED_URL_PATTERN_PREFIX + pNewVersion + "/";
475 + for ( int at; -1 != (at = pFileContents.indexOf( zCurVersionedUrlPattern )); ) {
476 + pFileContents = pFileContents.substring( 0, at ) + zNewVersionedUrlPattern + pFileContents.substring( at + zCurVersionedUrlPattern.length() );
477 + }
478 + return pFileContents;
479 + }
480 +
481 + protected int extractVersionFromUrlPattern( String pWarWebXml ) {
482 + for ( int at, from = 0; -1 != (at = pWarWebXml.indexOf( VERSIONED_URL_PATTERN_PREFIX, from )); from = at + 1 ) {
483 + int slashAt = pWarWebXml.indexOf( '/', at += VERSIONED_URL_PATTERN_PREFIX.length() );
484 + if ( slashAt > 0 ) {
485 + try {
486 + return Integer.parseInt( pWarWebXml.substring( at, slashAt ) );
487 + }
488 + catch ( NumberFormatException acceptable ) {
489 + // path starts w/ a 'v' but is not of pattern "v####"
490 + }
491 + }
492 + }
493 + throw new IllegalStateException( "Project's war/WEB-INF/web.xml does not appear to contain a 'versioned' <url-pattern>." );
494 + }
495 +
496 + /**
497 + * Executes the buildDependencies, clean, compile, jar, [GWTcompile], and then "packageIt" utility methods.
498 + */
499 + public synchronized boolean build() {
500 + if ( mBuilt ) {
501 + return false;
502 + }
503 + mBuilt = true;
504 + mSources = !getSource().isEmpty();
505 + boolean zAnythingBuilt = false;
506 + boolean zBuildIt;
507 + try {
508 + zBuildIt = buildDependencies() || needToBuild();
509 + }
510 + catch ( RuntimeException e ) {
511 + progress( "Build: " + this );
512 + throw e;
513 + }
514 + if ( !zBuildIt ) {
515 + progress( "Build: " + this + " NOT Needed!" );
516 + } else {
517 + progress( "Build: " + this );
518 + clean();
519 + if ( mSources ) {
520 + compile();
521 + jar();
522 + zAnythingBuilt = true;
523 + }
524 + }
525 + zAnythingBuilt |= GWTcompile();
526 + zAnythingBuilt |= packageIt();
527 + return zAnythingBuilt;
528 + }
529 +
530 + protected boolean needToBuild() {
531 + return needToBuild( determineOutputLastModified() );
532 + }
533 +
534 + protected boolean needToBuild( long pOutputLastModified ) {
535 + return (mProjectFileLastModified > pOutputLastModified) || //
536 + checkNewer( pOutputLastModified, "ClassPath", compileClasspath() ) || //
537 + checkNewer( pOutputLastModified, "Source", getSource() ) || //
538 + checkNewer( pOutputLastModified, "Resources", getResources() ) || //
539 + checkNewer( pOutputLastModified, "Dist", getDist() );
540 + }
541 +
542 + protected boolean checkNewer( long pOutputLastModified, String pWhat, Paths pPaths ) {
543 + Long zLastModified = pPaths.getGreatestLastModified();
544 + if ( (zLastModified != null) && (zLastModified > pOutputLastModified) ) {
545 + System.out.println( this + ": " + pWhat + " - " + new NewerBy( pOutputLastModified, zLastModified ) );
546 + return true;
547 + }
548 + return false;
549 + }
550 +
551 + protected long forceBuildLastModified() {
552 + return (mProjectFileLastModified - 1);
553 + }
554 +
555 + protected long determineOutputLastModified() {
556 + if ( mSources ) {
557 + File zJarFile = getJarPathFile();
558 + if ( zJarFile.isFile() ) {
559 + return zJarFile.lastModified();
560 + }
561 + } else {
562 + String zPhoneGapDir = getPhoneGapDirPath();
563 + if ( zPhoneGapDir != null ) {
564 + File zPhoneGapDirFile = new File( zPhoneGapDir );
565 + if ( zPhoneGapDirFile.isDirectory() ) {
566 + return zPhoneGapDirFile.lastModified();
567 + }
568 + }
569 + }
570 + return forceBuildLastModified();
571 + }
572 +
573 + /**
574 + * Deletes the "target" directory and all files and directories under it.
575 + */
576 + public void clean() {
577 + progress( "Clean: " + this );
578 + packageClean();
579 + delete( getGWTwarPath() );
580 + delete( getJarPath() );
581 + delete( getTargetPath() );
582 + }
583 +
584 + /**
585 + * Collects the source files using the "source" property and compiles them into a "classes" directory under the target
586 + * directory. It uses "classpath" and "dependencies" to find the libraries required to compile the source.
587 + * <p/>
588 + * Note: Each dependency project is not built automatically. Each needs to be built before the dependent project.
589 + *
590 + * @return The path to the "classes" directory or null if there was no sources to compile
591 + */
592 + public String compile() {
593 + Paths source = getSource();
594 + if ( source.isEmpty() ) {
595 + return null;
596 + }
597 + Paths classpath = compileClasspath();
598 +
599 + String classesDir = mkdir( path( "$target$/classes/" ) );
600 +
601 + String zMessage = "Compile: " + this;
602 + if ( LOGGER.debug.isEnabled() ) {
603 + zMessage += " | " + source.count() + " source files";
604 + if ( !classpath.isEmpty() ) {
605 + zMessage += "\n Classpath: " + classpath;
606 + }
607 + }
608 + progress( zMessage );
609 +
610 + List<String> zCompileArgs = createCompileJavaArgs( classpath, source, classesDir );
611 +
612 + compileJava( classpath, source, zCompileArgs );
613 +
614 + return classesDir;
615 + }
616 +
617 + protected List<String> createCompileJavaArgs( Paths pClasspath, Paths pSource, String pClassesDir ) {
618 + List<String> args = new ArrayList<String>();
619 + if ( LOGGER.trace.isEnabled() ) {
620 + args.add( "-verbose" );
621 + }
622 + args.add( "-d" );
623 + args.add( pClassesDir );
624 + args.add( "-g:source,lines" );
625 + args.add( "-source" );
626 + args.add( getSourceJavaVersion() );
627 + args.addAll( pSource.getFullPaths() );
628 + if ( !pClasspath.isEmpty() ) {
629 + args.add( "-classpath" );
630 + args.add( pClasspath.toString( File.pathSeparator ) );
631 + }
632 + return args;
633 + }
634 +
635 + protected void compileJava( Paths pClasspath, Paths pSource, List<String> pCompileArgs ) {
636 + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
637 +
638 + if ( compiler == null ) {
639 + throw new RuntimeException( "No compiler available. Ensure you are running from a " + getTargetJavaVersion() +
640 + "+ JDK, and not a JRE *and* that your class path includes tools.jar." );
641 + }
642 + int zError = compiler.run( getCompile_in(), getCompile_out(), getCompile_err(), pCompileArgs.toArray( new String[pCompileArgs.size()] ) );
643 + if ( zError != 0 ) {
644 + String zMessage = "Error (" + zError + ") during compilation of project: " + this +
645 + "\nSource: " + pSource.count() + " files\nCompilerArgs: " + pCompileArgs;
646 + if ( LOGGER.debug.isEnabled() ) {
647 + zMessage += "\nClasspath: " + pClasspath + "\nSource: " + pSource.toString( " " );
648 + }
649 + throw new RuntimeException( zMessage );
650 + }
651 + try {
652 + Thread.sleep( 100 );
653 + }
654 + catch ( InterruptedException ex ) {
655 + // Whatever
656 + }
657 + }
658 +
659 + protected InputStream getCompile_in() {
660 + return null;
661 + }
662 +
663 + protected OutputStream getCompile_out() {
664 + return null;
665 + }
666 +
667 + protected OutputStream getCompile_err() {
668 + return new OutputStream() {
669 + private StringBuilder mBuffer = new StringBuilder();
670 + private boolean mLastWasCRtreatedAsLF = false;
671 +
672 + private void dumpLine( int pByte ) {
673 + if ( pByte == 13 ) {
674 + mLastWasCRtreatedAsLF = true;
675 + pByte = 10;
676 + } else {
677 + boolean zLastWasCRtreatedAsLF = mLastWasCRtreatedAsLF;
678 + mLastWasCRtreatedAsLF = false;
679 + if ( (pByte == 10) && zLastWasCRtreatedAsLF ) {
680 + return;
681 + }
682 + }
683 + mBuffer.append( (char) pByte ); // Assuming Ascii!
684 + String line = mBuffer.toString();
685 + mBuffer = new StringBuilder();
686 + if ( !line.startsWith( "Note: " ) ) {
687 + System.err.print( line );
688 + }
689 + }
690 +
691 + @Override
692 + public void write( int pByte ) // Not a Unicode character, just a BYTE! --- Asumming Ascii ---
693 + throws IOException {
694 + if ( (10 <= pByte) && (pByte <= 13) ) // New Line Indicator
695 + { // LF: Line Feed, U+000A
696 + dumpLine( pByte ); // VT: Vertical Tab, U+000B
697 + return; // FF: Form Feed, U+000C
698 + } // CR: Carriage Return, U+000D
699 + mBuffer.append( (char) pByte ); // Assuming Ascii!
700 + }
701 + };
702 + }
703 +
704 + /**
705 + * Collects the class files from the "classes" directory and all the resource files using the "resources" property and encodes
706 + * them into a JAR file.
707 + * <p/>
708 + * If the resources don't contain a META-INF/MANIFEST.MF file, one is generated. If the project has a main property, the
709 + * generated manifest will include "Main-Class" and "Class-Path" entries to allow the main class to be run with "java -jar".
710 + *
711 + * @return The path to the created JAR file or null if No JAR created.
712 + */
713 + public String jar() {
714 + String zJarPath = getJarPath();
715 +
716 + Paths zClasses = new Paths( path( "$target$/classes/" ), "**.class" );
717 + Paths zResources = getResources();
718 + if ( zClasses.isEmpty() && zResources.isEmpty() ) {
719 + delete( zJarPath );
720 + return null;
721 + }
722 + progress( "JAR: " + this + " -> " + zJarPath );
723 +
724 + String jarDir = mkdir( path( "$target$/jar/" ) );
725 +
726 + zClasses.copyTo( jarDir );
727 + zResources.copyTo( jarDir );
728 +
729 + File manifestFile = new File( jarDir, "META-INF/MANIFEST.MF" );
730 + if ( !manifestFile.exists() ) {
731 + createDefaultManifestFile( zJarPath, manifestFile );
732 + }
733 +
734 + return jar( zJarPath, new Paths( jarDir ) );
735 + }
736 +
737 + protected void createDefaultManifestFile( String pJarFile, File pManifestFile ) {
738 + LOGGER.debug.log( "Generating JAR manifest: ", pManifestFile );
739 + mkdir( pManifestFile.getParent() );
740 + Manifest manifest = new Manifest();
741 + manifest.getMainAttributes().putValue( Attributes.Name.MANIFEST_VERSION.toString(), "1.0" );
742 + if ( hasMain() ) {
743 + LOGGER.debug.log( "Main class: ", getMain() );
744 + manifest.getMainAttributes().putValue( Attributes.Name.MAIN_CLASS.toString(), getMain() );
745 + StringBuilder buffer = new StringBuilder( 512 );
746 + buffer.append( Utils.fileName( pJarFile ) );
747 + buffer.append( " ." );
748 + Paths classpath = classpath();
749 + for ( String name : classpath.getRelativePaths( pJarFile ) ) {
750 + buffer.append( ' ' );
751 + buffer.append( name );
752 + }
753 + manifest.getMainAttributes().putValue( Attributes.Name.CLASS_PATH.toString(), buffer.toString() );
754 + }
755 + OutputStream output = createFileOutputStream( pManifestFile );
756 + try {
757 + manifest.write( output );
758 + Closeable zCloseable = output;
759 + output = null;
760 + close( zCloseable );
761 + }
762 + catch ( IOException e ) {
763 + throw new WrappedIOException( e );
764 + }
765 + finally {
766 + dispose( output );
767 + }
768 + }
769 +
770 + /**
771 + * Encodes the specified paths into a JAR file.
772 + *
773 + * @return The path to the JAR file.
774 + */
775 + public String jar( String jarFile, Paths paths ) {
776 + return innerJar( "JAR", jarFile, paths );
777 + }
778 +
779 + /**
780 + * Encodes the specified paths into a JAR/WAR file.
781 + *
782 + * @return The path to the JAR/WAR file.
783 + */
784 + protected String innerJar( String pType, String jarFile, Paths paths ) {
785 + Util.assertNotNull( "jarFile", jarFile );
786 + Util.assertNotNull( "paths", paths );
787 +
788 + progress( "Creating " + pType + " (" + paths.count() + " entries): " + jarFile );
789 +
790 + int zZipped = paths.zip( jarFile, new ZipFactory() {
791 + @Override
792 + public ZipOutputStream createZOS( String pFilePath, List<FilePath> pPaths ) {
793 + putManifestFirst( pPaths );
794 + try {
795 + return new JarOutputStream( FileUtil.createBufferedFileOutputStream( pFilePath ) );
796 + }
797 + catch ( IOException e ) {
798 + throw new WrappedIOException( e );
799 + }
800 + }
801 +
802 + @Override
803 + public ZipEntry createZE( String pRelativePath ) {
804 + return new JarEntry( pRelativePath );
805 + }
806 +
807 + private void putManifestFirst( List<FilePath> pPaths ) {
808 + int at = findManifest( pPaths );
809 + if ( at > 0 ) {
810 + FilePath zManifest = pPaths.remove( at );
811 + pPaths.add( 0, zManifest );
812 + }
813 + }
814 +
815 + private int findManifest( List<FilePath> pPaths ) {
816 + for ( int i = 0; i < pPaths.size(); i++ ) {
817 + if ( META_INF_MANIFEST_MF.equals( pPaths.get( i ).getFileSubPath() ) ) {
818 + return i;
819 + }
820 + }
821 + return -1;
822 + }
823 + } );
824 + return zZipped == 0 ? null : jarFile;
825 + }
826 +
827 + /**
828 + * Decodes the specified ZIP file.
829 + *
830 + * @return The path to the output directory.
831 + */
832 + protected void quiteUnzip( File zipFile, File outputDir ) {
833 + ZipInputStream input = new ZipInputStream( createFileInputStream( zipFile ) );
834 + try {
835 + for ( ZipEntry entry; null != (entry = input.getNextEntry()); ) {
836 + File file = new File( outputDir, entry.getName() );
837 + if ( entry.isDirectory() ) {
838 + mkdir( file.getPath() );
839 + continue;
840 + }
841 + writeStream( input, createFileOutputStream( file ) );
842 + }
843 + }
844 + catch ( IOException e ) {
845 + throw new WrappedIOException( e );
846 + }
847 + finally {
848 + dispose( input );
849 + }
850 + }
851 +
852 + /**
853 + * Decodes the specified ZIP file.
854 + */
855 + public void unzip( File zipFile, File outputDir ) {
856 + Util.assertNotNull( "zipFile", zipFile );
857 + Util.assertNotNull( "outputDir", outputDir );
858 + progress( "ZIP decoding: " + zipFile.getPath() + " -> " + outputDir.getPath() );
859 + quiteUnzip( zipFile, outputDir );
860 + }
861 +
862 + /**
863 + * Decodes the specified ZIP file.
864 + *
865 + * @return The path to the output directory.
866 + */
867 + public String unzip( String zipFile, String outputDir ) {
868 + zipFile = assertNotEmpty( "zipFile", zipFile );
869 + outputDir = assertNotEmpty( "outputDir", outputDir );
870 + progress( "ZIP decoding: " + zipFile + " -> " + outputDir );
871 + quiteUnzip( new File( zipFile ), new File( outputDir ) );
872 + return outputDir;
873 + }
874 +
875 + /**
876 + * Computes the classpath for the specified project and all its dependency projects, recursively.
877 + */
878 + protected Paths compileClasspath() {
879 + Paths classpath = getCompileClasspath();
880 + classpath.add( getClasspath() );
881 + for ( Project zProject : mDependantProjects ) {
882 + zProject.addDependantProjectsCompileClassPaths( classpath );
883 + }
884 + return classpath;
885 + }
886 +
887 + /**
888 + * Computes the classpath for all the dependencies of the specified project, recursively.
889 + */
890 + protected void addDependantProjectsCompileClassPaths( Paths pPathsToAddTo ) {
891 + addDependentProjectJar( pPathsToAddTo );
892 + pPathsToAddTo.add( compileClasspath() );
893 + }
894 +
895 + /**
896 + * Computes the classpath for the specified project and all its dependency projects, recursively.
897 + */
898 + protected Paths classpath() {
899 + Paths classpath = getClasspath();
900 + for ( Project zProject : mDependantProjects ) {
901 + zProject.addDependantProjectsClassPaths( classpath );
902 + }
903 + return classpath;
904 + }
905 +
906 + /**
907 + * Computes the classpath for all the dependencies of the specified project, recursively.
908 + */
909 + protected void addDependantProjectsClassPaths( Paths pPathsToAddTo ) {
910 + addDependentProjectJar( pPathsToAddTo );
911 + pPathsToAddTo.add( classpath() );
912 + }
913 +
914 + protected void addDependentProjectJar( Paths pPathsToAddTo ) {
915 + if ( mSources ) {
916 + File zJarFile = getJarPathFile();
917 + if ( !zJarFile.isFile() ) {
918 + throw new RuntimeException( "Dependency (" + this + ") Jar not found, not built?" );
919 + }
920 + pPathsToAddTo.add( new FilePath( zJarFile.getParentFile(), zJarFile.getName() ) );
921 + }
922 + }
923 +
924 + /**
925 + * Calls {@link #build(Project)} for each dependency project in the specified project.
926 + */
927 + public boolean buildDependencies() {
928 + boolean anyBuilt = false;
929 + for ( Project zProject : mDependantProjects ) {
930 + anyBuilt |= zProject.build();
931 + }
932 + return anyBuilt;
933 + }
934 +
935 + public void set( Object key, Object object ) {
936 + mManager.put( validateSetableKey( key ), object );
937 + }
938 +
939 + public void remove( Object key ) {
940 + set( key, null );
941 + }
942 +
943 + private Object validateSetableKey( Object pKey ) {
944 + if ( pKey instanceof String ) {
945 + String zStrKey = noEmpty( pKey.toString().toLowerCase() );
946 + if ( Parameter.reservedNames().contains( zStrKey ) ) {
947 + throw new IllegalArgumentException( zStrKey + " not updatable!" );
948 + }
949 + pKey = zStrKey;
950 + }
951 + Util.assertNotNull( "key", pKey );
952 + return pKey;
953 + }
954 +
955 + public synchronized void initialize( ProjectFactory pProjectFactory ) {
956 + List<String> zDependencies = getDependencies();
957 + if ( zDependencies != null ) {
958 + for ( String zDependency : zDependencies ) {
959 + mDependantProjects.add( pProjectFactory.project( mCanonicalProjectDir, zDependency ) );
960 + }
961 + }
962 +
963 + // Project defaults = new Project();
964 + //
965 + // File file = new File( canonical( pPath ) );
966 + // if ( file.isDirectory() )
967 + // {
968 + // String name = file.getName();
969 + // defaults.set( "name", name );
970 + // defaults.set( "target", file.getParent() + "/target/" + name + "/" );
971 + // }
972 + // else
973 + // {
974 + // String name = file.getParentFile().getName();
975 + // defaults.set( "name", name );
976 + // defaults.set( "target", file.getParentFile().getParent() + "/target/" + name + "/" );
977 + // }
978 + // defaults.set( "classpath", "lib|**/*.jar" );
979 + // defaults.set( "dist", "dist" );
980 + //
981 + // List<String> source = new ArrayList<String>();
982 + // source.add( "src|**/*.java" );
983 + // source.add( "src/main/java|**/*.java" );
984 + // defaults.set( "source", source );
985 + //
986 + // List<String> resources = new ArrayList<String>();
987 + // resources.add( "resources" );
988 + // resources.add( "src/main/resources" );
989 + // defaults.set( "resources", resources );
990 + //
991 + // Project project = project( pPath, defaults );
992 + //
993 + // // Remove dependency if a JAR of the same name is on the classpath.
994 + // Paths classpath = project.getPaths( "classpath" );
995 + // classpath.add( dependencyClasspaths( project, classpath, false, false ) );
996 + // for ( String dependency : project.getDependencies() )
997 + // {
998 + // String dependencyName = project( project.path( dependency ) ).getName();
999 + // for ( String classpathFile : classpath )
1000 + // {
1001 + // String name = fileWithoutExtension( classpathFile );
1002 + // int dashIndex = name.lastIndexOf( '-' );
1003 + // if ( dashIndex != -1 )
1004 + // {
1005 + // name = name.substring( 0, dashIndex );
1006 + // }
1007 + // if ( name.equals( dependencyName ) )
1008 + // {
1009 + // if ( DEBUG )
1010 + // {
1011 + // debug( "Ignoring " + project + " dependency: " + dependencyName + " (already on classpath: " + classpathFile + ")" );
1012 + // }
1013 + // project.remove( "dependencies", dependency );
1014 + // break;
1015 + // }
1016 + // }
1017 + // }
1018 + //
1019 + // if ( TRACE )
1020 + // {
1021 + // trace( "scar", "Project: " + project + "\n" + project );
1022 + // }
1023 + //
1024 + // return project;
1025 + }
1026 +
1027 + protected boolean mBuilt = false;
1028 + protected boolean mSources = false;
1029 + protected List<Project> mDependantProjects = new ArrayList<Project>();
1030 + }