package com.inet.gradle.setup.dmg;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import org.gradle.api.GradleException;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.dsl.DependencyHandler;
import org.gradle.api.internal.file.FileResolver;
import com.inet.gradle.setup.SetupBuilder;
import com.inet.gradle.setup.abstracts.Service;
import com.inet.gradle.setup.util.ReplacingInputStream;
import com.inet.gradle.setup.util.ResourceUtils;
public class OSXPrefPaneCreator extends AbstractOSXApplicationBuilder<Dmg, SetupBuilder> {
private File buildDir;
private Project project;
protected OSXPrefPaneCreator( Dmg task, SetupBuilder setup, FileResolver fileResolver ) {
super( task, setup, fileResolver );
buildDir = task.getTemporaryDir();
project = task.getProject();
}
/**
* Create a single Lauch4j launcher.
*
* @param launch the launch description
* @param task the task
* @param setup the SetupBuilder
* @return the file to the created exe.
* @throws Exception if any error occur
*/
void create( Service service ) throws Exception {
String displayName = service.getDisplayName();
String internalName = displayName.replaceAll( "[^A-Za-z0-9]", "" );
File prefPaneSource = unpackAndPatchPrefPaneSource( internalName );
ArrayList<String> command = new ArrayList<String>();
command.add( "gradle" );
command.add( "-b" );
command.add( new File( prefPaneSource, "build.gradle" ).getAbsolutePath() );
command.add( "xcodebuild" );
exec( command );
File prefPaneBinary = new File( prefPaneSource, "build/sym/Release/" + internalName + ".prefPane" );
if( !prefPaneBinary.exists() ) {
throw new GradleException( "Launch4j failed. " );
}
// Where will the Result be put
File resourcesOutput = new File( buildDir, displayName + ".app/Contents/Resources" );
// Rename to app-name to the final prefpane name in the service
File prefPaneLocation = new File( resourcesOutput, displayName + ".prefPane" );
// rename helper Tool in binary of the pref pane
File prefPaneContents = new File( prefPaneBinary, "Contents" );
Path iconPath = getApplicationIcon().toPath();
Files.copy( iconPath, new File( prefPaneContents, "Resources/" + internalName + ".app/Contents/Resources/applet.icns" ).toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING );
Files.move( new File( prefPaneContents, "MacOS/" + internalName ).toPath(), new File( prefPaneContents, "MacOS/" + displayName ).toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING );
Files.move( new File( prefPaneContents, "Resources/" + internalName + ".app" ).toPath(), new File( prefPaneContents, "Resources/" + displayName + ".app" ).toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING );
System.out.println( "Unpacked the Preference Pane to: " + prefPaneContents.getAbsolutePath() );
// Make executable
setApplicationFilePermissions( new File( prefPaneContents, "Resources/" + displayName + ".app/Contents/MacOS/applet" ) );
// Rename prefPane
Files.move( prefPaneBinary.toPath(), prefPaneLocation.toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING );
// Copy Icon
Files.copy( iconPath, new File( prefPaneLocation, "Contents/Resources/ProductIcon.icns" ).toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING );
// Patch Info.plist
File prefPanePLIST = new File( prefPaneLocation, "Contents/Info.plist" );
setPlistProperty( prefPanePLIST, ":CFBundleIdentifier", (getSetupBuilder().getMainClass() != null ? getSetupBuilder().getMainClass() : getSetupBuilder().getAppIdentifier()) + ".prefPane" );
setPlistProperty( prefPanePLIST, ":CFBundleName", displayName + " Preference Pane" );
setPlistProperty( prefPanePLIST, ":CFBundleExecutable", displayName );
setPlistProperty( prefPanePLIST, ":NSPrefPaneIconLabel", displayName );
setPlistProperty( prefPanePLIST, ":CFBundleExecutable", displayName );
File servicePLIST = new File( prefPaneLocation, "Contents/Resources/service.plist" );
setPlistProperty( servicePLIST, ":Name", displayName );
setPlistProperty( servicePLIST, ":Label", service.getMainClass() != null ? service.getMainClass() : getSetupBuilder().getAppIdentifier() );
// Program will be set during installation.
setPlistProperty( servicePLIST, ":Description", service.getDescription() );
setPlistProperty( servicePLIST, ":Version", getSetupBuilder().getVersion() );
setPlistProperty( servicePLIST, ":KeepAlive", String.valueOf( service.isKeepAlive() ) );
setPlistProperty( servicePLIST, ":RunAtBoot", String.valueOf( service.isStartOnBoot() ) );
setPlistProperty( servicePLIST, ":RunAtLoad", "true" );
if( task.getDaemonUser() != "root" ) {
// Root by default, will only set if not.
addPlistProperty( servicePLIST, ":UserName", "String", task.getDaemonUser() );
addPlistProperty( servicePLIST, ":GroupName", "String", task.getDaemonUser() );
}
// Reset the plist.
deletePlistProperty( servicePLIST, ":starter" );
// Output the preference link actions to the plist
for( int i = 0; i < task.getPreferencesLinks().size(); i++ ) {
if( i == 0 ) {
addPlistProperty( servicePLIST, ":starter", "array", null );
}
PreferencesLink preferencesLink = task.getPreferencesLinks().get( i );
addPlistProperty( servicePLIST, ":starter:", "dict", null );
addPlistProperty( servicePLIST, ":starter:" + i + ":title", "string", preferencesLink.getTitle() );
addPlistProperty( servicePLIST, ":starter:" + i + ":action", "string", preferencesLink.getAction() );
addPlistProperty( servicePLIST, ":starter:" + i + ":asroot", "bool", preferencesLink.isRunAsRoot() ? "YES" : "NO" );
}
ResourceUtils.deleteDirectory( prefPaneSource.toPath() );
}
/**
* Download and unpack the preferences pane setup files
*
* @param internalName to unpack it now
* @return file to prefpane sources
* @throws IOException if an error occurs
*/
private File unpackAndPatchPrefPaneSource( String internalName ) throws IOException {
// Create Config and load Dependencies.
String configName = "prefPaneSource";
Configuration config = project.getConfigurations().findByName( configName );
if( config == null ) {
config = project.getConfigurations().create( configName );
config.setVisible( false );
config.setTransitive( false );
DependencyHandler dependencies = project.getDependencies();
dependencies.add( configName, "de.inetsoftware:SetupBuilderOSXPrefPane:+:sources" );
}
// Prepare output directory
File outputDir = new File( buildDir, configName );
outputDir.mkdirs();
// Unzip the content
for( File file : config.getFiles() ) {
ResourceUtils.unZipIt( file, outputDir, ( entryName ) -> {
return entryName.replaceAll( "SetupBuilderOSXPrefPane", internalName );
}, ( inputStream ) -> {
return new ReplacingInputStream( inputStream, new HashMap<byte[], byte[]>() {
{
put( "SetupBuilderOSXPrefPane".getBytes(), internalName.getBytes() );
}
} );
} );
}
return outputDir;
}
}