Coverage Report - org.codehaus.plexus.compiler.jikes.JikesCompiler
 
Classes in this File Line Coverage Branch Coverage Complexity
JikesCompiler
0%
0/146
0%
0/64
5,778
JikesCompiler$1
0%
0/3
0%
0/4
5,778
 
 1  
 package org.codehaus.plexus.compiler.jikes;
 2  
 
 3  
 /**
 4  
  * The MIT License
 5  
  *
 6  
  * Copyright (c) 2005, The Codehaus
 7  
  *
 8  
  * Permission is hereby granted, free of charge, to any person obtaining a copy of
 9  
  * this software and associated documentation files (the "Software"), to deal in
 10  
  * the Software without restriction, including without limitation the rights to
 11  
  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 12  
  * of the Software, and to permit persons to whom the Software is furnished to do
 13  
  * so, subject to the following conditions:
 14  
  *
 15  
  * The above copyright notice and this permission notice shall be included in all
 16  
  * copies or substantial portions of the Software.
 17  
  *
 18  
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 19  
  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 20  
  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 21  
  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 22  
  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 23  
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 24  
  * SOFTWARE.
 25  
  */
 26  
 
 27  
 /*============================================================================
 28  
                    The Apache Software License, Version 1.1
 29  
  ============================================================================
 30  
 
 31  
  Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
 32  
 
 33  
  Redistribution and use in source and binary forms, with or without modifica-
 34  
  tion, are permitted provided that the following conditions are met:
 35  
 
 36  
  1. Redistributions of  source code must  retain the above copyright  notice,
 37  
     this list of conditions and the following disclaimer.
 38  
 
 39  
  2. Redistributions in binary form must reproduce the above copyright notice,
 40  
     this list of conditions and the following disclaimer in the documentation
 41  
     and/or other materials provided with the distribution.
 42  
 
 43  
  3. The end-user documentation included with the redistribution, if any, must
 44  
     include  the following  acknowledgment:  "This product includes  software
 45  
     developed  by the  Apache Software Foundation  (http://www.apache.org/)."
 46  
     Alternately, this  acknowledgment may  appear in the software itself,  if
 47  
     and wherever such third-party acknowledgments normally appear.
 48  
 
 49  
  4. The names "Apache Cocoon" and  "Apache Software Foundation" must  not  be
 50  
     used to  endorse or promote  products derived from  this software without
 51  
     prior written permission. For written permission, please contact
 52  
     apache@apache.org.
 53  
 
 54  
  5. Products  derived from this software may not  be called "Apache", nor may
 55  
     "Apache" appear  in their name,  without prior written permission  of the
 56  
     Apache Software Foundation.
 57  
 
 58  
  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
 59  
  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 60  
  FITNESS  FOR A PARTICULAR  PURPOSE ARE  DISCLAIMED.  IN NO  EVENT SHALL  THE
 61  
  APACHE SOFTWARE  FOUNDATION  OR ITS CONTRIBUTORS  BE LIABLE FOR  ANY DIRECT,
 62  
  INDIRECT, INCIDENTAL, SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLU-
 63  
  DING, BUT NOT LIMITED TO, PROCUREMENT  OF SUBSTITUTE GOODS OR SERVICES; LOSS
 64  
  OF USE, DATA, OR  PROFITS; OR BUSINESS  INTERRUPTION)  HOWEVER CAUSED AND ON
 65  
  ANY  THEORY OF LIABILITY,  WHETHER  IN CONTRACT,  STRICT LIABILITY,  OR TORT
 66  
  (INCLUDING  NEGLIGENCE OR  OTHERWISE) ARISING IN  ANY WAY OUT OF THE  USE OF
 67  
  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 68  
 
 69  
  This software  consists of voluntary contributions made  by many individuals
 70  
  on  behalf of the Apache Software  Foundation and was  originally created by
 71  
  Stefano Mazzocchi  <stefano@apache.org>. For more  information on the Apache
 72  
  Software Foundation, please see <http://www.apache.org/>.
 73  
 
 74  
 */
 75  
 
 76  
 import org.codehaus.plexus.compiler.AbstractCompiler;
 77  
 import org.codehaus.plexus.compiler.CompilerConfiguration;
 78  
 import org.codehaus.plexus.compiler.CompilerException;
 79  
 import org.codehaus.plexus.compiler.CompilerMessage;
 80  
 import org.codehaus.plexus.compiler.CompilerOutputStyle;
 81  
 import org.codehaus.plexus.compiler.CompilerResult;
 82  
 import org.codehaus.plexus.compiler.util.StreamPumper;
 83  
 import org.codehaus.plexus.util.IOUtil;
 84  
 import org.codehaus.plexus.util.Os;
 85  
 import org.codehaus.plexus.util.StringUtils;
 86  
 
 87  
 import java.io.BufferedInputStream;
 88  
 import java.io.BufferedReader;
 89  
 import java.io.BufferedWriter;
 90  
 import java.io.ByteArrayInputStream;
 91  
 import java.io.ByteArrayOutputStream;
 92  
 import java.io.File;
 93  
 import java.io.FileFilter;
 94  
 import java.io.FileWriter;
 95  
 import java.io.IOException;
 96  
 import java.io.InputStreamReader;
 97  
 import java.util.ArrayList;
 98  
 import java.util.List;
 99  
 import java.util.Map;
 100  
 
 101  
 /**
 102  
  * @plexus.component role="org.codehaus.plexus.compiler.Compiler" role-hint="jikes"
 103  
  */
 104  
 public class JikesCompiler
 105  
     extends AbstractCompiler
 106  
 {
 107  
     private static final int OUTPUT_BUFFER_SIZE = 1024;
 108  
 
 109  
     public JikesCompiler()
 110  
     {
 111  0
         super( CompilerOutputStyle.ONE_OUTPUT_FILE_PER_INPUT_FILE, ".java", ".class", null );
 112  0
     }
 113  
 
 114  
     // -----------------------------------------------------------------------
 115  
     // Compiler Implementation
 116  
     // -----------------------------------------------------------------------
 117  
 
 118  
     public CompilerResult performCompile( CompilerConfiguration config )
 119  
         throws CompilerException
 120  
     {
 121  
         // Ensures that the directory exist.
 122  0
         getDestinationDir( config );
 123  
 
 124  
         try
 125  
         {
 126  
             // TODO: This should use the CommandLine stuff from plexus-utils.
 127  
 
 128  
             // -----------------------------------------------------------------------
 129  
             // Execute the compiler
 130  
             // -----------------------------------------------------------------------
 131  
 
 132  0
             Process p = Runtime.getRuntime().exec( createCommandLine( config ) );
 133  
 
 134  0
             BufferedInputStream compilerErr = new BufferedInputStream( p.getErrorStream() );
 135  
 
 136  0
             ByteArrayOutputStream tmpErr = new ByteArrayOutputStream( OUTPUT_BUFFER_SIZE );
 137  0
             StreamPumper errPumper = new StreamPumper( compilerErr, tmpErr );
 138  
 
 139  0
             errPumper.start();
 140  
 
 141  0
             p.waitFor();
 142  
 
 143  0
             int exitValue = p.exitValue();
 144  
 
 145  
             // Wait until the complete error stream has been read
 146  0
             errPumper.join();
 147  
 
 148  0
             compilerErr.close();
 149  
 
 150  0
             p.destroy();
 151  
 
 152  0
             tmpErr.close();
 153  
 
 154  
             // -----------------------------------------------------------------------
 155  
             // Parse the output
 156  
             // -----------------------------------------------------------------------
 157  
 
 158  0
             BufferedReader input =
 159  0
                 new BufferedReader( new InputStreamReader( new ByteArrayInputStream( tmpErr.toByteArray() ) ) );
 160  
 
 161  0
             List<CompilerMessage> messages = new ArrayList<CompilerMessage>();
 162  
 
 163  0
             parseStream( input, messages );
 164  
 
 165  0
             if ( exitValue != 0 && exitValue != 1 )
 166  
             {
 167  0
                 messages.add( new CompilerMessage( "Exit code from jikes was not 0 or 1 ->" + exitValue, true ) );
 168  
             }
 169  
 
 170  0
             return new CompilerResult().compilerMessages( messages );
 171  
         }
 172  0
         catch ( IOException e )
 173  
         {
 174  0
             throw new CompilerException( "Error while compiling.", e );
 175  
         }
 176  0
         catch ( InterruptedException e )
 177  
         {
 178  0
             throw new CompilerException( "Error while compiling.", e );
 179  
         }
 180  
     }
 181  
 
 182  
     public String[] createCommandLine( CompilerConfiguration config )
 183  
         throws CompilerException
 184  
     {
 185  0
         List<String> args = new ArrayList<String>();
 186  
 
 187  0
         args.add( "jikes" );
 188  
 
 189  0
         String bootClassPath = getPathString( getBootClassPath() );
 190  0
         getLogger().debug( "Bootclasspath: " + bootClassPath );
 191  0
         if ( !StringUtils.isEmpty( bootClassPath ) )
 192  
         {
 193  0
             args.add( "-bootclasspath" );
 194  0
             args.add( bootClassPath );
 195  
         }
 196  
 
 197  0
         String classPath = getPathString( config.getClasspathEntries() );
 198  0
         getLogger().debug( "Classpath: " + classPath );
 199  0
         if ( !StringUtils.isEmpty( classPath ) )
 200  
         {
 201  0
             args.add( "-classpath" );
 202  0
             args.add( classPath );
 203  
         }
 204  
 
 205  0
         args.add( "+E" );
 206  
 
 207  0
         if ( config.getCustomCompilerArguments().size() > 0 )
 208  
         {
 209  0
             for ( Map.Entry<String, String> arg : config.getCustomCompilerArgumentsAsMap().entrySet() )
 210  
             {
 211  0
                 args.add( arg.getKey() );
 212  0
                 args.add( arg.getValue() );
 213  0
             }
 214  
         }
 215  
 
 216  0
         args.add( "-target" );
 217  0
         if ( StringUtils.isNotEmpty( config.getTargetVersion() ) )
 218  
         {
 219  0
             args.add( config.getTargetVersion() );
 220  
         }
 221  
         else
 222  
         {
 223  0
             args.add( "1.1" );
 224  
         }
 225  
 
 226  0
         args.add( "-source" );
 227  0
         if ( StringUtils.isNotEmpty( config.getSourceVersion() ) )
 228  
         {
 229  0
             args.add( config.getSourceVersion() );
 230  
         }
 231  
         else
 232  
         {
 233  0
             args.add( "1.3" );
 234  
         }
 235  
 
 236  0
         if ( !config.isShowWarnings() )
 237  
         {
 238  0
             args.add( "-nowarn" );
 239  
         }
 240  
 
 241  0
         if ( config.isShowDeprecation() )
 242  
         {
 243  0
             args.add( "-deprecation" );
 244  
         }
 245  
 
 246  0
         if ( config.isOptimize() )
 247  
         {
 248  0
             args.add( "-O" );
 249  
         }
 250  
 
 251  0
         if ( config.isVerbose() )
 252  
         {
 253  0
             args.add( "-verbose" );
 254  
         }
 255  
 
 256  0
         if ( config.isDebug() )
 257  
         {
 258  0
             args.add( "-g:lines" );
 259  
         }
 260  
 
 261  0
         args.add( "-d" );
 262  
 
 263  0
         args.add( getDestinationDir( config ).getAbsolutePath() );
 264  
 
 265  0
         String sourcePath = getPathString( config.getSourceLocations() );
 266  0
         getLogger().debug( "Source path:" + sourcePath );
 267  0
         if ( !StringUtils.isEmpty( sourcePath ) )
 268  
         {
 269  0
             args.add( "-sourcepath" );
 270  0
             args.add( sourcePath );
 271  
         }
 272  
 
 273  0
         String[] sourceFiles = getSourceFiles( config );
 274  
 
 275  0
         if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
 276  
         {
 277  0
             String tempFileName = null;
 278  0
             BufferedWriter fw = null;
 279  
 
 280  
             try
 281  
             {
 282  0
                 File tempFile = File.createTempFile( "compList", ".cmp" );
 283  0
                 tempFileName = tempFile.getAbsolutePath();
 284  0
                 getLogger().debug( "create TempFile" + tempFileName );
 285  
 
 286  0
                 tempFile.getParentFile().mkdirs();
 287  0
                 fw = new BufferedWriter( new FileWriter( tempFile ) );
 288  0
                 for ( int i = 0; i < sourceFiles.length; i++ )
 289  
                 {
 290  0
                     fw.write( sourceFiles[i] );
 291  0
                     fw.newLine();
 292  
                 }
 293  0
                 fw.flush();
 294  0
                 tempFile.deleteOnExit();
 295  
             }
 296  0
             catch ( IOException e )
 297  
             {
 298  0
                 throw new CompilerException( "Could not create temporary file " + tempFileName, e );
 299  
             }
 300  
             finally
 301  
             {
 302  0
                 IOUtil.close( fw );
 303  0
             }
 304  
 
 305  0
             args.add( "@" + tempFileName );
 306  0
         }
 307  
         else
 308  
         {
 309  0
             for ( int i = 0; i < sourceFiles.length; i++ )
 310  
             {
 311  0
                 args.add( sourceFiles[i] );
 312  
             }
 313  
 
 314  
         }
 315  
 
 316  0
         return (String[]) args.toArray( new String[args.size()] );
 317  
     }
 318  
 
 319  
     // -----------------------------------------------------------------------
 320  
     // Private
 321  
     // -----------------------------------------------------------------------
 322  
 
 323  
     private File getDestinationDir( CompilerConfiguration config )
 324  
     {
 325  0
         File destinationDir = new File( config.getOutputLocation() );
 326  
 
 327  0
         if ( !destinationDir.exists() )
 328  
         {
 329  0
             destinationDir.mkdirs();
 330  
         }
 331  
 
 332  0
         return destinationDir;
 333  
     }
 334  
 
 335  
     private List<String> getBootClassPath()
 336  
     {
 337  0
         List<String> bootClassPath = new ArrayList<String>();
 338  0
         FileFilter filter = new FileFilter()
 339  0
         {
 340  
 
 341  
             public boolean accept( File file )
 342  
             {
 343  0
                 String name = file.getName();
 344  0
                 return name.endsWith( ".jar" ) || name.endsWith( ".zip" );
 345  
             }
 346  
 
 347  
         };
 348  
 
 349  0
         File javaHomeDir = new File( System.getProperty( "java.home" ) );
 350  
 
 351  0
         File javaLibDir = new File( javaHomeDir, "lib" );
 352  0
         if ( javaLibDir.isDirectory() )
 353  
         {
 354  0
             bootClassPath.addAll( asList( javaLibDir.listFiles( filter ) ) );
 355  
         }
 356  
 
 357  0
         File javaClassesDir = new File( javaHomeDir, "../Classes" );
 358  0
         if ( javaClassesDir.isDirectory() )
 359  
         {
 360  0
             bootClassPath.addAll( asList( javaClassesDir.listFiles( filter ) ) );
 361  
         }
 362  
 
 363  0
         File javaExtDir = new File( javaLibDir, "ext" );
 364  0
         if ( javaExtDir.isDirectory() )
 365  
         {
 366  0
             bootClassPath.addAll( asList( javaExtDir.listFiles( filter ) ) );
 367  
         }
 368  
 
 369  0
         return bootClassPath;
 370  
     }
 371  
 
 372  
     private List<String> asList( File[] files )
 373  
     {
 374  0
         List<String> filenames = new ArrayList<String>( files.length );
 375  0
         for ( File file : files )
 376  
         {
 377  0
             filenames.add( file.toString() );
 378  
         }
 379  0
         return filenames;
 380  
     }
 381  
 
 382  
     /**
 383  
      * Parse the compiler error stream to produce a list of
 384  
      * <code>CompilerMessage</code>s
 385  
      *
 386  
      * @param input The error stream
 387  
      * @return The list of compiler error messages
 388  
      * @throws IOException If an error occurs during message collection
 389  
      */
 390  
     protected List<CompilerMessage> parseStream( BufferedReader input, List<CompilerMessage> messages )
 391  
         throws IOException
 392  
     {
 393  0
         String line = null;
 394  
         StringBuilder buffer;
 395  
 
 396  
         while ( true )
 397  
         {
 398  
             // cleanup the buffer
 399  0
             buffer = new StringBuilder(); // this is faster than clearing it
 400  
 
 401  
             // first line is not space-starting
 402  0
             if ( line == null )
 403  
             {
 404  0
                 line = input.readLine();
 405  
             }
 406  
 
 407  0
             if ( line == null )
 408  
             {
 409  0
                 return messages;
 410  
             }
 411  
 
 412  0
             buffer.append( line );
 413  
 
 414  
             // all other space-starting lines are one error
 415  
             while ( true )
 416  
             {
 417  0
                 line = input.readLine();
 418  
                 // EOF
 419  0
                 if ( line == null )
 420  
                 {
 421  0
                     break;
 422  
                 }
 423  
                 // Continuation of previous error starts with ' '
 424  0
                 if ( line.length() > 0 && line.charAt( 0 ) != ' ' )
 425  
                 {
 426  0
                     break;
 427  
                 }
 428  0
                 buffer.append( EOL );
 429  0
                 buffer.append( line );
 430  
             }
 431  
 
 432  0
             if ( buffer.length() > 0 )
 433  
             {
 434  
                 // add the error bean
 435  0
                 messages.add( parseError( buffer.toString() ) );
 436  
             }
 437  
         }
 438  
     }
 439  
 
 440  
     /**
 441  
      * Parse an individual compiler error message
 442  
      *
 443  
      * @param error The error text
 444  
      * @return A mssaged <code>CompilerMessage</code>
 445  
      */
 446  
     private CompilerMessage parseError( String error )
 447  
     {
 448  0
         if ( error.startsWith( "Error:" ) )
 449  
         {
 450  0
             return new CompilerMessage( error, true );
 451  
         }
 452  
 
 453  0
         String[] errorBits = StringUtils.split( error, ":" );
 454  
 
 455  
         int i;
 456  
         String file;
 457  
 
 458  0
         if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
 459  
         {
 460  0
             file = errorBits[0] + ':' + errorBits[1];
 461  0
             i = 2;
 462  
         }
 463  
         else
 464  
         {
 465  0
             file = errorBits[0];
 466  0
             i = 1;
 467  
         }
 468  
 
 469  0
         int startline = Integer.parseInt( errorBits[i++] );
 470  
 
 471  0
         int startcolumn = Integer.parseInt( errorBits[i++] );
 472  
 
 473  0
         int endline = Integer.parseInt( errorBits[i++] );
 474  
 
 475  0
         int endcolumn = Integer.parseInt( errorBits[i++] );
 476  
 
 477  0
         String type = errorBits[i++];
 478  
 
 479  0
         StringBuilder message = new StringBuilder( errorBits[i++] );
 480  0
         while ( i < errorBits.length )
 481  
         {
 482  0
             message.append( ':' ).append( errorBits[i++] );
 483  
         }
 484  
 
 485  0
         return new CompilerMessage( file, type.indexOf( "Error" ) > -1, startline, startcolumn, endline, endcolumn,
 486  0
                                     message.toString() );
 487  
     }
 488  
 }