blob: c3a75a45056a5013ff70130f2c08106d0e908178 [file] [log] [blame]
/*
*
* 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.royale.compiler.clients;
import org.apache.royale.compiler.Messages;
import org.apache.royale.compiler.clients.problems.ProblemFormatter;
import org.apache.royale.compiler.clients.problems.ProblemPrinter;
import org.apache.royale.compiler.clients.problems.ProblemQuery;
import org.apache.royale.compiler.clients.problems.WorkspaceProblemFormatter;
import org.apache.royale.compiler.common.VersionInfo;
import org.apache.royale.compiler.config.CommandLineConfigurator;
import org.apache.royale.compiler.config.Configuration;
import org.apache.royale.compiler.config.ConfigurationBuffer;
import org.apache.royale.compiler.config.ConfigurationPathResolver;
import org.apache.royale.compiler.config.ConfigurationValue;
import org.apache.royale.compiler.config.Configurator;
import org.apache.royale.compiler.exceptions.ConfigurationException;
import org.apache.royale.compiler.internal.config.IConfigurationFilter;
import org.apache.royale.compiler.internal.config.annotations.Arguments;
import org.apache.royale.compiler.internal.config.annotations.Config;
import org.apache.royale.compiler.internal.config.localization.LocalizationManager;
import org.apache.royale.compiler.internal.projects.CompilerProject;
import org.apache.royale.compiler.internal.projects.RoyaleProject;
import org.apache.royale.compiler.internal.targets.AppSWFTarget;
import org.apache.royale.compiler.internal.targets.Target;
import org.apache.royale.compiler.internal.workspaces.Workspace;
import org.apache.royale.compiler.problems.ConfigurationProblem;
import org.apache.royale.compiler.problems.FileNotFoundProblem;
import org.apache.royale.compiler.problems.ICompilerProblem;
import org.apache.royale.compiler.targets.ITargetProgressMonitor;
import org.apache.royale.compiler.targets.ITargetSettings;
import org.apache.royale.swf.io.ISWFWriterFactory;
import org.apache.royale.swf.Header;
import org.apache.royale.swf.ISWF;
import org.apache.royale.swf.io.ISWFWriter;
import org.apache.royale.swf.io.SWFReader;
import org.apache.royale.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.royale.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 RoyaleProject(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 RoyaleProject 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;
}
}
}