package com.mobilesorcery.sdk.core.build;
import java.io.File;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.ui.IMemento;
import com.mobilesorcery.sdk.core.DefaultPackager;
import com.mobilesorcery.sdk.core.IBuildResult;
import com.mobilesorcery.sdk.core.IBuildSession;
import com.mobilesorcery.sdk.core.IBuildState;
import com.mobilesorcery.sdk.core.IBuildVariant;
import com.mobilesorcery.sdk.core.IFileTreeDiff;
import com.mobilesorcery.sdk.core.IFilter;
import com.mobilesorcery.sdk.core.IProcessConsole;
import com.mobilesorcery.sdk.core.IPropertyOwner;
import com.mobilesorcery.sdk.core.MoSyncBuilder;
import com.mobilesorcery.sdk.core.MoSyncProject;
import com.mobilesorcery.sdk.core.MoSyncProjectParameterResolver;
import com.mobilesorcery.sdk.core.ParameterResolver;
import com.mobilesorcery.sdk.core.PropertyUtil;
import com.mobilesorcery.sdk.core.Util;
import com.mobilesorcery.sdk.core.LineReader.ILineHandler;
import com.mobilesorcery.sdk.internal.PipeTool;
import com.mobilesorcery.sdk.internal.dependencies.IDependencyProvider;
import com.mobilesorcery.sdk.profiles.IProfile;
public class LinkBuildStep extends AbstractBuildStep {
public static class Factory extends AbstractBuildStepFactory {
@Override
public IBuildStep create() {
return new LinkBuildStep();
}
@Override
public String getId() {
return ID;
}
@Override
public String getName() {
return "Link";
}
}
public static final String ID = "link";
public LinkBuildStep() {
setId(ID);
setName("Link");
}
@Override
public int incrementalBuild(MoSyncProject mosyncProject, IBuildSession session,
IBuildVariant variant, IFileTreeDiff diff,
IBuildResult result, IProgressMonitor monitor) throws Exception {
if (isOutputType(mosyncProject, variant, MoSyncBuilder.OUTPUT_TYPE_NATIVE_COMPILE)) {
getConsole().addMessage("Native compilation");
return CONTINUE;
}
int continueFlag = IBuildStep.CONTINUE;
IProcessConsole console = getConsole();
IPropertyOwner buildProperties = getBuildProperties();
PipeTool pipeTool = getPipeTool();
pipeTool.setParameterResolver(getParameterResolver());
ILineHandler lineHandler = getDefaultLineHandler();
IProject project = mosyncProject.getWrappedProject();
IProfile targetProfile = variant.getProfile();
IPath resource = MoSyncBuilder.getResourceOutputPath(project, variant);
ParameterResolver resolver = getParameterResolver();
boolean isLibOrExt = MoSyncBuilder.isLib(mosyncProject) || MoSyncBuilder.isExtension(mosyncProject);
IPath program = MoSyncBuilder.getProgramOutputPath(project, variant);
IPath programComb = MoSyncBuilder.getProgramCombOutputPath(project, variant);
IPath libraryOutput = isLibOrExt ? MoSyncBuilder.computeLibraryOutput(mosyncProject, buildProperties) : null;
boolean librariesHaveChanged = haveLibrariesChanged(mosyncProject, variant, buildProperties, programComb);
boolean requiresLinking = librariesHaveChanged || diff == null || !diff.isEmpty();
if (librariesHaveChanged) {
console.addMessage("Libraries have changed, will require re-linking");
}
/**
* Perform linking.
*/
if (requiresLinking) {
String[] objectFiles = getObjectFilesForProject(session);
pipeTool.setInputFiles(objectFiles);
String pipeToolMode = MoSyncBuilder.getPipeToolMode(mosyncProject, targetProfile, isLibOrExt);
pipeTool.setMode(pipeToolMode);
pipeTool.setOutputFile(isLibOrExt ? libraryOutput : program);
pipeTool.setLibraryPaths(MoSyncBuilder.resolvePaths(MoSyncBuilder.getLibraryPaths(project, buildProperties), resolver));
pipeTool.setLibraries(MoSyncBuilder.getLibraries(mosyncProject, variant, buildProperties));
boolean elim = !isLibOrExt && PropertyUtil.getBoolean(buildProperties, MoSyncBuilder.DEAD_CODE_ELIMINATION);
pipeTool.setDeadCodeElimination(elim);
pipeTool.setCollectStabs(true);
String[] extraLinkerSwitches = PropertyUtil.getStrings(buildProperties, MoSyncBuilder.EXTRA_LINK_SWITCHES);
pipeTool.setExtraSwitches(extraLinkerSwitches);
continueFlag = (pipeTool.run() == PipeTool.SKIP_RETURN_CODE ? IBuildStep.SKIP : IBuildStep.CONTINUE);
// If needed, run a second time to generate IL
if (isLibOrExt == false && pipeToolMode.equals(PipeTool.BUILD_C_MODE) == false) {
pipeTool.setMode(PipeTool.BUILD_C_MODE);
pipeTool.run();
}
if (elim) {
PipeTool elimPipeTool = new PipeTool();
elimPipeTool.setProject(project);
elimPipeTool.setVariant(variant);
elimPipeTool.setLineHandler(lineHandler);
elimPipeTool.setNoVerify(true);
elimPipeTool.setGenerateSLD(false);
elimPipeTool.setMode(PipeTool.BUILD_C_MODE);
elimPipeTool.setOutputFile(program);
elimPipeTool.setConsole(console);
elimPipeTool.setExtraSwitches(extraLinkerSwitches);
elimPipeTool.setAppCode(MoSyncBuilder.getCurrentAppCode(session));
elimPipeTool.setArguments(buildProperties);
File rebuildFile = new File(elimPipeTool.getExecDir(), "rebuild.s");
elimPipeTool.setInputFiles(new String[] { rebuildFile.getAbsolutePath() });
elimPipeTool.run();
}
if (!isLibOrExt) {
// Create "comb" file - program + resources in one. We'll
// always
// make one, even though no resources present.
ArrayList<File> parts = new ArrayList<File>();
if (program.toFile().exists()) {
parts.add(program.toFile());
}
if (getResourceFiles(session).length > 0 && program.toFile().exists() && resource.toFile().exists()) {
parts.add(resource.toFile());
}
if (parts.size() > 1) {
console.addMessage(MessageFormat.format("Combining {0} into one large file, {1}", Util.join(parts.toArray(), ", "), programComb
.toFile()));
}
Util.mergeFiles(new SubProgressMonitor(monitor, 1), parts.toArray(new File[parts.size()]), programComb.toFile());
}
}
IPath buildResult = isLibOrExt ? libraryOutput : programComb;
result.setIntermediateBuildResult(ID, buildResult.toFile());
return continueFlag;
}
private String[] getResourceFiles(IBuildSession session) {
String[] result = (String[]) session.getProperties().get(ResourceBuildStep.RESOURCE_FILES);
return result == null ? new String[0] : result;
}
private String[] getObjectFilesForProject(IBuildSession session) {
String[] result = (String[]) session.getProperties().get(CompileBuildStep.OBJECT_FILES);
return result == null ? new String[0] : result;
}
/**
* Returns true if any of the libraries that the given project depends
* on have changed.
*
* @param mosyncProject Project to check for changes.
* @param variant
* @param buildProperties Build properties of project.
* @param programComb Latest built program file.
* @return true if any of the libraries have changed, false otherwise.
*/
private boolean haveLibrariesChanged(MoSyncProject mosyncProject, IBuildVariant variant, IPropertyOwner buildProperties, IPath programComb)
{
long librariesTouched = mosyncProject.getLibraryLookup(variant, buildProperties).getLastTouched();
long programCombTouched = programComb.toFile().exists() ? programComb.toFile().lastModified() : Long.MAX_VALUE;
return librariesTouched > programCombTouched;
}
@Override
public boolean shouldAdd(IBuildSession session) {
return session.doLink();
}
@Override
public String[] getDependees() {
return new String[] { CompileBuildStep.ID };
}
}