1 package org.codehaus.mojo.natives.compiler;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 import java.io.File;
25 import java.io.IOException;
26 import java.util.ArrayList;
27 import java.util.List;
28
29 import org.codehaus.mojo.natives.NativeBuildException;
30 import org.codehaus.mojo.natives.SourceDependencyAnalyzer;
31 import org.codehaus.mojo.natives.parser.Parser;
32 import org.codehaus.mojo.natives.util.CommandLineUtil;
33 import org.codehaus.mojo.natives.util.EnvUtil;
34 import org.codehaus.plexus.logging.AbstractLogEnabled;
35 import org.codehaus.plexus.logging.Logger;
36 import org.codehaus.plexus.util.FileUtils;
37 import org.codehaus.plexus.util.Os;
38 import org.codehaus.plexus.util.cli.Commandline;
39
40 import edu.emory.mathcs.backport.java.util.concurrent.ArrayBlockingQueue;
41 import edu.emory.mathcs.backport.java.util.concurrent.RejectedExecutionException;
42 import edu.emory.mathcs.backport.java.util.concurrent.ThreadPoolExecutor;
43 import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;
44
45 public abstract class AbstractCompiler
46 extends AbstractLogEnabled
47 implements Compiler
48 {
49
50 protected abstract Parser getParser();
51
52 protected abstract Commandline getCommandLine( File src, File dest, CompilerConfiguration config )
53 throws NativeBuildException;
54
55 public List compile( CompilerConfiguration config, File[] sourceFiles )
56 throws NativeBuildException
57 {
58 if ( !config.getOutputDirectory().exists() )
59 {
60 config.getOutputDirectory().mkdirs();
61 }
62
63 List compilerOutputFiles = new ArrayList( sourceFiles.length );
64
65 CompilerThreadPoolExecutor compilerThreadPoolExecutor = null;
66
67 if ( config.getNumberOfConcurrentCompilation() > 1 )
68 {
69 compilerThreadPoolExecutor = new CompilerThreadPoolExecutor( config.getNumberOfConcurrentCompilation() );
70 }
71
72 for ( int i = 0; i < sourceFiles.length; ++i )
73 {
74 File source = sourceFiles[i];
75
76 File objectFile = getObjectFile( source, config.getOutputDirectory(), config.getObjectFileExtension() );
77
78 compilerOutputFiles.add( objectFile );
79
80 Parser parser = this.getParser();
81
82 if ( SourceDependencyAnalyzer.isStaled( source, objectFile, parser, config.getIncludePaths() ) )
83 {
84 if ( compilerThreadPoolExecutor != null && compilerThreadPoolExecutor.isErrorFound() )
85 {
86 break;
87 }
88
89 Commandline cl = getCommandLine( source, objectFile, config );
90 EnvUtil.setupCommandlineEnv( cl, config.getEnvFactory() );
91
92 if ( compilerThreadPoolExecutor != null )
93 {
94 try
95 {
96 compilerThreadPoolExecutor.execute( new CompilerRunnable( cl, this.getLogger() ) );
97 }
98 catch ( RejectedExecutionException e )
99 {
100 CommandLineUtil.execute( cl, this.getLogger() );
101 }
102 }
103 else
104 {
105 CommandLineUtil.execute( cl, this.getLogger() );
106 }
107
108 }
109 else
110 {
111 this.getLogger().debug( ( objectFile + " is up to date." ) );
112 }
113 }
114
115 if ( compilerThreadPoolExecutor != null )
116 {
117 if ( !compilerThreadPoolExecutor.isErrorFound() )
118 {
119 compilerThreadPoolExecutor.shutdown();
120 }
121
122 try
123 {
124 compilerThreadPoolExecutor.awaitTermination( Integer.MAX_VALUE, TimeUnit.SECONDS );
125 }
126 catch ( InterruptedException e )
127 {
128 }
129
130 if ( compilerThreadPoolExecutor.isErrorFound() )
131 {
132 throw new NativeBuildException( "Compilation failure detected." );
133 }
134 }
135
136 return compilerOutputFiles;
137 }
138
139
140
141
142
143
144 protected static String getObjectFileExtension( String fileExtension )
145 {
146 if ( fileExtension != null )
147 {
148 return fileExtension;
149 }
150 else
151 {
152 if ( Os.isFamily( "windows" ) )
153 {
154 return "obj";
155 }
156 else
157 {
158 return "o";
159 }
160 }
161 }
162
163
164
165
166
167
168
169
170
171
172 protected static File getObjectFile( File sourceFile, File outputDirectory, String objectFileExtension )
173 throws NativeBuildException
174 {
175 String objectFileName;
176
177 try
178 {
179 objectFileExtension = AbstractCompiler.getObjectFileExtension( objectFileExtension );
180
181
182
183
184 objectFileName = FileUtils.basename( sourceFile.getCanonicalPath() );
185
186 if ( objectFileName.charAt( objectFileName.length() - 1 ) != '.' )
187 {
188 objectFileName += "." + objectFileExtension;
189 }
190 else
191 {
192 objectFileName += objectFileExtension;
193 }
194 }
195 catch ( IOException e )
196 {
197 throw new NativeBuildException( "Failed to figure out object file name for " + sourceFile + ": "
198 + e.getMessage(), e );
199 }
200
201 File objectFile = new File( outputDirectory, objectFileName );
202
203 return objectFile;
204
205 }
206
207 private class CompilerThreadPoolExecutor
208 extends ThreadPoolExecutor
209 {
210 private boolean errorFound = false;
211
212 public synchronized void setErrorFound( boolean errorFound )
213 {
214 this.errorFound = errorFound;
215 }
216
217 public synchronized boolean isErrorFound()
218 {
219 return errorFound;
220 }
221
222 public CompilerThreadPoolExecutor( int corePoolSize )
223 {
224 super( corePoolSize, corePoolSize, 30, TimeUnit.SECONDS, new ArrayBlockingQueue( corePoolSize * 2 ) );
225 }
226
227 protected void afterExecute( Runnable r, Throwable t )
228 {
229 super.afterExecute( r, t );
230
231 if ( t != null )
232 {
233 this.setErrorFound( true );
234
235 this.shutdown();
236 }
237 }
238
239 protected void beforeExecute( Thread t, Runnable r )
240 {
241 super.beforeExecute( t, r );
242
243
244 if ( this.isErrorFound() )
245 {
246 ( (CompilerRunnable) r ).setSkip( true );
247 }
248 }
249 }
250
251 public class CompilerRunnable
252 implements Runnable
253 {
254 private Commandline cl;
255
256 private Logger logger;
257
258 private boolean skip = false;
259
260 public void setSkip( boolean skip )
261 {
262 this.skip = skip;
263 }
264
265 public CompilerRunnable( Commandline cl, Logger logger )
266 {
267 this.cl = cl;
268 this.logger = logger;
269 }
270
271 public void run()
272 throws NativeBuildException
273 {
274 if ( skip )
275 {
276 return;
277 }
278
279 CommandLineUtil.execute( cl, logger );
280 }
281
282 }
283
284 }