/* * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.flex.compiler.clients; import org.apache.flex.compiler.Messages; import org.apache.flex.compiler.clients.problems.ProblemFormatter; import org.apache.flex.compiler.clients.problems.ProblemPrinter; import org.apache.flex.compiler.clients.problems.ProblemQuery; import org.apache.flex.compiler.clients.problems.WorkspaceProblemFormatter; import org.apache.flex.compiler.common.VersionInfo; import org.apache.flex.compiler.config.CommandLineConfigurator; import org.apache.flex.compiler.config.Configuration; import org.apache.flex.compiler.config.ConfigurationBuffer; import org.apache.flex.compiler.config.ConfigurationPathResolver; import org.apache.flex.compiler.config.ConfigurationValue; import org.apache.flex.compiler.config.Configurator; import org.apache.flex.compiler.exceptions.ConfigurationException; import org.apache.flex.compiler.internal.config.IConfigurationFilter; import org.apache.flex.compiler.internal.config.annotations.Arguments; import org.apache.flex.compiler.internal.config.annotations.Config; import org.apache.flex.compiler.internal.config.localization.LocalizationManager; import org.apache.flex.compiler.internal.projects.CompilerProject; import org.apache.flex.compiler.internal.projects.FlexProject; import org.apache.flex.compiler.internal.targets.AppSWFTarget; import org.apache.flex.compiler.internal.targets.Target; import org.apache.flex.compiler.internal.workspaces.Workspace; import org.apache.flex.compiler.problems.ConfigurationProblem; import org.apache.flex.compiler.problems.FileNotFoundProblem; import org.apache.flex.compiler.problems.ICompilerProblem; import org.apache.flex.compiler.targets.ITargetProgressMonitor; import org.apache.flex.compiler.targets.ITargetSettings; import org.apache.flex.swf.io.ISWFWriterFactory; import org.apache.flex.swf.Header; import org.apache.flex.swf.ISWF; import org.apache.flex.swf.io.ISWFWriter; import org.apache.flex.swf.io.SWFReader; import org.apache.flex.swf.io.SizeReportWritingSWFWriter; import com.google.common.collect.ImmutableSet; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.io.IOUtils; import org.apache.flex.tools.FlexTool; /** * Command line optimizer - can read in a swf, apply the optimizations usually done during swf linking, * and write out the swf again. */ public class Optimizer implements FlexTool { static final String NEWLINE = System.getProperty("line.separator"); private static final String DEFAULT_VAR = "input"; private static final String L10N_CONFIG_PREFIX = "org.apache.flex.compiler.internal.config.configuration"; /** * Entry point for <code>optimizer</code> tool. * * @param args command line arguments */ public static void main(final String[] args) { int exitCode = staticMainNoExit(args); System.exit(exitCode); } public static int staticMainNoExit(final String[] args) { final Optimizer optimizer = new Optimizer(); return optimizer.mainNoExit(args); } @Override public String getName() { return FLEX_TOOL_OPTIMIZER; } @Override public int execute(String[] args) { return mainNoExit(args); } public int mainNoExit(final String[] args) { int result = 0; long startTime = System.nanoTime(); if( configure(args) ) { ITargetSettings targetSettings = getTargetSettings(); final File inputFile = new File(getInputFilePath()); File outputFile = new File(config.getOutput()); InputStream in = null; try { if( inputFile.exists() ) { // Read in the SWF in = new BufferedInputStream(new FileInputStream(inputFile)); SWFReader reader = new SWFReader(); try { ISWF swf = reader.readFrom(in, getInputFilePath()); // record any problems encountered reading the swf problems.addAll(reader.getProblems()); List<ICompilerProblem> linkProblems = new ArrayList<ICompilerProblem>(); // do the optimization OptimizerSWFTarget target = new OptimizerSWFTarget(swf, project, targetSettings, null); ISWF optimizedSWF = target.build(linkProblems); // record any problems found problems.addAll(linkProblems); if( !problems.hasErrors() ) { Header.Compression compression = Header.decideCompression(true, swf.getVersion(), false); final ISWFWriterFactory writerFactory = SizeReportWritingSWFWriter.getSWFWriterFactory( targetSettings.getSizeReport()); final ISWFWriter writer = writerFactory.createSWFWriter(optimizedSWF, compression, targetSettings.isDebugEnabled(), targetSettings.isTelemetryEnabled()); int swfSize = writer.writeTo(outputFile); long endTime = System.nanoTime(); String seconds = String.format("%5.3f", (endTime - startTime) / 1e9); Map<String, Object> params = new HashMap<String, Object>(); params.put("byteCount", swfSize); params.put("path", outputFile.getCanonicalPath()); params.put("seconds", seconds); System.out.println(Messages.getString( "MXMLC.bytes_written_to_file_in_seconds_format", params)); } } finally { IOUtils.closeQuietly(reader); } } else { problems.add(new FileNotFoundProblem(inputFile.getAbsolutePath())); } } catch(IOException ioe) { final ICompilerProblem problem = new ConfigurationProblem(null, -1, -1, -1, -1, ioe.getMessage()); problems.add(problem); } finally { if ( in != null ) { try { in.close(); } catch(IOException ioe) { final ICompilerProblem problem = new ConfigurationProblem(null, -1, -1, -1, -1, ioe.getMessage()); problems.add(problem); } } } } else { result = 1; } // Print out any errors we may have encountered ProblemFormatter formatter = new WorkspaceProblemFormatter(workspace, null); ProblemPrinter printer = new ProblemPrinter(formatter, System.err); printer.printProblems(problems.getFilteredProblems()); if( problems.hasErrors() ) result = 1; return result; } public Optimizer() { workspace = new Workspace(); project = new FlexProject(workspace); problems = new ProblemQuery(); } protected boolean configure(String[] args) { projectConfigurator = createConfigurator(); try { // Print brief usage if no arguments provided. if (args.length == 0) { final String usage = CommandLineConfigurator.brief( getProgramName(), DEFAULT_VAR, LocalizationManager.get(), L10N_CONFIG_PREFIX); System.out.println(getStartMessage()); if (usage != null) System.out.println(usage); // Create a default configuration so we can exit gracefully. config = new OptimizerConfiguration(); return false; } ConfigurationPathResolver resolver = new ConfigurationPathResolver(System.getProperty("user.dir")); projectConfigurator.setConfigurationPathResolver(resolver); projectConfigurator.setConfiguration(args, getConfigurationDefaultVariable()); projectConfigurator.applyToProject(project); config = (OptimizerConfiguration)projectConfigurator.getConfiguration(); configBuffer = projectConfigurator.getConfigurationBuffer(); config.setCompilerOptimize(null, true); problems.addAll(projectConfigurator.getConfigurationProblems()); // Print version if "-version" is present. if (configBuffer.getVar("version") != null) { System.out.println(VersionInfo.buildMessage()); return false; } // Print help if "-help" is present. final List<ConfigurationValue> helpVar = configBuffer.getVar("help"); if (helpVar != null) { processHelp(helpVar); return false; } if( problems.hasErrors() ) return false; } catch (Exception e) { final ICompilerProblem problem = new ConfigurationProblem(null, -1, -1, -1, -1, e.getMessage()); problems.add(problem); return false; } return true; } /** * Print detailed help information if -help is provided. */ private void processHelp(final List<ConfigurationValue> helpVar) { final Set<String> keywords = new LinkedHashSet<String>(); for (final ConfigurationValue val : helpVar) { for (final Object element : val.getArgs()) { String keyword = (String)element; while (keyword.startsWith("-")) keyword = keyword.substring(1); keywords.add(keyword); } } if (keywords.size() == 0) keywords.add("help"); final String usages = CommandLineConfigurator.usage( getProgramName(), DEFAULT_VAR, configBuffer, keywords, LocalizationManager.get(), L10N_CONFIG_PREFIX); System.out.println(getStartMessage()); System.out.println(usages); } /** * Get the start up message that contains the program name * with the copyright notice. * * @return */ private String getStartMessage() { // This must not be localized. String message = "Apache ActionScript Compiler (optimizer)" + NEWLINE + VersionInfo.buildMessage() + NEWLINE; return message; } /** * @return the input file path */ private String getInputFilePath() { return config.getInput(); } /** * @return the target settings specified by the configuration */ private ITargetSettings getTargetSettings() { if (targetSettings == null) targetSettings = projectConfigurator.getTargetSettings(getTargetType()); return targetSettings; } private Workspace workspace; private FlexProject project; private Configurator projectConfigurator; private OptimizerConfiguration config; private ITargetSettings targetSettings; private ProblemQuery problems; protected ConfigurationBuffer configBuffer; protected String getConfigurationDefaultVariable() { return "input"; } protected Configurator createConfigurator() { return new OptimizerConfigurator(OptimizerConfiguration.class); } /** * @return always "optimizer" */ protected String getProgramName() { return "optimizer"; } /** */ protected Target.TargetType getTargetType() { return Target.TargetType.SWF; } /** * Configuration class - adds the input option */ public static class OptimizerConfiguration extends Configuration { // // 'input' option // private String input; public String getInput() { return input; } @Config(isRequired=true) @Arguments("filename") public void setInput(ConfigurationValue val, String output) throws ConfigurationException { this.input = getOutputPath(val, output); } // // 'output' option // private String output = "output.swf"; @Override public String getOutput() { return output; } @Override @Config @Arguments("filename") public void setOutput(ConfigurationValue val, String output) throws ConfigurationException { this.output = output; } } /** * Optimizer configurator - provides overrides to create the right config buffer, * and defaults for the optimizer */ public static class OptimizerConfigurator extends Configurator { public OptimizerConfigurator(Class<? extends Configuration> configurationClass) { super(configurationClass); } /** * Set of vars that can be set from the command line */ private static Set<String> configVars = ImmutableSet.<String>of("help", "version", "load-config", "input", "output", "compiler.keep-as3-metadata", "compiler.debug"); /** * Create a config filter that will filter out parameters that the optimizer doesn't accept */ protected IConfigurationFilter createConfigFilter() { return new IConfigurationFilter() { @Override public boolean select(String name) { return configVars.contains(name); } }; } /** * Create the config buffer with the right filter */ @Override protected ConfigurationBuffer createConfigurationBuffer(Class<? extends Configuration> configClass) { return new ConfigurationBuffer(configClass, Collections.<String, String>emptyMap(), createConfigFilter()); } /** * Load default values */ @Override protected void loadDefaults (ConfigurationBuffer cfgbuf) throws ConfigurationException { } /** * By-pass the configurations that requires Flex SDK. * * This method is needed here because the baseclass version tries to set some config vars * that don't exist in an optimizer config. */ @Override protected void byPassConfigurationsRequiringFlexSDK() throws ConfigurationException { } } /** * A SWF target that will start with a SWF, and simply link it to build it */ public static class OptimizerSWFTarget extends AppSWFTarget { /** * The swf to optimize */ private ISWF swf; @Override public ISWF build(Collection<ICompilerProblem> problems) { return linkSWF(swf); } public OptimizerSWFTarget (ISWF swf, CompilerProject project, ITargetSettings targetSettings, ITargetProgressMonitor progressMonitor) { super(project, targetSettings, progressMonitor); this.swf = swf; } } }