package votorola::b::Java; # Copyright 2010, 2012, 2017, Michael Allan. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Votorola Software"), to deal in the Votorola Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Votorola Software, and to permit persons to whom the Votorola 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 Votorola Software. THE VOTOROLA 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 VOTOROLA SOFTWARE OR THE USE OR OTHER DEALINGS IN THE VOTOROLA 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 compile_time_library_jar_list home_dir jar javadoc JDK_dir list_found_jar @list_found_jar major_version product_version ); } our @EXPORT_OK; { use votorola::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.8'; $_ = 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 { use votorola::b::FileSync qw( editor_locked $editor_locked_message ); my $file = $File::Find::name; -d $file and return; # directory my $outfile = $file; # [ votorola/fu/bar.java ] $outfile =~ m!votorola/b/mvn! and return; # ignore maven symlinks $outfile =~ m!votorola/b/test! and return; # ignore test cases $outfile =~ m!votorola/(?:a|g)/web/gwt/super! and return; # only compiled by GWT, cf. javadoc() -exclude option $outfile =~ m!/package-info\.java$! and return; # javadoc metafile, no need to compile $outfile =~ s!\.java$!.class! or return; # [ votorola/fu/bar.class ] if( editor_locked( $file )) { print "\n$editor_locked_message\n"; return; } $outfile = compile_class_outdir() . '/' . $outfile; # [ /dir/votorola/fu/bar.class ] if( -f $outfile ) { File::stat::stat($file)->mtime > File::stat::stat($outfile)->mtime or return; } print ARGFILE $file; print ARGFILE "\n"; ++$compile__print_count; } sub compile() { use File::Find (); use votorola::b::Console qw( print_score $verbosity ); use votorola::b::Build qw( build_cache_root ensure_dir ); use votorola::b::Function qw( system_exit_decode ); # my $target_JDK = major_version(); ## but want 1.7 for consistency with javadoc build, q.v. farther below my $target_JDK = '1.7'; # requires specifying -bootclass below my $compile_class_outdir = ensure_dir( compile_class_outdir() ); my $command = JDK_dir() . '/bin/javac' . " -bootclasspath /opt/jdk$target_JDK/jre/lib/rt.jar" . ' -d ' . $compile_class_outdir # . ' -deprecation' # "shorthand for -Xlint:deprecation", which is already covered by -Xlint below . ' -g:lines,source,vars' . ' -source ' . $target_JDK . ' -sourcepath .' . ' -target ' . $target_JDK # . ' -verbose' . ' -Xlint' . ' -Xlint:-serial' # because its documentation (especially for @serialField and @serialData) is incomprehensible ; local *ARGFILE; my $argfile; # Source # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - $argfile = build_cache_root() . '/votorola/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}, 'votorola' ); close ARGFILE; } $compile__print_count or return 0; $command .= ' @' . $argfile; # Classpath, cf. javadoc() # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - $argfile = build_cache_root() . '/votorola/Java-compile-arg-classpath'; open ARGFILE, '>', $argfile or die; { print ARGFILE "-classpath $compile_class_outdir"; # Jars from votorola code. # ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` for my $file( compile_time_library_jar_list() ) { print ARGFILE ':'; print ARGFILE $file; if( $verbosity ) { print votorola::b::Build::call_stack_indentation() . "Java-compile-arg-classpath: $file\n"; } } # # 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 votorola::b::Build::call_stack_indentation() . "(compile $compile__print_count...\n"; if( $verbosity ) { print "\n$command\n"; print_score( undef, '^' ); } system $command; $? != 0 and exit system_exit_decode( $? ); return $compile__print_count; } =pod =item B() The directory to which compiled Java (*.class) files are output. =cut sub compile_class_outdir() { use votorola::b::Build qw( build_cache_root ); return build_cache_root() . '/votorola/Java-compile-out'; } =pod =item B() The list of all library jars and/or directories needed at compile time. The list is cached. It will only include a directory when compiling against a test build of lib-gwt-svg. =cut sub compile_time_library_jar_list() { use votorola::b::GWT (); our @_compile_time_library_jar; # cached our @list_found_jar; if( !@_compile_time_library_jar ) # then lazily create it { my $svg_from_build # whether to pull it straight from the build for test purposes = ''; # = 1; # TEST, first ensure you: cd ~/build/lib-gwt-svg; b/build x @list_found_jar = (); File::Find::find( {follow_fast=>1, no_chdir=>1, wanted=>sub { my $f = $File::Find::name; $f =~ m'votorola/b/mvn' and return; $f =~ m'votorola/b/test' and return; # skip these ones, only needed at runtime: $f =~ m'commons-logging\.jar$' and return; $f =~ m'slf4j-jdk14\.jar$' and return; $f =~ m'slf4j-api\.jar$' and return; $f =~ m'xercesImpl\.jar$' and return; if( $svg_from_build && $f =~ m'lib-gwt-svg\.jar$' ) { # cf. lib-gwt-svg b::Build::build__target_jar() push @list_found_jar, build_cache_root() . '/lib-gwt-svg/Java-compile-out'; push @list_found_jar, '/home/mike/code/lib-gwt-svg/switch/repo/src/main/resources'; # module definition XML, and perhaps other files push @list_found_jar, '/home/mike/code/lib-gwt-svg/switch/repo/src/main/java'; return 1; } list_found_jar(); }}, 'votorola' ); push( @list_found_jar, votorola::b::GWT::compile_time_library_jar_list() ); # external to Votorola @_compile_time_library_jar = @list_found_jar; } return @_compile_time_library_jar; } =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. Returns the number of files added to $jar_file. Optionally adds a $manifest_file. Unfortunately it cannot be updated with a new manifest. The jar tool instead adds the entries of the new manifest to the existing one, and then warns about any duplicates. The warning annoys, and the issue is unclear. Therefore if you provide a manifest and do not specify a false value for $manifest_is_changed, then this subroutine deletes the existing jar and starts from scratch. FIX. This is over-complicated by optimizations that are not needed. See notes on the failures that occured with a copy of this code in lib-gwt-svg, and the simple alternative used there in jar2(). http://reluk.ca/project/_/lib-gwt-svg/b/ =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; # directory -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 ); # /fu/bar -> fu/bar push @_jar__new_entry, $rel_file; } sub jar( $$;$$ ) { use File::Find (); use votorola::b::Build qw( build_cache_root ); use votorola::b::Console qw( print_score $verbosity ); use votorola::b::Function qw( system_exit_decode ); $_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() . '/votorola/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 votorola::b::Build::call_stack_indentation() . '(jar files ' . scalar @_jar__new_entry . "...\n"; if( $verbosity ) { print "\n$command\n"; print_score( undef, '^' ); } system $command; if( $? != 0 ) { unlink $_jar__jar_file; # easier than reverting its modtime (to effectively undo partial mod of jar) exit system_exit_decode( $? ); } return scalar @_jar__new_entry; } =pod =item B() ret $to_dir Builds the API docs. Note: it sometimes hangs. Probably it has something to do with linking to the other API indeces across the Web. Kill it and re-run to recover, or fix by replacing '-link' options with '-linkoffline'. =cut sub _javadoc__print_packages() # File::Find::find wanted sub { use File::Basename qw( dirname ); our $dir_last; my $file = $File::Find::name; # votorola/fu/bar.java -d $file and return; # directory my $dir = dirname( $file ); # votorola/fu $dir eq $dir_last and return; $file =~ m!\.java$! or return; # Java source $file =~ m!votorola/b/mvn/! and return; # exclude maven symlinks $file =~ m!votorola/b/test/! and return; # exclude test cases $dir =~ m!votorola/(?:a|g)/web/gwt/super! and return; # only compiled by GWT, cf. javadoc() -exclude option $dir_last = $dir; $dir =~ s!/!\.!g; # votorola.fu print ARGFILE $dir; print ARGFILE "\n"; } sub javadoc() { use File::Copy (); use votorola::b::Build qw( ensure_dir out_dir ); use votorola::b::Console qw( print_score $verbosity ); use votorola::b::FileSync qw( sync_to_file ); use votorola::b::Function qw( system_exit_decode ); use votorola::b::GWT (); # my $JDK_dir = JDK_dir(); # avoid the painful javadoc changes in JDK 1,8 my $JDK_dir = '/opt/jdk1.7'; # Classpath, cf. compile() # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - my $class_path = compile_class_outdir(); # Jars from votorola code # ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` for my $file( compile_time_library_jar_list() ) { $class_path .= ':' . $file; } # # Jars from the development kit # # ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` # for my $file( 'jre/lib/plugin.jar' ) # { # $class_path .= ':' . $JDK_dir . '/' . $file; # } # Command # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - my $to_dir = ensure_dir( out_dir() . '/votorola/_/javadoc' ); my $command = $JDK_dir . '/bin/javadoc' . ' -breakiterator' . " -classpath $class_path" . ' -d ' . $to_dir # .q` -doctitle "\"Vote Free\" cartoon"` # . ' -exclude votorola.a.web.gwt.super' # cf. _compile__print_all_source() # . ' -exclude votorola.g.web.gwt.super' # " ## Inapplicable till -subpackages (below) working. Meantime see _javadoc__print_packages(). .q` -header "Votorola"` . ' -link https://docs.oracle.com/javase/' . product_version() . '/docs/api/' . ' -link http://gwt-openlayers.sourceforge.net/maven-site-0.6/apidocs/' . ' -link http://jena.sourceforge.net/javadoc/' . ' -link http://reluk.ca/_/_/java/servlet-2_5-mrel-spec/javadocs/' . ' -link http://reluk.ca/system/host/havoc/opt/javamail-1.4/docs/javadocs/' # . ' -link http://reluk.ca/system/host/havoc/opt/lib-gwt-svg/0.5.5/javadoc/' . ' -link http://reluk.ca/project/_/lib-gwt-svg/release/javadoc-0.5.12+mca.1/' . ' -link http://reluk.ca/system/host/havoc/opt/openid4java-0.9.6.662/apidoc/' . ' -link http://reluk.ca/system/host/havoc/opt/wicket-1.5.4/apidocs/' . ' -linkoffline http://reluk.ca/system/host/havoc/opt/gwt/' . votorola::b::GWT::version() . '/doc/javadoc/ ' . votorola::b::GWT::SDK_dir() . '/doc/javadoc/' . ' -linksource' . ' -noqualifier votorola.*' . ' -overview votorola/b/javadoc-overview.html' . ' -package' . ' -quiet' . ' -sourcepath .' . ' -splitindex' # . ' -subpackages votorola' ## Fails after upgrade to 1.7 with error "unmappable character for encoding UTF8" in ## reference to class file content. No information (2012-1) except this: ## https://bugzilla.redhat.com/show_bug.cgi?id=719118 ## Workaround using argfile (below). . ' -use' . " -windowtitle 'Votorola Java API'" # . ' -Xdoclint:all,-accessibility,-missing' # # -accessibility: allow "no summary or caption for table" # # -missing: allow omission of @param etc. tags ## for JDK 1.8 ; # Packages, workaround for failed "-subpackages votorola" above # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - our $dir_last = ''; # init, prevent warnings local *ARGFILE; my $argfile = build_cache_root() . '/votorola/Java-javadoc-arg-packages'; open ARGFILE, '>', $argfile or die; { File::Find::find( {follow_fast=>1, no_chdir=>1, wanted=>\&_javadoc__print_packages}, 'votorola' ); close ARGFILE; } $command .= ' @' . $argfile; # Execute # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - print votorola::b::Build::call_stack_indentation() . "(javadoc ...\n"; if( $verbosity ) { print "\n$command\n"; print_score( undef, '^' ); } system $command; $? != 0 and exit system_exit_decode( $? ); # - - - sync_to_file( 'votorola/b/javadoc_autoindex-summary.txt', $to_dir . '/_autoindex-summary.html' ); my $default_CSS_file = "$to_dir/stylesheet-default.css"; -f $default_CSS_file or rename( "$to_dir/stylesheet.css", $default_CSS_file ); File::Copy::copy( 'votorola/b/javadoc.css', "$to_dir/stylesheet.css" ) or die; return $to_dir; } =pod =item B() File::Find::find wanted sub that appends the file (if it is a jar) to @list_found_jar. Returns 1 if the file was appended; 0 otherwise. =cut sub list_found_jar() { our @list_found_jar; my $file = $File::Find::name; -d $file and return; # directory $file =~ m'votorola/b/mvn' and return 0; $file =~ m'\.jar$' or return 0; push @list_found_jar, $file; return 1; } =pod =back =cut 1;