/* * Copyright 2015 - 2016 i-net software * * 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.inet.gradle.setup.dmg; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.apache.tools.ant.types.FileSet; import org.gradle.api.GradleException; import org.gradle.api.internal.file.FileResolver; import com.inet.gradle.setup.abstracts.AbstractBuilder; import com.inet.gradle.setup.abstracts.AbstractSetupBuilder; import com.inet.gradle.setup.abstracts.AbstractTask; import com.inet.gradle.setup.abstracts.Application; import com.inet.gradle.setup.abstracts.DocumentType; import com.oracle.appbundler.AppBundlerTask; import com.oracle.appbundler.Architecture; import com.oracle.appbundler.BundleDocument; /** * Abstract implementation for creating the resulting app bundler image * * @author gamma * * @param <T> the Task * @param <S> the SetupBuilder */ public abstract class AbstractOSXApplicationBuilder<T extends AbstractTask, S extends AbstractSetupBuilder> extends AbstractBuilder<T, S> { private S setup; private AppBundlerTask appBundler; /** * Setup this builder. * * @param task - original task * @param setup - original setup * @param fileResolver - original fileResolver */ protected AbstractOSXApplicationBuilder( T task, S setup, FileResolver fileResolver ) { super( task, fileResolver ); this.setup = setup; appBundler = new AppBundlerTask(); } /** * Prepare the basic settings of the application * * @param application to use * @throws Exception on errors */ protected void prepareApplication( Application application ) throws Exception { System.out.println( "BuildDir now: " + buildDir ); appBundler.setOutputDirectory( buildDir ); appBundler.setName( application.getDisplayName() ); appBundler.setDisplayName( application.getDisplayName() ); String version = setup.getVersion(); appBundler.setVersion( version ); int idx = version.indexOf( '.' ); if( idx >= 0 ) { idx = version.indexOf( '.', idx + 1 ); if( idx >= 0 ) { version = version.substring( 0, idx ); } } // Get the working directory and patch the main jar with it. String mainJar = application.getMainJar(); if( application.getWorkDir() != null ) { appBundler.setWorkingDirectory( new File( new File( "$APP_ROOT/Contents/Java" ), application.getWorkDir() ).toString() ); mainJar = new File( new File( application.getWorkDir() ), mainJar ).toString(); } appBundler.setShortVersion( version ); appBundler.setExecutableName( application.getExecutable() ); String identifier = setup.getAppIdentifier(); String appName = application.getDisplayName(); if( appName != setup.getApplication() ) { identifier += "." + appName.replaceAll( "^A-Za-z0-9]", "" ); } appBundler.setIdentifier( identifier ); appBundler.setMainClassName( application.getMainClass() ); appBundler.setJarLauncherName( mainJar ); appBundler.setCopyright( setup.getCopyright() ); appBundler.setIcon( getApplicationIcon() ); Architecture x86_64 = new Architecture(); x86_64.setName( "x86_64" ); appBundler.addConfiguredArch( x86_64 ); } /** * Execute the appbundler, create the final .app file */ protected void finishApplication() { bundleJre(); org.apache.tools.ant.Project antProject = new org.apache.tools.ant.Project(); appBundler.setProject( antProject ); appBundler.execute(); } /** * Set the document types from the list * * @param list of document types * @throws IOException on complications with the icon */ protected void setDocumentTypes( List<DocumentType> list ) throws IOException { // add file extensions for( DocumentType doc : list ) { BundleDocument bundle = new BundleDocument(); bundle.setExtensions( String.join( ",", doc.getFileExtension() ) ); bundle.setName( doc.getName() ); bundle.setRole( doc.getRole() ); // Viewer or Editor bundle.setIcon( getApplicationIcon().toString() ); appBundler.addConfiguredBundleDocument( bundle ); } } /** * Bundle the Java VM if set. */ protected void bundleJre() { Object jre = setup.getBundleJre(); if( jre == null ) { return; } File jreDir; try { jreDir = task.getProject().file( jre ); } catch( Exception e ) { jreDir = null; } if( jreDir == null || !jreDir.isDirectory() ) { ArrayList<String> command = new ArrayList<>(); command.add( "/usr/libexec/java_home" ); command.add( "-v" ); command.add( jre.toString() ); command.add( "-F" ); ByteArrayOutputStream baos = new ByteArrayOutputStream(); exec( command, null, baos ); jreDir = new File( baos.toString().trim() ); if( !jreDir.isDirectory() ) { throw new GradleException( "bundleJre version " + jre + " can not be found in: " + jreDir ); } } task.getProject().getLogger().lifecycle( "\tbundle JRE: " + jreDir ); FileSet fileSet = new FileSet(); fileSet.setDir( jreDir ); fileSet.appendIncludes( new String[] { "jre/*", "jre/lib/", "jre/bin/java" } ); fileSet.appendExcludes( new String[] { "jre/lib/deploy/", "jre/lib/deploy.jar", "jre/lib/javaws.jar", "jre/lib/libdeploy.dylib", "jre/lib/libnpjp2.dylib", "jre/lib/plugin.jar", "jre/lib/security/javaws.policy" } ); appBundler.addConfiguredRuntime( fileSet ); } /** * Returns the icns application icon file * * @return Icon file * @throws IOException when there are errors while getting the file */ protected File getApplicationIcon() throws IOException { File icons = setup.getIconForType( buildDir, "icns" ); if( icons == null ) { throw new IllegalArgumentException( "You have to specify a valid icon file" ); } return icons; } /** * Modify a plist file * * @param plist file to modify * @param property property to set * @param value of property */ protected void setPlistProperty( File plist, String property, String value ) { // Set Property in plist file // /usr/libexec/PlistBuddy -c "Set PreferenceSpecifiers:19:Titles:0 $buildDate" "$BUNDLE/Root.plist" ArrayList<String> command = new ArrayList<>(); command.add( "/usr/libexec/PlistBuddy" ); command.add( "-c" ); command.add( "Set " + property + " " + value ); command.add( plist.getAbsolutePath() ); exec( command ); } /** * Add an entry to a plist file * * @param plist file to modify * @param property property to set * @param type of property * @param value of property */ protected void addPlistProperty( File plist, String property, String type, String value ) { // Set Property in plist file // /usr/libexec/PlistBuddy -c "Add PreferenceSpecifiers:19:Titles:0 $buildDate" "$BUNDLE/Root.plist" ArrayList<String> command = new ArrayList<>(); command.add( "/usr/libexec/PlistBuddy" ); command.add( "-c" ); command.add( "Add " + property + " " + type + " " + value ); command.add( plist.getAbsolutePath() ); exec( command ); } /** * Add an entry to a plist file * * @param plist file to modify * @param property property to set */ protected void deletePlistProperty( File plist, String property ) { // Set Property in plist file // /usr/libexec/PlistBuddy -c "Add PreferenceSpecifiers:19:Titles:0 $buildDate" "$BUNDLE/Root.plist" ArrayList<String> command = new ArrayList<>(); command.add( "/usr/libexec/PlistBuddy" ); command.add( "-c" ); command.add( "Delete " + property ); command.add( plist.getAbsolutePath() ); exec( command, null, null, true ); } /** * Copy the files defined in the gradle script into their final destination * * @param application the application * @throws IOException on errors */ protected void copyBundleFiles( Application application ) throws IOException { File destination = new File( buildDir, application.getDisplayName() + ".app" ); getTask().copyTo( new File( destination, "Contents/Java" ) ); setApplicationFilePermissions( destination ); } /** * Set File permissions to the resulting application * * @param destination of the files to manipulate * @throws IOException on errors */ protected void setApplicationFilePermissions( File destination ) throws IOException { // Set Read on all files and folders ArrayList<String> command = new ArrayList<>(); command.add( "chmod" ); command.add( "-R" ); command.add( "a+r" ); command.add( destination.getAbsolutePath() ); exec( command ); // Set execute on all folders. command = new ArrayList<>(); command.add( "find" ); command.add( destination.getAbsolutePath() ); if( destination.isDirectory() ) { command.add( "-type" ); command.add( "d" ); } command.add( "-exec" ); command.add( "chmod" ); command.add( "a+x" ); command.add( "{}" ); command.add( ";" ); exec( command ); } /** * Return the current AppBundler. * * @return appBundler */ protected AppBundlerTask getAppBundler() { return appBundler; } protected S getSetupBuilder() { return setup; } }