Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package com.codename1.maven;
import com.codename1.ant.AntExecutor;
import com.codename1.ant.SortedProperties;
import com.codename1.builders.*;
import org.apache.commons.io.FileUtils;
import org.apache.commons.vfs2.FileObject;
import org.apache.commons.vfs2.FileSystemManager;
import org.apache.commons.vfs2.PatternFileSelector;
import org.apache.commons.vfs2.VFS;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.*;
import org.apache.tools.ant.taskdefs.Expand;
import org.apache.tools.ant.taskdefs.Zip;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.ZipFileSet;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.*;
import static com.codename1.maven.PathUtil.path;
/**
* Mojo that uses the CodenameOneBuildClient to send builds to the CodenameOne build server.
*
* It also supports a few local build targets, such as "ios-source", which generates an Xcode project,
* and "android-source", which generates an Android gradle project.
*/
@Mojo(name="build", requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME,
requiresDependencyCollection = ResolutionScope.COMPILE_PLUS_RUNTIME)
@Execute(phase = LifecyclePhase.PACKAGE)
public class CN1BuildMojo extends AbstractCN1Mojo {
public static final String BUILD_TARGET_XCODE_PROJECT = Executor.BUILD_TARGET_XCODE_PROJECT;
public static final String BUILD_TARGET_ANDROID_PROJECT = Executor.BUILD_TARGET_ANDROID_PROJECT;
private String serverMustProvideKotlinVersion;
/**
* The target platform. E.g. javase, javascript, ios, android, win
*/
@Parameter(property = "codename1.platform", required = true)
private String platform;
/**
* The build target, corresponding to ANT build targets in build-template.xml. E.g. javascript,
* mac-os-x-desktop, windows-desktop, windows-device, ios-device, ios-device-release, android-device, war
*/
@Parameter(property = "codename1.buildTarget", required = true, defaultValue = "${codename1.defaultBuildTarget}")
private String buildTarget;
/**
* Flag to indicate whether to use an automated build or not.
*/
@Parameter(property = "automated", defaultValue = "false")
private boolean automated;
/**
* Flag of whether to open the xcode/android studio project.
*/
@Parameter(property = "open", defaultValue = "true")
private boolean open;
private String cn1MavenPluginVersion = "";
private String cn1MavenVersion = "";
@Override
protected void executeImpl() throws MojoExecutionException, MojoFailureException {
if ("none".equalsIgnoreCase(buildTarget)) {
getLog().info("BuildTarget is None. Skipping cn1build goal");
return;
}
cn1MavenPluginVersion = project.getProperties().getProperty("cn1.plugin.version", "");
cn1MavenVersion = project.getProperties().getProperty("cn1.version", "");
File retrolambdaJar = getJar("net.orfjackal.retrolambda", "retrolambda");
if (retrolambdaJar != null && retrolambdaJar.exists()) {
System.setProperty("retrolambdaJarPath", retrolambdaJar.getAbsolutePath());
} else {
getLog().warn("Could not find retrolambda Jar from dependencies. Falling back to default version 2.5.1 that may have issues building Android.");
}
String projectPlatform = project.getProperties().getProperty("codename1.projectPlatform");
if (projectPlatform == null) {
getLog().debug("Skipping build because codename1.projectPlatform property is not defined");
return;
}
if (!projectPlatform.equals(platform)) {
getLog().debug("Skipping build because codename1.projectPlatform doesn't match the given platform");
return;
}
if (platform.contains("android")) {
if (!BUILD_TARGET_ANDROID_PROJECT.equals(buildTarget)) {
String apkName = project.getBuild().getFinalName() + ".apk";
File apkFile = new File(project.getBuild().getDirectory() + File.separator + apkName);
try {
if (apkFile.exists() && apkFile.lastModified() >= getSourcesModificationTime()) {
getLog().info("Sources have not been modified since APK at " + apkFile + " was created. Skipping Android build");
return;
}
} catch (IOException ex) {
throw new MojoExecutionException("Failed to check sources modification time", ex);
}
}
}
try {
createAntProject();
} catch (IOException ex) {
getLog().error("Failed to create and build ANT project", ex);
throw new MojoFailureException("Failed to create and build ANT project", ex);
} catch (LibraryPropertiesException ex) {
getLog().error("Failed to merge properties from library "+ex.libName+". " + ex.getMessage());
throw new MojoExecutionException("Failed to merge properties from library "+ex.libName+". " + ex.getMessage(), ex);
}
}
/**
* Merge a set of jars into a single jar file.
* @param dest The destination jar file. Also the first source if it already exists.
* @param src The source jar files to be merged into the destination.
*/
private void mergeJars(File dest, File... src) {
Zip task = (Zip)antProject.createTask("zip");
task.setDestFile(dest);
task.setUpdate(true);
for (File srcFile : src) {
if (srcFile.isDirectory()) {
FileSet fs = new FileSet();
// Multiversioned jars trip out the ASM class parsing.
// Specifically module-info.class files
fs.setExcludes("**/META-INF/versions/**");
fs.setProject(this.antProject);
fs.setDir(srcFile);
task.addFileset(fs);
} else {
ZipFileSet fileset = new ZipFileSet();
// Multiversioned jars trip out the ASM class parsing.
// Specifically module-info.class files
fileset.setExcludes("**/META-INF/versions/**");
fileset.setProject(antProject);
fileset.setSrc(srcFile);
task.addZipfileset(fileset);
}
}
task.execute();
}
/**
* The dependency scopes to include in the jar file that is sent to the build server.
*/
private static String[] BUNDLE_ARTIFACT_SCOPES = new String[] { "compile" };
/**
* Artifact IDs that should not be sent to the build server.
*/
private static String[] BUNDLE_ARTIFACT_ID_BLACKLIST = new String[] {"codenameone-core", "java-runtime"};
/**
* Gets the app extensions jar file that should be included in any iOS builds.
* @return The app extensions jar file if it exists. null otherwise.
* @throws IOException
*/
private File getAppExtensionsJar() throws IOException {
if (!project.getProperties().getProperty("codename1.platform", "").equalsIgnoreCase("ios")){
// App extensions are only for iOS
return null;
}
if (!project.getBasedir().getName().equalsIgnoreCase("ios")) {
// Make sure we're building the ios project
return null;
}
File appExtensionsDir = new File(project.getBasedir(), "app_extensions");
if (!appExtensionsDir.isDirectory()) return null;
File appExtensionsJar = new File(project.getBuild().getDirectory() + File.separator + "codenameone" + File.separator + "app_extensions.jar");
if (appExtensionsJar.exists() && appExtensionsJar.lastModified() < lastModifiedRecursive(appExtensionsDir)) {
// The app extensions jar is out of date.
appExtensionsJar.delete();
}
if (!appExtensionsJar.exists()) {
File tmpDir = new File(appExtensionsJar.getParentFile(), "app_extensions");
if (tmpDir.exists()) {
FileUtils.deleteDirectory(tmpDir);
}
tmpDir.mkdirs();
for (File appExtension : appExtensionsDir.listFiles()) {
Zip task = (Zip)antProject.createTask("zip");
File dest = new File(tmpDir, appExtension.getName()+".ios.appext");
task.setDestFile(dest);
task.setUpdate(false);
if (appExtension.isDirectory()) {
FileSet fs = new FileSet();
fs.setProject(this.antProject);
fs.setDir(appExtension);
task.addFileset(fs);
task.execute();
} else if (appExtension.getName().endsWith(".zip")) {
ZipFileSet fileset = new ZipFileSet();
fileset.setProject(antProject);
fileset.setSrc(appExtension);
task.addZipfileset(fileset);
task.execute();
}
}
Zip task = (Zip)antProject.createTask("zip");
task.setDestFile(appExtensionsJar);
task.setUpdate(false);
FileSet fs = new FileSet();
fs.setProject(this.antProject);
fs.setDir(tmpDir);
task.addFileset(fs);
task.execute();
}
if (appExtensionsJar.exists()) {
return appExtensionsJar;
}
return null;
}
/**
* Gets the localizations jar file that should be included in any iOS builds.
* @return The localizations jar file if it exists. null otherwise.
* @throws IOException
*/
private File getStringsJar() throws IOException {
if (!project.getProperties().getProperty("codename1.platform", "").equalsIgnoreCase("ios")){
// App extensions are only for iOS
return null;
}
if (!project.getBasedir().getName().equalsIgnoreCase("ios")) {
// Make sure we're building the ios project
return null;
}
File stringsDir = new File(project.getBasedir(), path("src", "main", "strings"));
if (!stringsDir.isDirectory()) return null;
File stringsJar = new File(project.getBuild().getDirectory() + File.separator + "codenameone" + File.separator + "strings.jar");
if (stringsJar.exists() && stringsJar.lastModified() < lastModifiedRecursive(stringsDir)) {
// The app extensions jar is out of date.
stringsJar.delete();
}
if (!stringsJar.exists()) {
File tmpDir = new File(stringsJar.getParentFile(), "strings");
if (tmpDir.exists()) {
FileUtils.deleteDirectory(tmpDir);
}
tmpDir.mkdirs();
for (File lproj : stringsDir.listFiles()) {
Zip task = (Zip)antProject.createTask("zip");
File dest = new File(tmpDir, lproj.getName()+".zip");
task.setDestFile(dest);
task.setUpdate(false);
if (lproj.isDirectory()) {
FileSet fs = new FileSet();
fs.setProject(this.antProject);
fs.setDir(lproj);
task.addFileset(fs);
task.execute();
} else if (lproj.getName().endsWith(".zip")) {
ZipFileSet fileset = new ZipFileSet();
fileset.setProject(antProject);
fileset.setSrc(lproj);
task.addZipfileset(fileset);
task.execute();
}
}
Zip task = (Zip)antProject.createTask("zip");
task.setDestFile(stringsJar);
task.setUpdate(false);
FileSet fs = new FileSet();
fs.setProject(this.antProject);
fs.setDir(tmpDir);
task.addFileset(fs);
task.execute();
}
if (stringsJar.exists()) {
return stringsJar;
}
return null;
}
private boolean isLocalBuildTarget(String buildTarget) {
return (buildTarget.startsWith("local-") || BUILD_TARGET_XCODE_PROJECT.equals(buildTarget) || BUILD_TARGET_ANDROID_PROJECT.equals(buildTarget));
}
private void createAntProject() throws IOException, LibraryPropertiesException, MojoExecutionException {
File cn1dir = new File(project.getBuild().getDirectory() + File.separator + "codenameone");
File antProject = new File(cn1dir, "antProject");
antProject.mkdirs();
File codenameOneSettings = new File(getCN1ProjectDir(), "codenameone_settings.properties");
File icon = new File(getCN1ProjectDir(), "icon.png");
if (icon.exists()) {
FileUtils.copyFile(icon, new File(antProject, "icon.png"));
} else {
FileUtils.copyInputStreamToFile(getClass().getResourceAsStream("codenameone-icon.png"), new File(antProject, "icon.png"));
}
File codenameOneSettingsCopy = new File(antProject, codenameOneSettings.getName());
FileUtils.copyFile(codenameOneSettings, codenameOneSettingsCopy);
FileUtils.copyInputStreamToFile(getClass().getResourceAsStream("buildxml-template.xml"), new File(antProject, "build.xml"));
File distDir = new File(antProject, "dist");
distDir.mkdirs();
// Build a jar with all dependencies that we will send to the build server.
File jarWithDependencies = new File(path(project.getBuild().getDirectory(), project.getBuild().getFinalName() + "-"+buildTarget+"-jar-with-dependencies.jar"));
List cpElements;
try {
//getLog().info("Classpath Elements: "+ project.getCompileClasspathElements());
cpElements = project.getCompileClasspathElements();
} catch (Exception ex) {
throw new MojoExecutionException("Failed to get classpath elements", ex);
}
File appExtensionsJar = getAppExtensionsJar();
if (appExtensionsJar != null) {
cpElements.add(appExtensionsJar.getAbsolutePath());
}
File stringsJar = getStringsJar();
if (stringsJar != null) {
cpElements.add(stringsJar.getAbsolutePath());
}
getLog().debug("Classpath Elements: "+cpElements);
if (jarWithDependencies.exists()) {
getLog().debug("Found jar file with dependencies at "+jarWithDependencies+". Will use that one unless it is out of date.");
// Evidently pom.xml file has already built the jar file - we will use that one. This allows
// developers to override what is included in the jar file that is sent to the server.
for (String artifact : cpElements) {
File jar = new File(artifact);
if (jar.isDirectory()) {
if (jarWithDependencies.lastModified() < lastModifiedRecursive(jar)) {
getLog().debug("Jar file out of date. Dependencies have changed. "+jarWithDependencies+". Deleting");
jarWithDependencies.delete();
break;
}
} else if (jar.exists() && jar.lastModified() > jarWithDependencies.lastModified()) {
// One of the dependency jar files is newer... so we delete the dependencies jar file
// and will generate a new one.
getLog().debug("Jar file out of date. Dependencies have changed. "+jarWithDependencies+". Deleting");
jarWithDependencies.delete();
break;
}
}
}
if (!jarWithDependencies.exists()) {
getLog().info(jarWithDependencies + " not found. Generating jar with dependencies now");
// Jars that should be stripped out and not sent to the server
List blackListJars = new ArrayList();
getLog().info("Project artifacts: "+project.getArtifacts());
for (Artifact artifact : project.getArtifacts()) {
boolean addToBlacklist = false;
if (artifact.getGroupId().equals("com.codenameone") && contains(artifact.getArtifactId(), BUNDLE_ARTIFACT_ID_BLACKLIST)) {
addToBlacklist = true;
}
if (!addToBlacklist && !isLocalBuildTarget(buildTarget)) {
// When sending to the build server, we'll strip the kotlin-stdlib and the server will provide it
// for local builds, it's easier to just include it.
if (artifact.getGroupId().equals("org.jetbrains.kotlin") && artifact.getArtifactId().equals("kotlin-stdlib")) {
addToBlacklist = true;
serverMustProvideKotlinVersion = artifact.getVersion();
getLog().debug("Adding kotlin-stdlib to blacklist. Server will provide this:" + artifact);
}
}
if (!addToBlacklist && !"compile".equals(artifact.getScope())) {
addToBlacklist = true;
}
if (addToBlacklist) {
File jar = getJar(artifact);
if (jar != null) {
blackListJars.add(jar.getAbsolutePath());
blackListJars.add(jar.getPath());
try {
blackListJars.add(jar.getCanonicalPath());
getLog().debug("Added "+jar+" to blacklist");
} catch (Exception ex){}
}
}
}
getLog().debug("Merging compile classpath elements into jar with dependencies: "+cpElements);
List jarsToMerge = new ArrayList();
for (String element : cpElements) {
String canonicalEl = element;
try {
canonicalEl = new File(canonicalEl).getCanonicalPath();
} catch (Exception ex){}
if (blackListJars.contains(element) || blackListJars.contains(canonicalEl)) {
getLog().debug("NOT adding jar "+element+" because it is on the blacklist");
continue;
}
if (!new File(element).exists()) {
continue;
}
getLog().debug("Adding jar " + element + " to " + jarWithDependencies + " Jar file="+element);
jarsToMerge.add(new File(element));
}
mergeJars(jarWithDependencies, jarsToMerge.toArray(new File[jarsToMerge.size()]));
}
try {
updateCodenameOne(false);
} catch (MojoExecutionException ex) {
getLog().error("Failed to update Codename One");
throw new IOException("Failed to update Codename One", ex);
}
File antDistDir = new File(antProject, "dist");
File antDistJar = new File(antDistDir, project.getBuild().getFinalName() + "-"+buildTarget+"-jar-with-dependencies.jar");
antDistDir.mkdirs();
FileUtils.copyFile(jarWithDependencies, antDistJar);
Properties p = new Properties();
p.setProperty("codenameone_settings.properties", codenameOneSettingsCopy.getAbsolutePath());
p.setProperty("CodeNameOneBuildClient.jar", path(System.getProperty("user.home"), ".codenameone", "CodeNameOneBuildClient.jar"));
p.setProperty("dist.jar", antDistJar.getAbsolutePath());
if (automated) {
p.setProperty("automated", "true");
}
getLog().info("Running ANT build target " + buildTarget);
String logPasskey = UUID.randomUUID().toString();
Properties cn1SettingsProps = new Properties();
try (FileInputStream fis = new FileInputStream(codenameOneSettingsCopy)) {
cn1SettingsProps.load(fis);
}
if (serverMustProvideKotlinVersion != null) {
cn1SettingsProps.setProperty("codename1.arg.requireKotlinStdlib", serverMustProvideKotlinVersion);
}
FileSystemManager fsManager = VFS.getManager();
FileObject jarFile = fsManager.resolveFile( "jar:"+jarWithDependencies.getAbsolutePath() + "!/META-INF/codenameone" );
if (jarFile != null) {
FileObject[] appendedPropsFiles = jarFile.findFiles(new PatternFileSelector(".*\\/codenameone_library_appended.properties"));
if (appendedPropsFiles != null) {
for (FileObject appendedPropsFile : appendedPropsFiles) {
SortedProperties appendedProps = new SortedProperties();
try (InputStream appendedPropsIn = appendedPropsFile.getContent().getInputStream()) {
appendedProps.load(appendedPropsIn);
}
for (String propName : appendedProps.stringPropertyNames()) {
String propVal = appendedProps.getProperty(propName);
if (!cn1SettingsProps.containsKey(propName)) {
cn1SettingsProps.put(propName, propVal);
} else {
String existing = cn1SettingsProps.getProperty(propName);
if (!existing.contains(propVal)) {
cn1SettingsProps.setProperty(propName, existing + propVal);
}
}
}
}
}
FileObject[] requiredPropsFiles = jarFile.findFiles(new PatternFileSelector(".*\\/codenameone_library_required.properties"));
if (requiredPropsFiles != null) {
for (FileObject requiredPropsFile : requiredPropsFiles) {
SortedProperties requiredProps = new SortedProperties();
try (InputStream appendedPropsIn = requiredPropsFile.getContent().getInputStream()) {
requiredProps.load(appendedPropsIn);
}
String artifactId = requiredPropsFile.getParent().getName().getBaseName();
String groupId = requiredPropsFile.getParent().getParent().getName().getBaseName();
String libraryName = groupId + ":" + artifactId;
cn1SettingsProps = mergeRequiredProperties(libraryName, requiredProps, cn1SettingsProps);
}
}
}
cn1SettingsProps.setProperty("codename1.arg.hyp.beamId", logPasskey);
cn1SettingsProps.setProperty("codename1.arg.maven.codenameone-core.version", cn1MavenVersion);
cn1SettingsProps.setProperty("codename1.arg.maven.codenameone-maven-plugin", cn1MavenPluginVersion);
try (FileOutputStream fos = new FileOutputStream(codenameOneSettingsCopy)) {
cn1SettingsProps.store(fos,"");
}
final Process[] proc = new Process[1];
final boolean[] closingHypLog = new boolean[1];
Thread hyperBeamThread = new Thread(()->{
ProcessBuilder pb = new ProcessBuilder("hyp", "beam", logPasskey);
pb.redirectErrorStream(true);
try {
proc[0] = pb.start();
InputStream out = proc[0].getInputStream();
byte[] buffer = new byte[4000];
while (isAlive(proc[0])) {
int no = out.available();
if (no > 0) {
int n = out.read(buffer, 0, Math.min(no, buffer.length));
getLog().info(new String(buffer, 0, n));
}
try {
Thread.sleep(10);
}
catch (InterruptedException e) {
}
}
} catch (Exception ex) {
if (!closingHypLog[0]) {
getLog().warn("Failed to start hyperlog. The build log will not stream to your console. If the build fails, you can download the error log at https://cloud.codenameone.com/secure/index.html");
getLog().debug(ex);
}
}
});
try {
if (isLocalBuildTarget(buildTarget)) {
automated = false;
if (buildTarget.contains("android") || BUILD_TARGET_ANDROID_PROJECT.equals(buildTarget)) {
doAndroidLocalBuild(antProject, cn1SettingsProps, antDistJar);
} else if (buildTarget.contains("ios") || BUILD_TARGET_XCODE_PROJECT.equals(buildTarget)) {
doIOSLocalBuild(antProject, cn1SettingsProps, antDistJar);
} else {
throw new MojoExecutionException("Build target not supported "+buildTarget);
}
} else {
if (automated) {
getLog().debug("Attempting to start hyper beam stream the build log to the console");
hyperBeamThread.start();
}
AntExecutor.executeAntTask(new File(antProject, "build.xml").getAbsolutePath(), buildTarget, p);
}
} finally {
if (automated) {
try {
closingHypLog[0] = true;
proc[0].destroyForcibly();
} catch (Exception ex) {
}
}
}
if (automated) {
getLog().info("Extracting server result");
File result = new File(antDistDir, "result.zip");
if (!result.exists()) {
throw new IOException("Failed to find result.zip after automated build");
}
Expand unzip = (Expand)this.antProject.createTask("unzip");
unzip.setSrc(result);
File resultDir = new File(antDistDir, "result");
resultDir.mkdir();
unzip.setDest(resultDir);
unzip.execute();
for (File child : resultDir.listFiles()) {
String name = child.getName();
int dotpos = name.lastIndexOf(".");
if (dotpos < 0) {
continue;
}
String extension = name.substring(dotpos);
String base = name.substring(0, dotpos);
File copyTo = new File(project.getBuild().getDirectory() + File.separator + project.getBuild().getFinalName() + extension);
FileUtils.copyFile(child, copyTo);
if (".war".equals(extension)) {
projectHelper.attachArtifact(project, "war", copyTo);
} else if (".zip".equals(extension) && "javascript".equals(buildTarget)) {
projectHelper.attachArtifact(project, "zip", "webapp", copyTo);
} else if (".dmg".equals(extension) && "mac-os-x-desktop".equals(buildTarget)) {
projectHelper.attachArtifact(project, "dmg", "mac-app", copyTo);
} else if (".pkg".equals(extension) && "mac-os-x-desktop".equals(buildTarget)) {
projectHelper.attachArtifact(project, "pkg", "mac-app-installer", copyTo);
}
}
FileUtils.deleteDirectory(resultDir);
result.delete();
afterBuild();
}
}
private static boolean isAlive(Process proc) {
try {
proc.exitValue();
return false;
}
catch (IllegalThreadStateException e) {
return true;
}
}
private String generateCertificate(String password, String alias, String fullName, String orgName, String company, String city, String state, String twoLetterCountryCode, boolean sha512) throws Exception {
File keyTool = new File(System.getProperty("java.home") + File.separator + "bin" + File.separator + "keytool");
if (!keyTool.exists()) {
keyTool = new File(System.getProperty("java.home") + File.separator + "bin" + File.separator + "keytool.exe");
}
File keyfileLocation = new File(System.getProperty("user.home") + File.separator + "Keychain.ks");
int counter = 1;
while (keyfileLocation.exists()) {
keyfileLocation = new File(System.getProperty("user.home") + File.separator + "Keychain_" + counter + ".ks");
counter++;
}
ProcessBuilder pb = new ProcessBuilder(keyTool.getAbsolutePath(),
"-genkey", "-keystore", keyfileLocation.getAbsolutePath(), "-storetype", "jks", "-alias", alias,
"-keyalg", "RSA", "-keysize", "2048", "-validity", "15000", "-dname", "CN=" + fullName.replace(",", "\\,")
+ ", OU=" + orgName.replace(",", "\\,")
+ ", O=" + company.replace(",", "\\,")
+ ", L=" + city.replace(",", "\\,")
+ ", S=" + state.replace(",", "\\,")
+ ", C=" + twoLetterCountryCode, "-storepass", password, "-keypass", password, "-v");
if(sha512) {
pb.command().add("-sigalg");
pb.command().add("SHA512withRSA");
}
Process p = pb.start();
int res = p.waitFor();
//error occured
if(res > 0){
StringBuilder msg = new StringBuilder();
final InputStream input = p.getInputStream();
final InputStream stream = p.getErrorStream();
byte[] buffer = new byte[8192];
int i = input.read(buffer);
while (i > -1) {
String str = new String(buffer, 0, i);
System.out.print(str);
msg.append(str);
i = stream.read(buffer);
}
i = stream.read(buffer);
while (i > -1) {
String str = new String(buffer, 0, i);
System.out.print(str);
msg.append(str);
i = stream.read(buffer);
}
return null;
}
return keyfileLocation.getAbsolutePath();
}
private File getGeneratedAndroidProjectSourceDirectory() {
return new File(project.getBuild().getDirectory(), project.getBuild().getFinalName() + "-android-source");
}
private File getGeneratedIOSProjectSourceDirectory() {
return new File(project.getBuild().getDirectory(), project.getBuild().getFinalName() + "-ios-source");
}
private void doAndroidLocalBuild(File tmpProjectDir, Properties props, File distJar) throws MojoExecutionException {
if (BUILD_TARGET_ANDROID_PROJECT.equals(buildTarget)) {
File generatedProject = getGeneratedAndroidProjectSourceDirectory();
getLog().info("Generating android gradle Project to "+generatedProject+"...");
try {
if (generatedProject.exists()) {
getLog().info("Android gradle project already exists. Checking to see if it needs updating...");
if (getSourcesModificationTime() <= lastModifiedRecursive(generatedProject)) {
getLog().info("Sources have not changed. Skipping android gradle project generation");
if (open) {
openAndroidStudioProject(generatedProject);
}
return;
}
}
} catch (IOException ex) {
throw new MojoExecutionException("Failed to find last modification time of "+generatedProject);
}
}
File codenameOneJar = getJar("com.codenameone", "codenameone-core");
AndroidGradleBuilder e = new AndroidGradleBuilder();
e.setBuildTarget(buildTarget);
e.setLogger(getLog());
File buildDirectory = new File(tmpProjectDir, "dist" + File.separator + "android-build");
e.setBuildDirectory(buildDirectory);
e.setCodenameOneJar(codenameOneJar);
e.setPlatform("android");
BuildRequest r = new BuildRequest();
r.setDisplayName(props.getProperty("codename1.displayName"));
r.setPackageName(props.getProperty("codename1.packageName"));
r.setMainClass(props.getProperty("codename1.mainName"));
r.setVersion(props.getProperty("codename1.version"));
String iconPath = props.getProperty("codename1.icon");
File iconFile = new File(iconPath);
if (!iconFile.isAbsolute()) {
iconFile = new File(getCN1ProjectDir(), iconPath);
}
try {
BufferedImage bi = ImageIO.read(iconFile);
if(bi.getWidth() != 512 || bi.getHeight() != 512) {
throw new MojoExecutionException("The icon must be a 512x512 pixel PNG image. It will be scaled to the proper sizes for devices");
}
r.setIcon(iconFile.getAbsolutePath());
} catch (IOException ex) {
throw new MojoExecutionException("Error reading the icon: the icon must be a 512x512 pixel PNG image. It will be scaled to the proper sizes for devices");
}
r.setVendor(props.getProperty("codename1.vendor"));
r.setSubTitle(props.getProperty("codename1.secondaryTitle"));
r.setType("android");
r.setKeystoreAlias(props.getProperty("codename1.android.keystoreAlias"));
String keystorePath = props.getProperty("codename1.android.keystore");
if (keystorePath != null) {
File keystoreFile = new File(keystorePath);
if (!keystoreFile.isAbsolute()) {
keystoreFile = new File(getCN1ProjectDir(), keystorePath);
}
if (keystoreFile.exists() && keystoreFile.isFile()) {
try {
r.setCertificate(keystoreFile.getAbsolutePath());
} catch (IOException ex) {
throw new MojoExecutionException("Failed to load keystore file. ", ex);
}
} else {
File androidCerts = new File(getCN1ProjectDir(), "androidCerts");
androidCerts.mkdirs();
keystoreFile = new File(androidCerts, "KeyChain.ks");
if (!keystoreFile.exists()) {
try {
String alias = r.getKeystoreAlias();
if (alias == null || alias.isEmpty()) {
alias = "androidKey";
r.setKeystoreAlias(alias);
props.setProperty("codename1.android.keystoreAlias", alias);
}
String password = props.getProperty("codename1.android.keystorePassword");
if (password == null || password.isEmpty()) {
password = "password";
props.setProperty("codename1.android.keystorePassword", password);
}
getLog().info("No Keystore found. Generating one now");
String keyPath = generateCertificate(password, alias, r.getVendor(), "", r.getVendor(), "Vancouver", "BC", "CA", false);
FileUtils.copyFile(new File(keyPath), keystoreFile);
r.setCertificate(keystoreFile.getAbsolutePath());
getLog().info("Generated keystore with password 'password' at "+keystoreFile+". alias=androidKey");
new File(keyPath).delete();
SortedProperties sp = new SortedProperties();
try (FileInputStream fis = new FileInputStream(new File(getCN1ProjectDir(), "codenameone_settings.properties"))) {
sp.load(fis);
}
sp.setProperty("codename1.android.keystore", keystoreFile.getAbsolutePath());
sp.setProperty("codename1.android.keystorePassword", password);
sp.setProperty("codename1.android.keystoreAlias", alias);
try (FileOutputStream fos = new FileOutputStream(new File(getCN1ProjectDir(), "codenameone_settings.properties"))) {
sp.store(fos, "Updated keystore");
}
} catch (Exception ex) {
getLog().error("Failed to generate keystore", ex);
throw new MojoExecutionException("Failed to generate keystore", ex);
}
}
}
}
r.setCertificatePassword(props.getProperty("codename1.android.keystorePassword"));
for (Object k : props.keySet()) {
String key = (String)k;
if(key.startsWith("codename1.arg.")) {
String value = props.getProperty(key);
String currentKey = key.substring(14);
if(currentKey.indexOf(' ') > -1) {
throw new MojoExecutionException("The build argument contains a space in the key: '" + currentKey + "'");
}
r.putArgument(currentKey, value);
}
}
BuildRequest request = r;
request.setIncludeSource(true);
String testBuild = request.getArg("build.unitTest", null);
if(testBuild != null && testBuild.equals("1")) {
e.setUnitTestMode(true);
}
try {
getLog().info("Starting android project builder...");
boolean result = e.build(distJar, request);
getLog().info("Android project builder completed with result "+result);
if (!result) {
getLog().error("Received false return value from build()");
throw new MojoExecutionException("Android build failed. Received false return value for build");
}
if (BUILD_TARGET_ANDROID_PROJECT.equals(buildTarget) && e.getGradleProjectDirectory() != null) {
File gradleProject = e.getGradleProjectDirectory();
File output = getGeneratedAndroidProjectSourceDirectory();
output.getParentFile().mkdirs();
try {
getLog().info("Copying Gradle Project to "+output);
FileUtils.copyDirectory(gradleProject, output);
} catch (IOException ex) {
throw new MojoExecutionException("Failed to copy gradle project at "+gradleProject+" to "+output, ex);
}
}
if (open) {
openAndroidStudioProject(getGeneratedAndroidProjectSourceDirectory());
}
} catch (BuildException ex) {
getLog().error("Failed to build Android project with error: "+ex.getMessage(), ex);
getLog().error(e.getErrorMessage());
throw new MojoExecutionException("Failed to build android app", ex);
} finally {
e.cleanup();
}
}
private void openAndroidStudioProject(File generatedProject) {
if (isMac) {
getLog().info("Trying to open project in Android studio");
ProcessBuilder pb = new ProcessBuilder("open", "-a", "/Applications/Android Studio.app", generatedProject.getAbsolutePath());
try {
pb.start();
} catch (Exception ex) {
getLog().warn("Failed to open project in Android studio", ex);
getLog().warn("Please open the project in Android studio manually.");
getLog().warn("The project is located at "+generatedProject.getAbsolutePath());
}
} else if (isWindows) {
getLog().info("Trying to open project in Android studio");
ProcessBuilder pb = new ProcessBuilder("C:\\Program Files\\Android\\Android Studio\\bin\\studio.bat", generatedProject.getAbsolutePath());
try {
pb.start();
} catch (Exception ex) {
getLog().warn("Failed to open project in Android studio", ex);
getLog().warn("Please open the project in Android studio manually.");
getLog().warn("The project is located at "+generatedProject.getAbsolutePath());
}
} else {
getLog().warn("Opening automatically in Android studio not supported on this platform.");
getLog().warn("Please open the project in Android studio manually.");
getLog().warn("The project is located at "+generatedProject.getAbsolutePath());
}
}
private File getWorkspace(Properties props, File xcprojectRoot) {
return new File(xcprojectRoot, props.getProperty("codename1.mainName")+".xcworkspace");
}
private void openWorkspace(File workspace) throws MojoExecutionException {
try {
ProcessBuilder pb = new ProcessBuilder("open", workspace.getAbsolutePath());
Process p = pb.start();
int result = p.waitFor();
if (result != 0) {
throw new MojoExecutionException("Failed to open project at "+workspace+". Result code: "+result);
}
} catch (Exception ex) {
throw new MojoExecutionException("Failed to open project at "+workspace, ex);
}
}
private void doIOSLocalBuild(File tmpProjectDir, Properties props, File distJar) throws MojoExecutionException {
if (BUILD_TARGET_XCODE_PROJECT.equals(buildTarget)) {
File generatedProject = getGeneratedIOSProjectSourceDirectory();
getLog().info("Generating Xcode Project to "+generatedProject+"...");
try {
if (generatedProject.exists()) {
getLog().info("Xcode project already exists. Checking to see if it needs updating...");
if (getSourcesModificationTime() <= lastModifiedRecursive(generatedProject)) {
getLog().info("Sources have not changed. Skipping Xcode project generation");
if (open) {
getLog().info("Opening workspace project "+getWorkspace(props, generatedProject));
openWorkspace(getWorkspace(props, generatedProject));
}
return;
}
}
} catch (IOException ex) {
throw new MojoExecutionException("Failed to find last modification time of "+generatedProject);
}
}
File codenameOneJar = getJar("com.codenameone", "codenameone-core");
IPhoneBuilder e = new IPhoneBuilder();
e.setLogger(getLog());
File buildDirectory = new File(tmpProjectDir, "dist" + File.separator + "ios-build");
e.setBuildDirectory(buildDirectory);
e.setCodenameOneJar(codenameOneJar);
e.setPlatform("ios");
BuildRequest r = new BuildRequest();
r.setAppid(props.getProperty("codename1.ios.appid"));
r.setDisplayName(props.getProperty("codename1.displayName"));
r.setPackageName(props.getProperty("codename1.packageName"));
r.setMainClass(props.getProperty("codename1.mainName"));
r.setVersion(props.getProperty("codename1.version"));
String iconPath = props.getProperty("codename1.icon");
File iconFile = new File(iconPath);
if (!iconFile.isAbsolute()) {
iconFile = new File(getCN1ProjectDir(), iconPath);
}
try {
BufferedImage bi = ImageIO.read(iconFile);
if(bi.getWidth() != 512 || bi.getHeight() != 512) {
throw new MojoExecutionException("The icon must be a 512x512 pixel PNG image. It will be scaled to the proper sizes for devices");
}
r.setIcon(iconFile.getAbsolutePath());
} catch (IOException ex) {
throw new MojoExecutionException("Error reading the icon: the icon must be a 512x512 pixel PNG image. It will be scaled to the proper sizes for devices");
}
r.setVendor(props.getProperty("codename1.vendor"));
r.setSubTitle(props.getProperty("codename1.secondaryTitle"));
r.setType("ios");
for (Object k : props.keySet()) {
String key = (String)k;
if(key.startsWith("codename1.arg.")) {
String value = props.getProperty(key);
String currentKey = key.substring(14);
if(currentKey.indexOf(' ') > -1) {
throw new MojoExecutionException("The build argument contains a space in the key: '" + currentKey + "'");
}
r.putArgument(currentKey, value);
}
}
BuildRequest request = r;
String incSources = request.getArg("build.incSources", null);
request.setIncludeSource(true);
String testBuild = request.getArg("build.unitTest", null);
if(testBuild != null && testBuild.equals("1")) {
e.setUnitTestMode(true);
}
try {
boolean result = e.build(distJar, request);
if (!result) {
throw new MojoExecutionException("iOS build failed");
}
if (BUILD_TARGET_XCODE_PROJECT.equals(buildTarget) && e.getXcodeProjectDir() != null) {
File xcodeProject = e.getXcodeProjectDir();
File output = getGeneratedIOSProjectSourceDirectory();
output.getParentFile().mkdirs();
try {
getLog().info("Copying Xcode Project to "+output);
FileUtils.copyDirectory(xcodeProject, output);
} catch (IOException ex) {
throw new MojoExecutionException("Failed to copy xcode project at "+xcodeProject+" to "+output, ex);
}
if (open) {
getLog().info("Opening workspace project "+getWorkspace(props, output));
openWorkspace(getWorkspace(props, output));
}
}
} catch (BuildException ex) {
throw new MojoExecutionException("Failed to build ios app", ex);
} finally {
e.cleanup();
}
}
protected void afterBuild() {
}
private static class LibraryPropertiesException extends Exception {
private String libName;
LibraryPropertiesException(String libName, String message) {
super(message);
this.libName = libName;
}
}
private static class VersionMismatchException extends LibraryPropertiesException {
VersionMismatchException(String libName, String message) {
super(libName, message);
}
}
private static class PropertyConflictException extends LibraryPropertiesException {
PropertyConflictException(String libName, String message) {
super(libName, message);
}
}
private SortedProperties mergeRequiredProperties(String libraryName, Properties libProps, Properties projectProps) throws LibraryPropertiesException {
String javaVersion = (String)projectProps.getProperty("codename1.arg.java.version", "8");
String javaVersionLib = (String)libProps.get("codename1.arg.java.version");
if(javaVersionLib != null){
int v1 = 5;
if(javaVersion != null){
v1 = Integer.parseInt(javaVersion);
}
int v2 = Integer.parseInt(javaVersionLib);
//if the lib java version is bigger, this library cannot be used
if(v1 < v2){
throw new VersionMismatchException(libraryName, "Cannot use a cn1lib with java version "
+ "greater then the project java version");
}
}
//merge and save
SortedProperties merged = new SortedProperties();
merged.putAll(projectProps);
Enumeration keys = libProps.propertyNames();
while(keys.hasMoreElements()){
String key = (String) keys.nextElement();
if(!merged.containsKey(key)){
merged.put(key, libProps.getProperty(key));
}else{
//if this property already exists with a different value the
//install will fail
if(!merged.get(key).equals(libProps.getProperty(key))){
throw new PropertyConflictException(libraryName, "Property " + key + " has a conflict");
}
}
}
return merged;
}
}