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.setDestDirectory(dest); 362 unArchiver.extract(); 363 364 String packageName = project.getArtifactId()+"-"+project.getVersion()+"-"+sourceClassifier; 365 File source = new File(dest, packageName); 366 FileUtils.copyDirectoryStructureIfModified(source, buildDir); 367 368 } catch (Throwable e) { 369 throw new MojoExecutionException("Could not extract the native source package.", e); 370 } 371 } 372 373 }