package textbender::a::b::Java; # Copyright 2004-2007, Michael Allan. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Textbender Software"), to deal in the Textbender Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Textbender Software, and to permit persons to whom the Textbender Software is furnished to do so, subject to the following conditions: The preceding copyright notice and this permission notice shall be included in all copies or substantial portions of the Textbender Software. THE TEXTBENDER SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE TEXTBENDER SOFTWARE OR THE USE OR OTHER DEALINGS IN THE TEXTBENDER SOFTWARE. use strict; use warnings; =pod =head1 EXPORTS =over 4 =cut BEGIN { use Exporter (); our @ISA; @ISA = qw( Exporter ); our @EXPORT_OK; @EXPORT_OK = qw ( compile compile_class_outdir jar javadoc JDK_dir list_found_jar @list_found_jar ); } our @EXPORT_OK; { use textbender::a::b::Config qw( do_fail config_basepath_from_package ); my $config_path = config_basepath_from_package(__PACKAGE__) . '.pl'; do $config_path or do_fail $config_path; my $expected_major_version = '1.6'; $_ = major_version(); $_ eq $expected_major_version or die "Expecting major version '$expected_major_version', found '$_'"; } =pod =item B Compiles out-of-sync source files *.java to *.class. Returns the number of files compiled. =cut my $compile__print_count; sub _compile__print_all_source() # File::Find::find wanted sub { my $file = $File::Find::name; -d $file and return; # skip directories my $outfile = $file; # textbender/foo/bar.java $outfile =~ s:\.java$:.class: or return; # textbender/foo/bar.class -l $file && $file =~ m'/\.\#[^/]+$' and die "\nemacs lock file (do you need to save this buffer?): $_\n"; $outfile = compile_class_outdir() . '/' . $outfile; # /dir/textbender/foo/bar.class if( -f $outfile ) { File::stat::stat($file)->mtime > File::stat::stat($outfile)->mtime or return; } else # finally, filter by path (last because we still want to compile off-path dependencies (pulled in by javac on the last path) that are now out-of-date) { $file =~ m:^textbender/a/b/rhinohideDemo/: and return; # exclude Rhinohide demo } print ARGFILE $file; print ARGFILE "\n"; ++$compile__print_count; } sub compile() { use File::Find (); use textbender::a::b::Console qw( print_score $verbosity ); use textbender::a::b::Build qw( build_cache_root ensure_dir ); my $compile_class_outdir = ensure_dir( compile_class_outdir() ); my $command = JDK_dir() . '/bin/javac' . ' -sourcepath .' . " -d $compile_class_outdir" . ' -deprecation -g' # Though deprecation warnings are emitted only for single-file compiles, # with or without -deprecation, in 1.4, ## use -Xlint:deprecation in 1.5 ??? # . ' -source ' . textbender::a::b::Java::major_version() ## latest is the default in 1.5 # . ' -target ' . textbender::a::b::Java::major_version() ## latest is the default in 1.5 # . ' -verbose' . ' -Xlint' # new in 1.5 . ' -Xlint:-serial' # because its documentation (especially for @serialField and @serialData) is incomprehensible ; local *ARGFILE; my $argfile; # Source. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - $argfile = build_cache_root() . '/textbender/a/b/Java-compile-arg-source'; open ARGFILE, '>', $argfile or die; { $compile__print_count = 0; # thus far File::Find::find ( {follow_fast=>1, no_chdir=>1, wanted=>\&_compile__print_all_source}, 'textbender' ); close ARGFILE; } $compile__print_count or return 0; $command .= ' @' . $argfile; # Classpath. Cf. javadoc(). # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - $argfile = build_cache_root() . '/textbender/a/b/Java-compile-arg-classpath'; open ARGFILE, '>', $argfile or die; { print ARGFILE "-classpath $compile_class_outdir"; # Jars from textbender code. # ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` our @list_found_jar; @list_found_jar = (); File::Find::find ( {follow_fast=>1, no_chdir=>1, wanted=>\&list_found_jar}, 'textbender' ); for my $file( @list_found_jar ) { print ARGFILE ':'; print ARGFILE $file; } # Jars from the development kit. # ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` for my $file( 'jre/lib/plugin.jar' ) # for JSObject (in-browser code) { print ARGFILE ':'; print ARGFILE JDK_dir() . '/' . $file; } # ` ` ` print ARGFILE "\n"; close ARGFILE; } $command .= ' @' . $argfile; # - - - print textbender::a::b::Build::call_stack_indentation() . "(compile $compile__print_count...\n"; if( $verbosity ) { print "\n$command\n"; print_score( undef, '^' ); } system $command and exit; return $compile__print_count; } =pod =item B Returns the directory to which compiled Java (*.class) files are output. =cut sub compile_class_outdir() { use textbender::a::b::Build qw( build_cache_root ); return build_cache_root() . '/textbender/a/b/Java-compile-out'; } =pod =item B ( $entry_dir, $jar_file ; $manifest_file, $manifest_is_changed ) Syncs files from $entry_dir to $jar_file, based on its mod time. [FIX by deprecating entirely. It's over-complicated by efficiencies that are not needed. Use a simple in-line command instead.] Returns the number of files added to $jar_file. Optionally adds a $manifest_file. Unfortunately, when updating, you cannot simply replace the entire manifest; the jar tool will instead add the entries of the specified manifest file to the existing manifest, and will warn about any duplicates. The warning is annoying and the issue is unclear. So, if you provide a manifest, and you do not specify a false value for $manifest_is_changed, then this subroutine will delete the existing jar and start from scratch. =cut my $_jar__entry_dir; my $_jar__jar_file; my @_jar__new_entry; sub _jar__list_new_entries() # File::Find::find wanted sub { use File::stat (); my $file = $File::Find::name; -d $file and return; # skip directories -l $file && $file =~ m'/\.\#[^/]+$' and die "\nemacs lock file (do you need to save this buffer?): $_\n"; if( -f $_jar__jar_file ) { File::stat::stat($file)->mtime > File::stat::stat($_jar__jar_file)->mtime or return; } my $rel_file = substr( $file, length($_jar__entry_dir) + 1 ); # /foo/bar -> foo/bar push @_jar__new_entry, $rel_file; } sub jar( $$;$$ ) { use File::Find (); use textbender::a::b::Build qw( build_cache_root ); use textbender::a::b::Console qw( print_score $verbosity ); $_jar__entry_dir = shift; $_jar__jar_file = shift; my $manifest_file = shift; my $manifest_is_changed = shift; defined $manifest_is_changed or $manifest_is_changed = 1; if( -f $_jar__jar_file && $manifest_file ) { if( $manifest_is_changed ) { unlink $_jar__jar_file; # per pod above } else { $manifest_file = ''; } } @_jar__new_entry = (); File::Find::find ( {follow_fast=>1, no_chdir=>1, wanted=>\&_jar__list_new_entries}, $_jar__entry_dir ); @_jar__new_entry or return 0; local *ARGFILE; my $argfile; $argfile = build_cache_root() . '/textbender/a/b/Java-jar-arg'; open ARGFILE, '>', $argfile or die; { for my $entry( @_jar__new_entry ) { print ARGFILE "-C $_jar__entry_dir\n"; print ARGFILE "$entry\n"; } close ARGFILE; } my $c; if( -f $_jar__jar_file ) { $c = 'u'; # update $manifest_file and die "should not occur, else it will complain about duplicate entries"; } else { $c = 'c'; # create $manifest_file and $c .= 'm'; } my $command = JDK_dir() . "/bin/jar ${c}f"; $manifest_file and $command .= ' ' . $manifest_file; $command .= " $_jar__jar_file \@$argfile"; print textbender::a::b::Build::call_stack_indentation() . '(jar files ' . scalar @_jar__new_entry . "...\n"; if( $verbosity ) { print "\n$command\n"; print_score( undef, '^' ); } if( system $command ) { unlink $_jar__jar_file; # easier than reverting its modtime (to effectively undo partial mod of jar) exit; } return scalar @_jar__new_entry; } =pod =item B Builds the API docs. =cut sub javadoc() { # use textbender::_::Textbender qw( textbender_wave_HTML ); use textbender::a::b::Build qw( ensure_dir ); use textbender::a::b::Console qw( print_score $verbosity ); # Classpath. Cf. compile(). # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - my $class_path = compile_class_outdir(); # Jars from textbender code. # ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` our @list_found_jar; @list_found_jar = (); File::Find::find ( {follow_fast=>1, no_chdir=>1, wanted=>\&list_found_jar}, 'textbender' ); for my $file( @list_found_jar ) { $class_path .= ':' . $file; } # Jars from the development kit. # ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` for my $file( 'jre/lib/plugin.jar' ) { $class_path .= ':' . JDK_dir() . '/' . $file; } # - - - my $command = JDK_dir() . '/bin/javadoc' . ' -breakiterator' . " -classpath $class_path" . ' -d ' . ensure_dir( javadoc_outdir() ) .q` -doctitle ""` .q` -header ""` . ' -link http://java.sun.com/javase/' . product_version() . '/docs/api/' . ' -link http://java.sun.com/javase/' . product_version() . '/docs/jre/api/plugin/dom/' . ' -linksource' . ' -noqualifier textbender.*' . ' -overview textbender/a/b/javadoc-overview.html' . ' -package' . ' -quiet' . ' -sourcepath .' . ' -stylesheetfile textbender/a/b/javadoc.css' . ' -subpackages textbender' . ' -use' . " -windowtitle 'textbender API documentation'" ; print textbender::a::b::Build::call_stack_indentation() . "(javadoc ...\n"; if( $verbosity ) { print "\n$command\n"; print_score( undef, '^' ); } system $command and exit; } =pod =item B File::Find::find wanted sub that appends the file (if it is a jar) to @list_found_jar. =cut sub list_found_jar() { our @list_found_jar; my $file = $File::Find::name; -d $file and return; # skip directories $file =~ m'\.jar$' or return; push @list_found_jar, $file; } =pod =back =cut 1;