Java tutorial
/* * Copyright (C) 2009 Jayway AB * Copyright (C) 2007-2008 JVending Masa * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.jayway.maven.plugins.android.phase09package; import com.jayway.maven.plugins.android.AbstractAndroidMojo; import com.jayway.maven.plugins.android.CommandExecutor; import com.jayway.maven.plugins.android.ExecutionException; import com.jayway.maven.plugins.android.common.AaptCommandBuilder; import com.jayway.maven.plugins.android.common.NativeHelper; import com.jayway.maven.plugins.android.config.PullParameter; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; import org.apache.maven.artifact.Artifact; import org.apache.maven.model.Resource; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.ResolutionScope; import org.codehaus.plexus.archiver.ArchiverException; import org.codehaus.plexus.archiver.jar.JarArchiver; import org.codehaus.plexus.archiver.util.DefaultFileSet; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; import java.util.ArrayList; import java.util.List; import static com.jayway.maven.plugins.android.common.AndroidExtension.AAR; import static com.jayway.maven.plugins.android.common.AndroidExtension.APKLIB; /** * Creates the apklib file.<br/> * apklib files do not generate deployable artifacts. * * @author nmaiorana@gmail.com * @deprecated Use Aar instead see {@link com.jayway.maven.plugins.android.phase09package.AarMojo} */ @Deprecated @Mojo(name = "apklib", defaultPhase = LifecyclePhase.PACKAGE, requiresDependencyResolution = ResolutionScope.COMPILE) public class ApklibMojo extends AbstractAndroidMojo { /** * The name of the top level folder in the APKLIB where native libraries are found. * NOTE: This is inconsistent with APK where the folder is called "lib" */ public static final String NATIVE_LIBRARIES_FOLDER = "libs"; /** * <p>Classifier to add to the artifact generated. If given, the artifact will be an attachment instead.</p> */ @Parameter private String classifier; /** * Specifies the application makefile to use for the build (if other than the default Application.mk). */ @Parameter @PullParameter private String applicationMakefile; /** * Defines the architecture for the NDK build */ @Parameter(property = "android.ndk.build.architecture") @PullParameter private String ndkArchitecture; /** * Specifies the classifier with which the artifact should be stored in the repository */ @Parameter(property = "android.ndk.build.native-classifier") @PullParameter private String ndkClassifier; private List<String> sourceFolders = new ArrayList<String>(); /** * @throws MojoExecutionException * @throws MojoFailureException */ public void execute() throws MojoExecutionException, MojoFailureException { String out = targetDirectory.getPath(); for (String src : project.getCompileSourceRoots()) { if (!src.startsWith(out)) { sourceFolders.add(src); } } generateIntermediateApk(); File outputFile = createApkLibraryFile(); if (classifier == null) { // Set the generated file as the main artifact (because the pom states <packaging>apklib</packaging>) project.getArtifact().setFile(outputFile); } else { // If there is a classifier specified, attach the artifact using that projectHelper.attachArtifact(project, outputFile, classifier); } if (attachJar) { final File jarFile = new File(targetDirectory, finalName + ".jar"); projectHelper.attachArtifact(project, "jar", project.getArtifact().getClassifier(), jarFile); } } private File createApkLibraryFile() throws MojoExecutionException { final File apklibrary = new File(targetDirectory, finalName + "." + APKLIB); FileUtils.deleteQuietly(apklibrary); try { JarArchiver jarArchiver = new JarArchiver(); jarArchiver.setDestFile(apklibrary); jarArchiver.addFile(destinationManifestFile, "AndroidManifest.xml"); addDirectory(jarArchiver, assetsDirectory, "assets"); addDirectory(jarArchiver, resourceDirectory, "res"); for (String src : sourceFolders) { addDirectory(jarArchiver, new File(src), "src"); } File[] overlayDirectories = getResourceOverlayDirectories(); for (File resOverlayDir : overlayDirectories) { if (resOverlayDir != null && resOverlayDir.exists()) { addDirectory(jarArchiver, resOverlayDir, "res"); } } addJavaResources(jarArchiver, resources, "src"); // Lastly, add any native libraries addNativeLibraries(jarArchiver); jarArchiver.createArchive(); } catch (ArchiverException e) { throw new MojoExecutionException("ArchiverException while creating ." + APKLIB + " file.", e); } catch (IOException e) { throw new MojoExecutionException("IOException while creating ." + APKLIB + " file.", e); } return apklibrary; } private void addNativeLibraries(final JarArchiver jarArchiver) throws MojoExecutionException { try { if (nativeLibrariesDirectory.exists()) { getLog().info(nativeLibrariesDirectory + " exists, adding libraries."); addDirectory(jarArchiver, nativeLibrariesDirectory, NATIVE_LIBRARIES_FOLDER); } else { getLog().info( nativeLibrariesDirectory + " does not exist, looking for libraries in target directory."); // Add native libraries built and attached in this build String[] ndkArchitectures = NativeHelper.getNdkArchitectures(ndkArchitecture, applicationMakefile, project.getBasedir()); for (String architecture : ndkArchitectures) { final File ndkLibsDirectory = new File(ndkOutputDirectory, architecture); addSharedLibraries(jarArchiver, ndkLibsDirectory, architecture); // Add native library dependencies // FIXME: Remove as causes duplicate libraries when building final APK if this set includes // libraries from dependencies of the APKLIB //final File dependentLibs = new File( ndkOutputDirectory.getAbsolutePath(), ndkArchitecture ); //addSharedLibraries( jarArchiver, dependentLibs, prefix ); } } // Removing this as the APK is now able to pull the native libs from the chained apklibs // get native libs from other apklibs // for ( Artifact apkLibraryArtifact : getTransitiveDependencyArtifacts( APKLIB ) ) // { // final File apklibLibsDirectory = getUnpackedLibNativesFolder( apkLibraryArtifact ); // if ( apklibLibsDirectory.exists() ) // { // addDirectory( jarArchiver, apklibLibsDirectory, NATIVE_LIBRARIES_FOLDER ); // } // } } catch (ArchiverException e) { throw new MojoExecutionException("IOException while creating ." + APKLIB + " file.", e); } // TODO: Next is to check for any: // TODO: - compiled in (as part of this build) libs // TODO: - That is of course easy if the artifact is indeed attached // TODO: - If not attached, it gets a little trickier - check the target dir for any compiled .so files (generated by NDK mojo) // TODO: - But where is that directory configured? } protected void addJavaResources(JarArchiver jarArchiver, List<Resource> javaResources, String prefix) throws IOException { for (Resource javaResource : javaResources) { addJavaResource(jarArchiver, javaResource, prefix); } } /** * Adds a Java Resources directory (typically "src/main/resources") to a {@link JarArchiver}. * * @param jarArchiver * @param javaResource The Java resource to add. * @param prefix An optional prefix for where in the Jar file the directory's contents should go. * @throws IOException in case the resource path can not be resolved */ protected void addJavaResource(JarArchiver jarArchiver, Resource javaResource, String prefix) throws IOException { if (javaResource != null) { final File javaResourceDirectory = new File(javaResource.getDirectory()); if (javaResourceDirectory.exists()) { final String resourcePath = javaResourceDirectory.getCanonicalPath(); final String apkLibUnpackBasePath = getUnpackedLibsDirectory().getCanonicalPath(); // Don't include our dependencies' resource dirs. if (!resourcePath.startsWith(apkLibUnpackBasePath)) { final DefaultFileSet javaResourceFileSet = new DefaultFileSet(); javaResourceFileSet.setDirectory(javaResourceDirectory); javaResourceFileSet.setPrefix(endWithSlash(prefix)); jarArchiver.addFileSet(javaResourceFileSet); } } } } /** * Makes sure the string ends with "/" * * @param prefix any string, or null. * @return the prefix with a "/" at the end, never null. */ protected String endWithSlash(String prefix) { prefix = StringUtils.defaultIfEmpty(prefix, "/"); if (!prefix.endsWith("/")) { prefix = prefix + "/"; } return prefix; } /** * Adds a directory to a {@link JarArchiver} with a directory prefix. * * @param jarArchiver * @param directory The directory to add. * @param prefix An optional prefix for where in the Jar file the directory's contents should go. */ protected void addDirectory(JarArchiver jarArchiver, File directory, String prefix) { if (directory != null && directory.exists()) { final DefaultFileSet fileSet = new DefaultFileSet(); fileSet.setPrefix(endWithSlash(prefix)); fileSet.setDirectory(directory); // XXX: trying to avoid duplicated sources fileSet.setExcludes(new String[] { "**/R.java", "**/BuildConfig.java" }); jarArchiver.addFileSet(fileSet); getLog().debug("Added files from " + directory); } } /** * Adds all shared libraries (.so) to a {@link JarArchiver} under 'libs'. * * @param jarArchiver The jarArchiver to add files to * @param directory The directory to scan for .so files * @param architecture The prefix for where in the jar the .so files will go. */ protected void addSharedLibraries(JarArchiver jarArchiver, File directory, String architecture) { getLog().debug("Searching for shared libraries in " + directory); File[] libFiles = directory.listFiles(new FilenameFilter() { public boolean accept(final File dir, final String name) { return name.startsWith("lib") && name.endsWith(".so"); } }); if (libFiles != null) { for (File libFile : libFiles) { String dest = NATIVE_LIBRARIES_FOLDER + "/" + architecture + "/" + libFile.getName(); getLog().debug("Adding " + libFile + " as " + dest); jarArchiver.addFile(libFile, dest); } } } /** * Generates an intermediate apk file (actually .ap_) containing the resources and assets. * * @throws MojoExecutionException */ private void generateIntermediateApk() throws MojoExecutionException { CommandExecutor executor = CommandExecutor.Factory.createDefaultCommmandExecutor(); executor.setLogger(this.getLog()); File[] overlayDirectories = getResourceOverlayDirectories(); File androidJar = getAndroidSdk().getAndroidJar(); File outputFile = new File(targetDirectory, finalName + ".ap_"); List<File> dependencyArtifactResDirectoryList = new ArrayList<File>(); for (Artifact libraryArtifact : getTransitiveDependencyArtifacts(APKLIB, AAR)) { final File apklibResDirectory = getUnpackedLibResourceFolder(libraryArtifact); if (apklibResDirectory.exists()) { dependencyArtifactResDirectoryList.add(apklibResDirectory); } } AaptCommandBuilder commandBuilder = AaptCommandBuilder.packageResources(getLog()) .forceOverwriteExistingFiles().setPathToAndroidManifest(destinationManifestFile) .addResourceDirectoriesIfExists(overlayDirectories).addResourceDirectoryIfExists(resourceDirectory) .addResourceDirectoriesIfExists(dependencyArtifactResDirectoryList).autoAddOverlay() // NB aapt only accepts a single assets parameter - combinedAssets is a merge of all assets .addRawAssetsDirectoryIfExists(combinedAssets).addExistingPackageToBaseIncludeSet(androidJar) .setOutputApkFile(outputFile).addConfigurations(configurations).setVerbose(aaptVerbose); getLog().debug(getAndroidSdk().getAaptPath() + " " + commandBuilder.toString()); getLog().info("Generating apklib"); try { executor.setCaptureStdOut(true); List<String> commands = commandBuilder.build(); executor.executeCommand(getAndroidSdk().getAaptPath(), commands, project.getBasedir(), false); } catch (ExecutionException e) { throw new MojoExecutionException("", e); } } }