/* Copyright (C) 2009 Mobile Sorcery AB This program is free software; you can redistribute it and/or modify it under the terms of the Eclipse Public License v1.0. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Eclipse Public License v1.0 for more details. You should have received a copy of the Eclipse Public License v1.0 along with this program. It is also available at http://www.eclipse.org/legal/epl-v10.html */ /** * */ package com.mobilesorcery.sdk.internal.builder; import java.io.File; import java.io.IOException; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Set; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.debug.core.DebugPlugin; import com.mobilesorcery.sdk.core.CommandLineExecutor; import com.mobilesorcery.sdk.core.CoreMoSyncPlugin; import com.mobilesorcery.sdk.core.MoSyncBuilder; import com.mobilesorcery.sdk.core.MoSyncTool; import com.mobilesorcery.sdk.core.ParameterResolverException; import com.mobilesorcery.sdk.core.PropertyUtil; import com.mobilesorcery.sdk.core.Util; import com.mobilesorcery.sdk.internal.PipeTool; import com.mobilesorcery.sdk.internal.dependencies.DependencyManager; import com.mobilesorcery.sdk.internal.dependencies.IDependencyProvider; import com.mobilesorcery.sdk.internal.dependencies.ResourceFileDependencyProvider; import com.mobilesorcery.sdk.profiles.ProfileDBManager; public class MoSyncResourceBuilderVisitor extends IncrementalBuilderVisitor { private ArrayList<IResource> resourceFiles = new ArrayList<IResource>(); private IPath outputFile; private PipeTool pipeTool; private IDependencyProvider<IResource> dependencyProvider; @Override public boolean visit(IResource resource) throws CoreException { super.visit(resource); if (doesAffectBuild(resource) && MoSyncBuilder.isResourceFile(resource)) { resourceFiles.add(resource); } return true; } public String[] getResourceFiles() throws CoreException { // TODO: side-effect-less resourceFiles = new ArrayList<IResource>(); project.accept(this); String[] result = new String[resourceFiles.size()]; for (int i = 0; i < result.length; i++) { result[i] = resourceFiles.get(i).getLocation().toOSString(); } return result; } public void setOutputFile(IPath outputFile) { this.outputFile = outputFile; } public void setPipeTool(PipeTool pipeTool) { this.pipeTool = pipeTool; } private int countResourceFiles(List<String> resourceFiles, String ext) { int count = 0; for (String resourceFile : resourceFiles) { if (ext.equals(Util.getExtension(new File(resourceFile)))) { count++; } } return count; } public void incrementalCompile(IProgressMonitor monitor, DependencyManager<IResource> dependencyManager, DependencyManager.Delta<IResource> dependencyDelta) throws CoreException, IOException { Set<IResource> recompileThese = computeResourcesToRebuild(dependencyManager); if (!recompileThese.isEmpty()) { List<String> resourceFiles = new ArrayList<String>(Arrays.asList(getResourceFiles())); int lstxCount = countResourceFiles(resourceFiles, "lstx"); int lstCount = countResourceFiles(resourceFiles, "lst"); if (lstxCount > 0 && lstCount > 0) { throw new CoreException(new Status(IStatus.ERROR, CoreMoSyncPlugin.PLUGIN_ID, "Cannot mix .lst files and .lstx")); } File resDir = MoSyncBuilder.getResourcesDirectory(project); if (resDir != null && lstCount == 0) { resourceFiles.add(resDir.getAbsolutePath()); } if ((lstxCount > 0 || resDir != null) && lstCount == 0) { // Beware; here we once more update the resourceFiles array... resourceFiles = Arrays.asList(compileWithResComp(resourceFiles.toArray(new String[0]), monitor, dependencyManager, dependencyDelta)); } if (!resourceFiles.isEmpty()) { compileWithPipeTool(monitor, dependencyManager, dependencyDelta, resourceFiles.toArray(new String[0])); } } } private String[] getExtraSwitches() { String[] extraResourceSwitches = PropertyUtil.getStrings(getBuildProperties(), MoSyncBuilder.EXTRA_RES_SWITCHES); return extraResourceSwitches; } private String compileWithResComp(String[]resourceFiles, IProgressMonitor monitor, DependencyManager<IResource> dependencyManager, DependencyManager.Delta<IResource> dependencyDelta) throws CoreException, IOException { if (getExtraSwitches().length > 0) { throw new CoreException(new Status(IStatus.ERROR, CoreMoSyncPlugin.PLUGIN_ID, "Extra resource switches are not allowed when using .lstx files or file tree based resources.")); } IPath rescomp = MoSyncTool.getDefault().getBinary("rescomp"); IPath resourceOutput = MoSyncBuilder.getResourceOutputPath(project, getVariant()); IPath intermediateLstFile = resourceOutput.removeLastSegments(1).append("~tmpres.lst"); String platform = ProfileDBManager.getPlatform(getVariant().getProfile()); if (platform == null) { throw new CoreException(new Status(IStatus.ERROR, CoreMoSyncPlugin.PLUGIN_ID, MessageFormat.format("No platform defined for {0}", getVariant()))); } ArrayList<String> args = new ArrayList<String>(); args.add(rescomp.toOSString()); args.add("-L"); args.add(platform); args.add(intermediateLstFile.removeLastSegments(1).toOSString()); for (String resourceFile : resourceFiles) { args.add(resourceFile); } CommandLineExecutor exe = new CommandLineExecutor(MoSyncBuilder.CONSOLE_ID); if (exe.runCommandLine(args.toArray(new String[0])) != 0) { throw new CoreException(new Status(IStatus.ERROR, CoreMoSyncPlugin.PLUGIN_ID, "Could not compile resources")); } return intermediateLstFile.toOSString(); } private void compileWithPipeTool(IProgressMonitor monitor, DependencyManager<IResource> dependencyManager, DependencyManager.Delta<IResource> dependencyDelta, String[] lstFiles) throws CoreException { String[] extraResourceSwitches = PropertyUtil.getStrings(getBuildProperties(), MoSyncBuilder.EXTRA_RES_SWITCHES); pipeTool.setExtraSwitches(extraResourceSwitches); pipeTool.setMode(PipeTool.BUILD_RESOURCES_MODE); pipeTool.setInputFiles(lstFiles); pipeTool.setOutputFile(outputFile); pipeTool.setParameterResolver(getParameterResolver()); try { pipeTool.run(); } catch (ParameterResolverException e) { throw ParameterResolverException.toCoreException(e); } // Explicitly add dependencies for the pipetool output file -- TODO: // outputfile must equal getresourceoutput; remove one. IPath resourcePath = MoSyncBuilder.getResourceOutputPath(project, getVariant()); IFile[] resourceFiles = ResourcesPlugin.getWorkspace().getRoot().findFilesForLocationURI(resourcePath.toFile().toURI()); for (IFile resourceFile : resourceFiles) { dependencyDelta.addDependencies(resourceFile, dependencyProvider); } dependencyDelta.addDependencies(this.resourceFiles, dependencyProvider); } @Override public void setDependencyProvider(IDependencyProvider<IResource> dependencyProvider) { this.dependencyProvider = dependencyProvider; } @Override public boolean isBuildable(IResource resource) { // Ok, I added the 'resource' file just so it will not get filtered out -- what we'd really // like is the dependency files of pipe-tool resource compilation to work properly return Util.equals(MoSyncBuilder.getResourcesDirectory(resource.getProject()), resource.getLocation().toFile()) || MoSyncBuilder.isResourceFile(resource) || MoSyncBuilder.getResourceOutputPath(project, getVariant()).equals(resource.getLocation()); } protected String getName() { return "Resource Compiler"; } }