package com.mobilesorcery.sdk.core;
import java.io.File;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import com.mobilesorcery.sdk.core.apisupport.nfc.NFCSupport;
import com.mobilesorcery.sdk.core.build.AbstractBuildStep;
import com.mobilesorcery.sdk.core.security.IApplicationPermissions;
import com.mobilesorcery.sdk.core.security.ICommonPermissions;
import com.mobilesorcery.sdk.internal.builder.IncrementalBuilderVisitor;
import com.mobilesorcery.sdk.internal.builder.MoSyncBuilderVisitor;
import com.mobilesorcery.sdk.profiles.IProfile;
import com.mobilesorcery.sdk.profiles.Profile;
/**
* Uses the packager tool in the mosync binary directory to create packages
*
* @author mattias.bybro@mosync.com
*
*/
public abstract class PackageToolPackager extends AbstractPackager {
@Override
public void createPackage(MoSyncProject project, IBuildSession session,
IBuildVariant variant, IFileTreeDiff diff, IBuildResult buildResult)
throws CoreException {
IProfile profile = variant.getProfile();
try {
if (needsPackaging(diff)) {
IPath packagerTool = MoSyncTool.getDefault().getBinary(
"package");
DefaultPackager internal = new DefaultPackager(project, variant);
CommandLineBuilder commandLine = new CommandLineBuilder(
packagerTool.toOSString());
addGeneralParameters(project, session, variant, commandLine);
addPlatformSpecifics(project, variant, commandLine);
String packageOutputDirStr = internal
.get(DefaultPackager.PACKAGE_OUTPUT_DIR);
File packageOutputDir = new File(packageOutputDirStr);
packageOutputDir.mkdirs();
internal.runCommandLine(commandLine.asArray(),
commandLine.toHiddenString());
}
buildResult.setBuildResult(computeBuildResult(project, variant));
} catch (Exception e) {
String errorMsg = MessageFormat.format(
"Failed to create package for {0} (platform: {1}). Reason: {2}",
profile, Profile.getAbbreviatedPlatform(profile), e.getMessage());
throw new CoreException(new Status(IStatus.ERROR,
CoreMoSyncPlugin.PLUGIN_ID, errorMsg, e));
}
}
private boolean needsPackaging(IFileTreeDiff diff) {
// Poor man's dependency check -- if any file is touched with the
// exception
// of .* resources, then rebuild. This is because we have no dependency
// file output from the package tool (yet?)
if (diff == null) {
return true;
}
Collection<IPath> changeSet = diff.getChanged();
for (IPath changed : changeSet) {
if (!changed.lastSegment().startsWith(".")) {
return true;
}
}
return false;
}
/**
* Clients override this method and/or the createPackage method.
*
* @param variant
* @param project
* @return
* @throws ParameterResolverException
* @throws CoreException
*/
protected Map<String, List<File>> computeBuildResult(MoSyncProject project,
IBuildVariant variant) throws ParameterResolverException, CoreException {
return null;
}
protected List<File> computeNativeBuildResult(MoSyncProject project,
IBuildVariant variant) throws ParameterResolverException, CoreException {
return null;
}
private void addGeneralParameters(MoSyncProject project, IBuildSession session,
IBuildVariant variant, CommandLineBuilder commandLine)
throws Exception {
DefaultPackager internal = new DefaultPackager(project, variant);
IProfile profile = variant.getProfile();
File program = internal.resolveFile("%program-output%");
File resource = internal.resolveFile("%resource-output%");
File iconFile = project.getIconFile();
if (iconFile == null) {
iconFile = getDefaultIconFile();
}
String packageOutputDir = internal
.get(DefaultPackager.PACKAGE_OUTPUT_DIR);
String vendor = internal.get(DefaultPackager.APP_VENDOR_NAME);
Version version = new Version(internal.get(DefaultPackager.APP_VERSION));
String appName = internal.get(DefaultPackager.APP_NAME);
IApplicationPermissions permissions = project.getPermissions();
Object modifiedPermissions = session.getProperties().get(AbstractBuildStep.MODIFIED_PERMISSIONS);
if (modifiedPermissions instanceof IApplicationPermissions) {
permissions = (IApplicationPermissions) modifiedPermissions;
}
String permissionsStr = Util.join(
permissions.getRequestedPermissions(true).toArray(), ",");
if (project.getProfileManagerType() == MoSyncTool.DEFAULT_PROFILE_TYPE) {
commandLine.flag("-t").with("platform");
}
commandLine.flag("-p").with(program);
if (resource.exists()) {
commandLine.flag("-r").with(resource);
}
if (iconFile != null && iconFile.exists()) {
commandLine.flag("-i").with(iconFile);
}
commandLine.flag("-d").with(packageOutputDir).flag("-m")
.with(MoSyncTool.toString(profile)).flag("--vendor")
.with(vendor).flag("-n").with(appName).flag("--version")
.with(version.asCanonicalString());
commandLine.flag("--project").with(project.getWrappedProject().getLocation().toFile());
List<String> extensions = new ArrayList<String>();
getNativePathsAndModules(project, variant, null, extensions);
if (!extensions.isEmpty()) {
commandLine.flag("--extensions").with(Util.join(extensions.toArray(), ","));
}
commandLine.flag("--output-type").with(getOutputType(project));
if (!Util.isEmpty(permissionsStr)) {
commandLine.flag("--permissions").with(permissionsStr);
}
if (shouldUseDebugRuntimes(project, variant)) {
commandLine.flag("--debug");
}
if (permissions.isPermissionRequested(ICommonPermissions.NFC)) {
File nfcDescription = NFCSupport.create(project)
.getNFCDescription();
commandLine.flag("--nfc").with(nfcDescription);
}
}
public void buildNative(MoSyncProject project, IBuildSession session,
IBuildVariant variant, IBuildResult result) throws Exception {
IPropertyOwner properties = MoSyncBuilder.getPropertyOwner(project, variant.getConfigurationId());
CommandLineBuilder commandLine = new CommandLineBuilder(MoSyncTool.getDefault().getBinary("nbuild").toOSString());
commandLine.flag("--platform").with(variant.getProfile().getVendor().getName());
//TODO: Spaces and special chars?
commandLine.flag("--name").with(project.getName());
File location = project.getWrappedProject().getLocation().toFile();
commandLine.flag("--project").with(location);
IPath dst = MoSyncBuilder.getPackageOutputPath(project.getWrappedProject(), variant).removeLastSegments(1);
commandLine.flag("--dst").with(dst.toOSString());
commandLine.flag("--config").with(variant.getConfigurationId());
boolean isDebug = PropertyUtil.getBoolean(properties, MoSyncBuilder.USE_DEBUG_RUNTIME_LIBS);
String libVariant = isDebug ? "debug" : "release";
commandLine.flag("--lib-variant").with(libVariant);
String compilerSwitches = MoSyncBuilder.getExtraCompilerSwitches(project, variant);
// TODO: Platform independent!?
commandLine.flag("--compiler-switches").with(compilerSwitches);
// This one makes sure that exclusions, wild cards, etc work as before!
IncrementalBuilderVisitor visitor = new IncrementalBuilderVisitor() {
public boolean doesAffectBuild(IResource resource) {
return super.doesAffectBuild(resource) && MoSyncBuilderVisitor.hasExtension(resource, MoSyncBuilderVisitor.C_SOURCE_FILE_EXTS);
}
};
visitor.setProject(project.getWrappedProject());
visitor.setDiff(null);
Set<IResource> sourceFiles = visitor.computeResourcesToRebuild(null);
ArrayList<String> listOfRelativeFiles = new ArrayList<String>();
for (IResource sourceFile : sourceFiles) {
listOfRelativeFiles.add(sourceFile.getProjectRelativePath().toOSString());
commandLine.flag("-S" + sourceFile.getProjectRelativePath().toOSString());
}
ArrayList<String> modules = new ArrayList<String>();
ArrayList<IPath> includePaths = new ArrayList<IPath>();
getNativePathsAndModules(project, variant, includePaths, modules);
String[] includes = MoSyncBuilderVisitor.assembleIncludeString(includePaths.toArray(new IPath[0]));
for (String include : includes) {
commandLine.flag(include);
}
if (modules.size() > 0) {
commandLine.flag("--modules").with(Util.join(modules.toArray(), ","));
}
// TODO: Preference?
if (CoreMoSyncPlugin.getDefault().getPreferenceStore().getBoolean(MoSyncBuilder.VERBOSE_BUILDS)) {
commandLine.flag("--verbose");
}
addNativePlatformSpecifics(project, variant, commandLine);
DefaultPackager internal = new DefaultPackager(project, variant);
if (internal.runCommandLine(commandLine.asArray(),
commandLine.toHiddenString()) != 0) {
throw new CoreException(new Status(IStatus.ERROR, CoreMoSyncPlugin.PLUGIN_ID, "Build failed."));
}
List<File> nativeBuildResult = computeNativeBuildResult(project, variant);
if (nativeBuildResult != null) {
result.setBuildResult(NATIVE_LIBS, nativeBuildResult.toArray(new File[0]));
}
}
protected void getNativePathsAndModules(MoSyncProject project, IBuildVariant variant, List<IPath> filteredIncludePaths, List<String> modules) throws ParameterResolverException {
if (modules == null) {
// We allow nulls, so this one's here to avoid NPEs
modules = new ArrayList<String>();
}
if (filteredIncludePaths == null) {
// We allow nulls, so this one's here to avoid NPEs
filteredIncludePaths = new ArrayList<IPath>();
}
modules.addAll(getExtensionModules(project, variant));
filteredIncludePaths.add(MoSyncBuilder.getOutputPath(project.getWrappedProject(), variant));
filteredIncludePaths.addAll(Arrays.asList(MoSyncBuilder.getBaseIncludePaths(project, variant, true)));
}
protected List<String> getExtensionModules(MoSyncProject project, IBuildVariant variant) {
return Arrays.asList(PropertyUtil.getStrings(MoSyncBuilder.getPropertyOwner(project, variant.getConfigurationId()), MoSyncBuilder.EXTENSIONS));
}
protected File getDefaultIconFile() {
File iconFile = MoSyncTool.getDefault().getMoSyncHome()
.append("etc/default.icon").toFile();
return iconFile.exists() ? iconFile : null;
}
protected void addPlatformSpecifics(MoSyncProject project,
IBuildVariant variant, CommandLineBuilder commandLine)
throws Exception {
}
protected void addNativePlatformSpecifics(MoSyncProject project,
IBuildVariant variant, CommandLineBuilder commandLine) throws Exception {
}
protected Map<String, List<File>> createBuildResult(File file) {
HashMap<String, List<File>> result = new HashMap<String, List<File>>();
result.put(IBuildResult.MAIN, Arrays.asList(file));
return result;
}
protected IProcessConsole getBuildConsole() {
return CoreMoSyncPlugin.getDefault().createConsole(MoSyncBuilder.CONSOLE_ID);
}
}