001 /**
002 * Copyright (C) 2009 Progress Software, Inc.
003 * http://fusesource.com
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.fusesource.hawtjni.maven;
018
019 import java.io.File;
020 import java.io.IOException;
021 import java.util.List;
022
023 import org.apache.maven.artifact.Artifact;
024 import org.apache.maven.artifact.factory.ArtifactFactory;
025 import org.apache.maven.artifact.repository.ArtifactRepository;
026 import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
027 import org.apache.maven.artifact.resolver.ArtifactResolutionException;
028 import org.apache.maven.artifact.resolver.ArtifactResolver;
029 import org.apache.maven.model.Resource;
030 import org.apache.maven.plugin.AbstractMojo;
031 import org.apache.maven.plugin.MojoExecutionException;
032 import org.apache.maven.project.MavenProject;
033 import org.codehaus.plexus.archiver.UnArchiver;
034 import org.codehaus.plexus.archiver.manager.ArchiverManager;
035 import org.codehaus.plexus.util.FileUtils;
036 import org.codehaus.plexus.util.cli.CommandLineException;
037 import org.fusesource.hawtjni.runtime.Library;
038
039 /**
040 * This goal builds the JNI module which was previously
041 * generated with the generate goal. It adds the JNI module
042 * to the test resource path so that unit tests can load
043 * the freshly built JNI library.
044 *
045 * @goal build
046 * @phase generate-test-resources
047 * @author <a href="http://hiramchirino.com">Hiram Chirino</a>
048 */
049 public class BuildMojo extends AbstractMojo {
050
051 /**
052 * The maven project.
053 *
054 * @parameter expression="${project}"
055 * @required
056 * @readonly
057 */
058 protected MavenProject project;
059
060 /**
061 * Remote repositories
062 *
063 * @parameter expression="${project.remoteArtifactRepositories}"
064 * @required
065 * @readonly
066 */
067 protected List remoteArtifactRepositories;
068
069 /**
070 * Local maven repository.
071 *
072 * @parameter expression="${localRepository}"
073 * @required
074 * @readonly
075 */
076 protected ArtifactRepository localRepository;
077
078 /**
079 * Artifact factory, needed to download the package source file
080 *
081 * @component role="org.apache.maven.artifact.factory.ArtifactFactory"
082 * @required
083 * @readonly
084 */
085 protected ArtifactFactory artifactFactory;
086
087 /**
088 * Artifact resolver, needed to download the package source file
089 *
090 * @component role="org.apache.maven.artifact.resolver.ArtifactResolver"
091 * @required
092 * @readonly
093 */
094 protected ArtifactResolver artifactResolver;
095
096 /**
097 * @component
098 * @required
099 * @readonly
100 */
101 private ArchiverManager archiverManager;
102
103 /**
104 * The base name of the library, used to determine generated file names.
105 *
106 * @parameter default-value="${project.artifactId}"
107 */
108 private String name;
109
110 /**
111 * Where the unpacked build package is located.
112 *
113 * @parameter default-value="${project.build.directory}/generated-sources/hawtjni/native-package"
114 */
115 private File packageDirectory;
116
117 /**
118 * The output directory where the built JNI library will placed. This directory will be added
119 * to as a test resource path so that unit tests can verify the built JNI library.
120 *
121 * The library will placed under the META-INF/native/${platform} directory that the HawtJNI
122 * Library uses to find JNI libraries as classpath resources.
123 *
124 * @parameter default-value="${project.build.directory}/generated-sources/hawtjni/lib"
125 */
126 private File libDirectory;
127
128 /**
129 * The directory where the build will be produced. It creates a native-build and native-dist directory
130 * under the specified directory.
131 *
132 * @parameter default-value="${project.build.directory}"
133 */
134 private File buildDirectory;
135
136 /**
137 * Should we skip executing the autogen.sh file.
138 *
139 * @parameter default-value="${skip-autogen}"
140 */
141 private boolean skipAutogen;
142
143 /**
144 * Should we force executing the autogen.sh file.
145 *
146 * @parameter default-value="${force-autogen}"
147 */
148 private boolean forceAutogen;
149
150 /**
151 * Extra arguments you want to pass to the autogen.sh command.
152 *
153 * @parameter
154 */
155 private List<String> autogenArgs;
156
157 /**
158 * Should we skip executing the configure command.
159 *
160 * @parameter default-value="${skip-configure}"
161 */
162 private boolean skipConfigure;
163
164 /**
165 * Should we force executing the configure command.
166 *
167 * @parameter default-value="${force-configure}"
168 */
169 private boolean forceConfigure;
170
171 /**
172 * Should we display all the native build output?
173 *
174 * @parameter default-value="${hawtjni-verbose}"
175 */
176 private boolean verbose;
177
178 /**
179 * Extra arguments you want to pass to the configure command.
180 *
181 * @parameter
182 */
183 private List<String> configureArgs;
184
185 /**
186 * The platform identifier of this build. If not specified,
187 * it will be automatically detected.
188 *
189 * @parameter
190 */
191 private String platform;
192
193 /**
194 * The classifier of the package archive that will be created.
195 *
196 * @parameter default-value="native-src"
197 */
198 private String sourceClassifier;
199
200 /**
201 * If the source build could not be fully generated, perhaps the autotools
202 * were not available on this platform, should we attempt to download
203 * a previously deployed source package and build that?
204 *
205 * @parameter default-value="true"
206 */
207 private boolean downloadSourcePackage = true;
208
209 private final CLI cli = new CLI();
210
211 public void execute() throws MojoExecutionException {
212 cli.verbose = verbose;
213 cli.log = getLog();
214 try {
215 File buildDir = new File(buildDirectory, "native-build");
216 buildDir.mkdirs();
217 if ( CLI.IS_WINDOWS ) {
218 vsBasedBuild(buildDir);
219 } else {
220 configureBasedBuild(buildDir);
221 }
222
223 getLog().info("Adding test resource root: "+libDirectory.getAbsolutePath());
224 Resource testResource = new Resource();
225 testResource.setDirectory(libDirectory.getAbsolutePath());
226 this.project.addTestResource(testResource); //();
227
228 } catch (Exception e) {
229 throw new MojoExecutionException("build failed: "+e, e);
230 }
231 }
232
233 private void vsBasedBuild(File buildDir) throws CommandLineException, MojoExecutionException, IOException {
234
235 FileUtils.copyDirectoryStructureIfModified(packageDirectory, buildDir);
236
237 Library library = new Library(name);
238 String platform;
239 String configuration="release";
240 if( "windows32".equals(library.getPlatform()) ) {
241 platform = "Win32";
242 } else if( "windows64".equals(library.getPlatform()) ) {
243 platform = "x64";
244 } else {
245 throw new MojoExecutionException("Usupported platform: "+library.getPlatform());
246 }
247
248 String toolset = System.getenv("PlatformToolset");
249 if( "Windows7.1SDK".equals(toolset) ) {
250 // vcbuild was removed.. use the msbuild tool instead.
251 int rc = cli.system(buildDir, new String[]{"msbuild", "vs2010.vcxproj", "/property:Platform="+platform, "/property:Configuration="+configuration});
252 if( rc != 0 ) {
253 throw new MojoExecutionException("vcbuild failed with exit code: "+rc);
254 }
255 } else {
256 // try to use a vcbuild..
257 int rc = cli.system(buildDir, new String[]{"vcbuild", "/platform:"+platform, "vs2008.vcproj", configuration});
258 if( rc != 0 ) {
259 throw new MojoExecutionException("vcbuild failed with exit code: "+rc);
260 }
261 }
262
263
264
265 File libFile=FileUtils.resolveFile(buildDir, "target/"+platform+"-"+configuration+"/lib/"+library.getLibraryFileName());
266 if( !libFile.exists() ) {
267 throw new MojoExecutionException("vcbuild did not generate: "+libFile);
268 }
269
270 File target=FileUtils.resolveFile(libDirectory, library.getPlatformSpecifcResourcePath());
271 FileUtils.copyFile(libFile, target);
272 }
273
274
275 private void configureBasedBuild(File buildDir) throws IOException, MojoExecutionException, CommandLineException {
276
277 File configure = new File(packageDirectory, "configure");
278 if( configure.exists() ) {
279 FileUtils.copyDirectoryStructureIfModified(packageDirectory, buildDir);
280 } else if (downloadSourcePackage) {
281 downloadNativeSourcePackage(buildDir);
282 } else {
283 throw new MojoExecutionException("The configure script is missing from the generated native source package and downloadSourcePackage is disabled: "+configure);
284 }
285
286 configure = new File(buildDir, "configure");
287 File autogen = new File(buildDir, "autogen.sh");
288 File makefile = new File(buildDir, "Makefile");
289
290 File distDirectory = new File(buildDir, "target");
291 File distLibDirectory = new File(distDirectory, "lib");
292 distLibDirectory.mkdirs();
293
294 if( autogen.exists() && !skipAutogen ) {
295 if( (!configure.exists() && !CLI.IS_WINDOWS) || forceAutogen ) {
296 cli.setExecutable(autogen);
297 int rc = cli.system(buildDir, new String[] {"./autogen.sh"}, autogenArgs);
298 if( rc != 0 ) {
299 throw new MojoExecutionException("./autogen.sh failed with exit code: "+rc);
300 }
301 }
302 }
303
304 if( configure.exists() && !skipConfigure ) {
305 if( !makefile.exists() || forceConfigure ) {
306
307 File autotools = new File(buildDir, "autotools");
308 File[] listFiles = autotools.listFiles();
309 if( listFiles!=null ) {
310 for (File file : listFiles) {
311 cli.setExecutable(file);
312 }
313 }
314
315 cli.setExecutable(configure);
316 int rc = cli.system(buildDir, new String[]{"./configure", "--disable-ccache", "--prefix="+distDirectory.getCanonicalPath()}, configureArgs);
317 if( rc != 0 ) {
318 throw new MojoExecutionException("./configure failed with exit code: "+rc);
319 }
320 }
321 }
322
323 int rc = cli.system(buildDir, new String[]{"make", "install"});
324 if( rc != 0 ) {
325 throw new MojoExecutionException("make based build failed with exit code: "+rc);
326 }
327
328 Library library = new Library(name);
329
330 File libFile = new File(distLibDirectory, library.getLibraryFileName());
331 if( !libFile.exists() ) {
332 throw new MojoExecutionException("Make based build did not generate: "+libFile);
333 }
334
335 if( platform == null ) {
336 platform = library.getPlatform();
337 }
338
339 File target=FileUtils.resolveFile(libDirectory, library.getPlatformSpecifcResourcePath(platform));
340 FileUtils.copyFile(libFile, target);
341 }
342
343 public void downloadNativeSourcePackage(File buildDir) throws MojoExecutionException {
344 Artifact artifact = artifactFactory.createArtifactWithClassifier(project.getGroupId(), project.getArtifactId(), project.getVersion(), "zip", sourceClassifier);
345 try {
346 artifactResolver.resolve(artifact, remoteArtifactRepositories, localRepository);
347 } catch (ArtifactResolutionException e) {
348 throw new MojoExecutionException("Error downloading.", e);
349 } catch (ArtifactNotFoundException e) {
350 throw new MojoExecutionException("Requested download does not exist.", e);
351 }
352
353 File packageZipFile = artifact.getFile();
354
355 try {
356 File dest = new File(buildDirectory, "native-build-extracted");
357 getLog().info("Extracting "+packageZipFile+" to "+dest);
358
359 UnArchiver unArchiver = archiverManager.getUnArchiver("zip");
360 unArchiver.setSourceFile(packageZipFile);
361 unArchiver.extract("", dest);
362
363 String packageName = project.getArtifactId()+"-"+project.getVersion()+"-"+sourceClassifier;
364 File source = new File(dest, packageName);
365 FileUtils.copyDirectoryStructureIfModified(source, buildDir);
366
367 } catch (Throwable e) {
368 throw new MojoExecutionException("Could not extract the native source package.", e);
369 }
370 }
371
372 }