org.apache.royale.compiler.clients.MXMLC Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of compiler Show documentation
Show all versions of compiler Show documentation
The Apache Royale Compiler
/*
*
* 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 java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.output.CountingOutputStream;
import org.apache.flex.tools.FlexTool;
import org.apache.royale.compiler.Messages;
import org.apache.royale.compiler.clients.problems.CompilerProblemCategorizer;
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.CompilerDiagnosticsConstants;
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.config.ICompilerProblemSettings;
import org.apache.royale.compiler.config.ICompilerSettingsConstants;
import org.apache.royale.compiler.config.RSLSettings;
import org.apache.royale.compiler.config.RSLSettings.RSLAndPolicyFileURLPair;
import org.apache.royale.compiler.exceptions.ConfigurationException;
import org.apache.royale.compiler.filespecs.IFileSpecification;
import org.apache.royale.compiler.internal.common.Counter;
import org.apache.royale.compiler.internal.config.FlashBuilderConfigurator;
import org.apache.royale.compiler.internal.config.localization.LocalizationManager;
import org.apache.royale.compiler.internal.definitions.DefinitionBase;
import org.apache.royale.compiler.internal.graph.GraphMLWriter;
import org.apache.royale.compiler.internal.projects.RoyaleProject;
import org.apache.royale.compiler.internal.projects.DefinitionPriority.BasePriority;
import org.apache.royale.compiler.internal.projects.RoyaleProjectConfigurator;
import org.apache.royale.compiler.internal.targets.LinkageChecker;
import org.apache.royale.compiler.internal.targets.SWFTarget;
import org.apache.royale.compiler.internal.targets.Target;
import org.apache.royale.compiler.internal.units.ResourceModuleCompilationUnit;
import org.apache.royale.compiler.internal.units.SourceCompilationUnitFactory;
import org.apache.royale.compiler.internal.units.StyleModuleCompilationUnit;
import org.apache.royale.compiler.internal.watcher.WatchThread;
import org.apache.royale.compiler.internal.watcher.WatchThread.IWatchWriter;
import org.apache.royale.compiler.internal.workspaces.Workspace;
import org.apache.royale.compiler.problems.ConfigurationProblem;
import org.apache.royale.compiler.problems.FileIOProblem;
import org.apache.royale.compiler.problems.ICompilerProblem;
import org.apache.royale.compiler.problems.InternalCompilerProblem;
import org.apache.royale.compiler.problems.UnableToBuildSWFProblem;
import org.apache.royale.compiler.projects.ICompilerProject;
import org.apache.royale.compiler.targets.ISWFTarget;
import org.apache.royale.compiler.targets.ITargetReport;
import org.apache.royale.compiler.targets.ITargetSettings;
import org.apache.royale.compiler.targets.ITarget.TargetType;
import org.apache.royale.compiler.tree.as.IASNode;
import org.apache.royale.compiler.tree.as.IFileNode;
import org.apache.royale.compiler.units.ICompilationUnit;
import org.apache.royale.compiler.units.ICompilationUnit.UnitType;
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.SizeReportWritingSWFWriter;
import org.apache.royale.utils.FilenameNormalization;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
/**
* The entry-point class for mxmlc.
*/
public class MXMLC implements FlexTool
{
static final String NEWLINE = System.getProperty("line.separator");
private static final String SWF_EXT = ".swf";
private static final String DEFAULT_VAR = "file-specs";
private static final String L10N_CONFIG_PREFIX = "org.apache.royale.compiler.internal.config.configuration";
/**
* Exit code enumerations.
*/
public static enum ExitCode
{
// NOTE: Negative error codes do not work on OSX.
// Therefore the following enum values must be non-negative.
SUCCESS(0),
PRINT_HELP(1),
FAILED_WITH_ERRORS(2),
FAILED_WITH_EXCEPTIONS(3),
FAILED_WITH_CONFIG_ERRORS(4),
WATCHING(1000);
ExitCode(int code)
{
assert code >= 0 : "Exit code must be non-negative";
this.code = code;
}
final int code;
public int getCode()
{
return code;
}
}
/**
* Entry point for the mxmlc
tool.
*
* @param args Command line arguments.
*/
public static void main(final String[] args)
{
final int exitCode = staticMainNoExit(args);
if (exitCode != ExitCode.WATCHING.getCode())
{
System.exit(exitCode);
}
}
/**
* Entry point for the {@code } Ant task.
*
* @param args Command line arguments.
* @return An exit code.
*/
public static int staticMainNoExit(final String[] args)
{
final MXMLC mxmlc = new MXMLC();
return mxmlc.mainNoExit(args);
}
/**
* Determines whether an exit code should be considered
* a fatal failure, such as for an Ant task.
*
* @param code A numeric exit code.
* @return true
if the Ant task failed.
*/
public static boolean isFatalFailure(final int code)
{
// This method really belongs in ExitCode
// but that would complicate FlexTask.
return code == ExitCode.FAILED_WITH_ERRORS.getCode() ||
code == ExitCode.FAILED_WITH_EXCEPTIONS.getCode() ||
code == ExitCode.FAILED_WITH_CONFIG_ERRORS.getCode();
}
@Override
public String getName() {
return FLEX_TOOL_MXMLC;
}
@Override
public int execute(String[] args) {
return mainNoExit(args);
}
/**
* Entry point for when you already have an MXMLC instance.
* This is for unit testing.
*
* @param args Command line arguments.
* @return An exit code.
*/
public int mainNoExit(final String[] args)
{
return mainNoExit(args, System.err);
}
/**
* Entry point for when you already have an MXML instance and want
* to redirect System.err
. This is for unit testing.
*
* @param args Command line arguments.
* @param err An {@link OutputStream} to use instead of System.err
.
* @return An exit code.
*/
@SuppressWarnings("unused")
public int mainNoExit(final String[] args, OutputStream err)
{
startTime = System.nanoTime();
ExitCode exitCode = ExitCode.SUCCESS;
try
{
final boolean continueCompilation = configure(args);
boolean legacyOutput = config.useLegacyMessageFormat();
CompilerProblemCategorizer categorizer = null;
if (legacyOutput)
categorizer = createProblemCategorizer();
ProblemFormatter formatter = new WorkspaceProblemFormatter(workspace, categorizer);
ProblemPrinter printer = new ProblemPrinter(formatter, err);
if (continueCompilation)
{
if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.COMPC_PHASES) == CompilerDiagnosticsConstants.COMPC_PHASES)
System.out.println("Configuration is ok");
project.setProblems(problems.getProblems());
compile();
exitCode = printProblems(printer, legacyOutput);
reportTargetCompletion();
}
else if (problems.hasFilteredProblems())
{
if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.COMPC_PHASES) == CompilerDiagnosticsConstants.COMPC_PHASES)
System.out.println("Failed with config errors");
printer.printProblems(problems.getFilteredProblems());
exitCode = ExitCode.FAILED_WITH_CONFIG_ERRORS;
}
else
{
exitCode = ExitCode.PRINT_HELP;
}
}
catch (Exception e)
{
if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.COMPC_PHASES) == CompilerDiagnosticsConstants.COMPC_PHASES)
System.out.println("Failed with exceptions");
PrintStream printStream = new PrintStream(err);
printStream.println(e.getMessage());
exitCode = ExitCode.FAILED_WITH_EXCEPTIONS;
}
finally
{
if (!config.getWatch() || !ExitCode.SUCCESS.equals(exitCode))
{
waitAndClose();
}
if (Counter.COUNT_TOKENS || Counter.COUNT_NODES ||
Counter.COUNT_DEFINITIONS || Counter.COUNT_SCOPES)
{
Counter.getInstance().dumpCounts();
}
}
if (config.getWatch() && ExitCode.SUCCESS.equals(exitCode))
{
setupWatcher();
exitCode = ExitCode.WATCHING;
}
return exitCode.getCode();
}
protected void setupWatcher()
{
if (!config.getWatch())
{
return;
}
IWatchWriter writer = new IWatchWriter()
{
public void rebuild(Collection units, Collection problems) throws InterruptedException, IOException
{
startTime = System.nanoTime();
workspace.startBuilding();
try
{
if (!setupTargetFile())
{
throw new IOException("Failed to setup target file.");
}
buildArtifact();
for (ICompilationUnit unit : units)
{
// call waitForBuildFinish() to ensure that binding data
// doesn't get lost when a new definition is created
unit.waitForBuildFinish(problems, null);
}
}
finally
{
workspace.doneBuilding();
}
}
public void write(Collection units) throws InterruptedException, IOException
{
workspace.startBuilding();
try
{
final File outputFile = new File(getOutputFilePath());
final int swfSize = writeSWF(swfTarget, outputFile);
long endTime = System.nanoTime();
String seconds = String.format("%5.3f", (endTime - startTime) / 1e9);
Map params = new HashMap();
params.put("byteCount", swfSize);
params.put("path", outputFile.getCanonicalPath());
params.put("seconds", seconds);
swfOutputMessage = Messages.getString("MXMLC.bytes_written_to_file_in_seconds_format",
params);
reportTargetCompletion();
}
finally
{
workspace.doneBuilding();
}
}
};
WatchThread watcherThread = new WatchThread("SWF", writer, config, project, workspace, problems);
watcherThread.start();
}
/**
* Entry point for when you already have an MXML instance and just want to
* compile and not link. This is for FB integration, but other IDEs could
* use this too.
*
* @param args Command line arguments.
* @param err An {@link OutputStream} to use instead of System.err
.
* @return An exit code.
*/
@SuppressWarnings("unused")
public int mainCompileOnly(final String[] args, OutputStream err)
{
if (err == null)
err = System.err;
startTime = System.nanoTime();
ExitCode exitCode = ExitCode.SUCCESS;
try
{
final boolean continueCompilation = configure(args);
boolean legacyOutput = config.useLegacyMessageFormat();
CompilerProblemCategorizer categorizer = null;
if (legacyOutput)
categorizer = createProblemCategorizer();
ProblemFormatter formatter = new WorkspaceProblemFormatter(workspace, categorizer);
ProblemPrinter printer = new ProblemPrinter(formatter, err);
if (continueCompilation)
{
compile(true); // skip linking
exitCode = printProblems(printer, legacyOutput);
reportTargetCompletion();
}
else if (problems.hasFilteredProblems())
{
printer.printProblems(problems.getFilteredProblems());
exitCode = ExitCode.FAILED_WITH_CONFIG_ERRORS;
}
else
{
exitCode = ExitCode.PRINT_HELP;
}
}
catch (Exception e)
{
PrintStream printStream = new PrintStream(err);
printStream.println(e.getMessage());
exitCode = ExitCode.FAILED_WITH_EXCEPTIONS;
}
finally
{
waitAndClose();
if (Counter.COUNT_TOKENS || Counter.COUNT_NODES ||
Counter.COUNT_DEFINITIONS || Counter.COUNT_SCOPES)
{
Counter.getInstance().dumpCounts();
}
}
return exitCode.getCode();
}
/**
* Print the problems in either the legacy format or the new format.
*
* @param printer
* @param legacyOutput
* @return ExitCode
*/
private ExitCode printProblems(ProblemPrinter printer, boolean legacyOutput)
{
ExitCode exitCode = ExitCode.SUCCESS;
if (legacyOutput)
{
if (printer.printProblems(problems.getFilteredProblems()) > 0)
{
if (problems.hasErrors())
exitCode = ExitCode.FAILED_WITH_ERRORS;
// no exit code for warnings because anything except 0 is
// detected as a failure by various build tools
}
}
else
{
Collection errors = new ArrayList();
Collection warnings = new ArrayList();
problems.getErrorsAndWarnings(errors, warnings);
int errorCount = errors.size();
int warningCount = warnings.size();
if (warningCount > 0)
{
System.err.println(Messages.getString("MXMLC.WarningsHeader"));
printer.printProblems(warnings);
}
if (errorCount > 0)
{
System.err.println(Messages.getString("MXMLC.ErrorsHeader"));
printer.printProblems(errors);
}
// Output summary of errors and warnings
if (errorCount == 1)
System.err.println(Messages.getString("MXMLC.1_error"));
else if (errorCount > 0)
System.err.println(Messages.getString("MXMLC.multiple_errors_format",
Collections.singletonMap("errorCount", errors.size())));
if (warningCount == 1)
System.err.println(Messages.getString("MXMLC.1_warning"));
else if (warningCount > 0)
System.err.println(Messages.getString("MXMLC.multiple_warnings_format",
Collections.singletonMap("warningCount", warnings.size())));
if (errorCount > 0)
exitCode = ExitCode.FAILED_WITH_ERRORS;
// no exit code for warnings because anything except 0 is
// detected as a failure by various build tools
}
return exitCode;
}
public MXMLC()
{
workspace = new Workspace();
project = new RoyaleProject(workspace);
problems = new ProblemQuery();
}
protected Workspace workspace;
protected RoyaleProject project;
public Configuration config;
public ProblemQuery problems;
public ConfigurationBuffer configBuffer;
public Class extends Configuration> configurationClass = Configuration.class;
protected Configurator projectConfigurator;
protected ICompilationUnit mainCU;
protected SWFTarget target;
protected long startTime; // start time of execution in nanoseconds
protected ITargetSettings targetSettings;
private ISWF swfTarget;
private String swfOutputMessage;
/**
* Print a message.
*
* @param msg Message text.
*/
public void println(final String msg)
{
System.out.println(msg);
}
/**
* Wait till the workspace to finish compilation and close.
*/
protected void waitAndClose()
{
workspace.startIdleState();
try
{
workspace.close();
}
finally
{
workspace.endIdleState(Collections.>emptyMap());
}
}
/**
* Force terminate the compilation process.
*/
protected void close()
{
workspace.close();
}
/**
* Create a new Configurator. This method may be overridden to allow
* Configurator subclasses to be created that have custom configurations.
*
* @return a new instance or subclass of {@link Configurator}.
*
*/
protected Configurator createConfigurator()
{
return new RoyaleProjectConfigurator(configurationClass);
}
/**
* Load configurations from all the sources.
*
* @param args command line arguments
* @return True if mxmlc should continue with compilation.
*/
public boolean configure(final 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);
println(getStartMessage());
if (usage != null)
println(usage);
// Create a default configuration so we can exit gracefully.
config = new Configuration();
configBuffer = new ConfigurationBuffer(
Configuration.class, Configuration.getAliases());
return false;
}
ConfigurationPathResolver resolver = new ConfigurationPathResolver(System.getProperty("user.dir"));
projectConfigurator.setConfigurationPathResolver(resolver);
projectConfigurator.setWarnOnRoyaleOnlyOptionUsage(false);
if (useFlashBuilderProjectFiles(args))
projectConfigurator.setConfiguration(FlashBuilderConfigurator.computeFlashBuilderArgs(args, getTargetType().getExtension()),
getConfigurationDefaultVariable());
else
projectConfigurator.setConfiguration(args, getConfigurationDefaultVariable());
projectConfigurator.applyToProject(project);
getTargetSettings(); // get targetSettings here to flush out any configuration problems.
problems = new ProblemQuery(projectConfigurator.getCompilerProblemSettings());
// Get the configuration and configBuffer which are now initialized.
config = projectConfigurator.getConfiguration();
Messages.setLocale(config.getToolsLocale());
project.apiReportFile = config.getApiReport();
configBuffer = projectConfigurator.getConfigurationBuffer();
problems.addAll(projectConfigurator.getConfigurationProblems());
// Print version if "-version" is present.
if (configBuffer.getVar("version") != null)
{
println(VersionInfo.buildMessage());
return false;
}
// Print help if "-help" is present.
final List helpVar = configBuffer.getVar("help");
if (helpVar != null)
{
processHelp(helpVar);
return false;
}
for (String fileName : projectConfigurator.getLoadedConfigurationFiles())
{
println(Messages.getString("MXMLC.Loading_configuration_format",
Collections.singletonMap("configurationName", fileName)));
}
// Add a blank line between the configuration list and the rest of
// the output to make the start of the output easier to detect.
println("");
if (config.isVerbose())
{
for (final IFileSpecification themeFile : project.getThemeFiles())
{
println(Messages.getString("MXMLC.Found_theme_file_format",
Collections.singletonMap("themePath",
themeFile.getPath())));
}
}
// If we have configuration errors then exit before trying to
// validate the target.
if (problems.hasErrors())
return false;
validateTargetFile();
DefinitionBase.setPerformanceCachingEnabled(!config.getWatch());
return true;
}
catch (ConfigurationException e)
{
final ICompilerProblem problem = new ConfigurationProblem(e);
problems.add(problem);
return false;
}
catch (Exception e)
{
final ICompilerProblem problem = new ConfigurationProblem(null, -1, -1, -1, -1, e.getMessage());
problems.add(problem);
return false;
}
}
/**
* Get the default variable for this configuration. MXMLC has a default
* variable of "file-spec" and COMPC has default variable of
* "include-classes".
*
* @return the default variable for the configuration.
*/
protected String getConfigurationDefaultVariable()
{
return ICompilerSettingsConstants.FILE_SPECS_VAR;
}
private boolean useFlashBuilderProjectFiles(String[] args)
{
for (String arg : args)
{
if (arg.equals("-fb") || arg.equals("-use-flashbuilder-project-files"))
return true;
}
return false;
}
/**
* Validate target file.
*
* @throws ConfigurationException
*/
protected void validateTargetFile() throws ConfigurationException
{
if(mainCU instanceof ResourceModuleCompilationUnit)
return; //when compiling a Resource Module, no target file is defined.
final String targetFile = config.getTargetFile();
if (targetFile == null)
throw new ConfigurationException.MustSpecifyTarget(null, null, -1);
final File file = new File(targetFile);
if (!file.exists())
throw new ConfigurationException.IOError(targetFile);
}
/**
* Main body of this program. This method is called from the public static
* method's for this program.
*
* @return true if compiler succeeds
*/
protected boolean compile()
{
return compile(false);
}
private boolean compile(boolean skipLinking)
{
boolean compilationSuccess = false;
try
{
if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.COMPC_PHASES) == CompilerDiagnosticsConstants.COMPC_PHASES)
System.out.println("Setting up target file");
if (!setupTargetFile())
{
if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.COMPC_PHASES) == CompilerDiagnosticsConstants.COMPC_PHASES)
System.out.println("Could not set up target file");
return false;
}
if (config.isDumpAst())
dumpAST();
buildArtifact();
project.generateAPIReport();
if (swfTarget == null)
{
if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.COMPC_PHASES) == CompilerDiagnosticsConstants.COMPC_PHASES)
System.out.println("No swftarget");
return false;
}
// Don't create a swf if there are errors unless a
// developer requested otherwise.
if (!config.getCreateTargetWithErrors() && problems.hasErrors())
{
if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.COMPC_PHASES) == CompilerDiagnosticsConstants.COMPC_PHASES)
System.out.println("got errors creating target");
return false;
}
if (skipLinking)
return true;
if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.COMPC_PHASES) == CompilerDiagnosticsConstants.COMPC_PHASES)
System.out.println("attempting to write output");
final File outputFile = new File(getOutputFilePath());
final int swfSize = writeSWF(swfTarget, outputFile);
long endTime = System.nanoTime();
String seconds = String.format("%5.3f", (endTime - startTime) / 1e9);
Map params = new HashMap();
params.put("byteCount", swfSize);
params.put("path", outputFile.getCanonicalPath());
params.put("seconds", seconds);
swfOutputMessage = Messages.getString("MXMLC.bytes_written_to_file_in_seconds_format",
params);
dumpDependencyGraphIfNeeded();
compilationSuccess = true;
}
catch (IOException e)
{
if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.COMPC_PHASES) == CompilerDiagnosticsConstants.COMPC_PHASES)
System.out.println("got IOException in compile()");
final FileIOProblem problem = new FileIOProblem(e);
problems.add(problem);
}
catch (Exception e)
{
if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.COMPC_PHASES) == CompilerDiagnosticsConstants.COMPC_PHASES)
System.out.println("got Exception in compile()");
final ICompilerProblem problem = new InternalCompilerProblem(e);
problems.add(problem);
}
return compilationSuccess;
}
/**
* Setup theme files.
*/
protected void setupThemeFiles()
{
project.setThemeFiles(toFileSpecifications(config.getCompilerThemeFiles(), workspace));
if (config.isVerbose())
{
for (final IFileSpecification themeFile : project.getThemeFiles())
{
verboseMessage(Messages.getString("MXMLC.Found_theme_file_format",
Collections.singletonMap("themePath", themeFile.getPath())));
}
}
}
/**
* Reports the size and location of the target that was created.
* @throws InterruptedException
*
*/
protected void reportTargetCompletion() throws InterruptedException
{
if (swfOutputMessage != null)
{
reportRequiredRSLs(target);
println(swfOutputMessage);
}
}
/**
* Set up any user defines customization of the problem severities.
*
*/
private CompilerProblemCategorizer createProblemCategorizer()
{
ICompilerProblemSettings problemSettings = null;
try
{
problemSettings = projectConfigurator.getCompilerProblemSettings();
}
catch (Exception e)
{
// Create a categorizer that will only use default settings.
}
return new CompilerProblemCategorizer(problemSettings);
}
/**
* Parse all source files and dumpAST
*
* @throws InterruptedException
*/
private void dumpAST() throws InterruptedException
{
final List astDump = new ArrayList();
final Collection problems = new ArrayList();
final ImmutableList compilationUnits = target.getReachableCompilationUnits(problems);
for (final ICompilationUnit compilationUnit : compilationUnits)
{
final IASNode ast = compilationUnit.getSyntaxTreeRequest().get().getAST();
if (ast != null)
{
if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.FILE_NODE) == CompilerDiagnosticsConstants.FILE_NODE)
System.out.println("MXMLC waiting for lock in populateFunctionNodes");
((IFileNode)ast).populateFunctionNodes();
if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.FILE_NODE) == CompilerDiagnosticsConstants.FILE_NODE)
System.out.println("MXMLC done with lock in populateFunctionNodes");
astDump.add(ast.toString());
}
}
println(Joiner.on("\n\n").join(astDump));
}
/**
* Build target artifact.
*
* @throws InterruptedException threading error
* @throws IOException IO error
*/
protected void buildArtifact() throws InterruptedException, IOException
{
swfTarget = buildSWFModel();
}
/**
* Build SWF model object and collect problems building SWF in
* {@link #problems}.
*
* @return SWF model or null if SWF can't be built.
* @throws InterruptedException concurrency problem
*/
private ISWF buildSWFModel() throws InterruptedException
{
final List problemsBuildingSWF =
new ArrayList();
final ISWF swf = target.build(problemsBuildingSWF);
problems.addAll(problemsBuildingSWF);
if (swf == null)
{
ICompilerProblem problem = new UnableToBuildSWFProblem(getOutputFilePath());
problems.add(problem);
}
return swf;
}
private void reportRequiredRSLs(ISWFTarget target) throws InterruptedException
{
// Report the required RSLs:
if (hasRSLs())
{
ITargetReport report = target.getTargetReport();
if (report == null)
return; // target must not have been built.
List requiredRSLs = report.getRequiredRSLs();
List legacyRSLs = targetSettings.getRuntimeSharedLibraries();
if (requiredRSLs.isEmpty() && legacyRSLs.isEmpty())
return;
println(Messages.getString("MXMLC.Required_RSLs"));
// loop thru the RSLs and print out the required RSLs.
for (RSLSettings rslSettings : requiredRSLs)
{
ListrslURLs = rslSettings.getRSLURLs();
Map params = new HashMap();
params.put("rslPath",rslURLs.get(0).getRSLURL());
switch (rslURLs.size())
{
case 0:
assert false; // One RSL URL is required.
break;
case 1:
println(Messages.getString("MXMLC.required_rsl_url_format",
params));
break;
case 2:
println(Messages.getString("MXMLC.required_rsl_url_with_1_failover_format",
params));
break;
default:
params.put("failoverCount", rslURLs.size() - 1);
println(Messages.getString("MXMLC.required_rsl_url_with_multiple_failovers_format",
params));
break;
}
}
// All -runtime-shared-libraries are required
for (String rslURL : legacyRSLs)
println(Messages.getString("MXMLC.required_rsl_url_format",
Collections.singletonMap("rslPath", rslURL)));
}
}
/**
* Virtual method that returns the type of target we are building.
* Subclasses will override this method to return different target types.
*
* @return The {@link TargetType} of the target we are building.
*/
protected TargetType getTargetType()
{
return TargetType.SWF;
}
private ITargetSettings getTargetSettings()
{
if (targetSettings == null)
targetSettings = projectConfigurator.getTargetSettings(getTargetType());
return targetSettings;
}
private boolean hasRSLs()
{
return (getTargetSettings().getRuntimeSharedLibraryPath().size() > 0) ||
(getTargetSettings().getRuntimeSharedLibraryPath().size() > 0);
}
/**
* Write out SWF file and return file size in bytes.
*
* @param swf SWF model
* @param outputFile output SWF file handle
* @return SWF file size in bytes
* @throws IOException error
*/
private int writeSWF(final ISWF swf, final File outputFile) throws IOException
{
final Header.Compression compression = Header.decideCompression(
targetSettings.useCompression(),
targetSettings.getSWFVersion(),
targetSettings.isDebugEnabled());
final ISWFWriterFactory writerFactory = SizeReportWritingSWFWriter.getSWFWriterFactory(
targetSettings.getSizeReport());
final ISWFWriter writer = writerFactory.createSWFWriter(swf, compression,
targetSettings.isDebugEnabled(), targetSettings.isTelemetryEnabled());
return writer.writeTo(outputFile);
}
/**
* MXMLC uses target file as the main compilation unit and derive the output
* SWF file name from this file.
*
* @return true if successful, false otherwise.
*
* @throws InterruptedException
*/
protected boolean setupTargetFile() throws InterruptedException
{
final String mainFileName = config.getTargetFile();
if(mainFileName != null)
{
final String normalizedMainFileName = FilenameNormalization.normalize(mainFileName);
// Can not add a SourceHandler for *.css file because we don't want
// to create compilation units for CSS files on the source path.
if (mainFileName.toLowerCase().endsWith(".css"))
{
mainCU = new StyleModuleCompilationUnit(
project,
workspace.getFileSpecification(normalizedMainFileName),
BasePriority.SOURCE_LIST);
// TODO: Use CSS file name once CSS module runtime code is finalized.
config.setMainDefinition("CSSModule2Main");
project.addCompilationUnitsAndUpdateDefinitions(
Collections.singleton(mainCU));
}
else
{
final SourceCompilationUnitFactory compilationUnitFactory =
project.getSourceCompilationUnitFactory();
File normalizedMainFile = new File(normalizedMainFileName);
if (compilationUnitFactory.canCreateCompilationUnit(normalizedMainFile))
{
// Remove the main file from the source path and put it on the
// source list. The only reason this needs to be done is to
// prevent the compilation unit of the main file from shadowing
// another compilation unit with the same qname. This can
// happen in the odd case where you have test.mxml (main file)
// and test.as in the same directory and test.mxml's compilation
// unit end up shadowing test.as's cu.
project.removeSourceFile(normalizedMainFile);
project.addIncludeSourceFile(normalizedMainFile, true);
Collection mainFileCompilationUnits =
workspace.getCompilationUnits(normalizedMainFileName, project);
assert mainFileCompilationUnits.size() == 1;
mainCU = Iterables.getOnlyElement(mainFileCompilationUnits);
}
}
}
else
{
final List resourceBundleProblems = new ArrayList();
Collection includedResourceBundles = target.getIncludedResourceBundlesCompilationUnits(resourceBundleProblems);
problems.addAll(resourceBundleProblems);
if(includedResourceBundles.size() > 0)
{
//This means that a Resource Module is requested to be built.
mainCU = new ResourceModuleCompilationUnit(project,
"GeneratedResourceModule",
includedResourceBundles,
BasePriority.SOURCE_LIST);
config.setMainDefinition("GeneratedResourceModule");
project.addCompilationUnitsAndUpdateDefinitions(
Collections.singleton(mainCU));
}
}
Preconditions.checkNotNull(mainCU, "Main compilation unit can't be null");
if (getTargetSettings() == null)
return false;
target = (SWFTarget)project.createSWFTarget(getTargetSettings(), null);
return true;
}
/**
* Setups the locale related settings.
*/
protected void setupLocaleSettings()
{
project.setLocales(config.getCompilerLocales());
project.setLocaleDependentResources(config.getLocaleDependentSources());
}
/**
* Get the output file path. If {@code -output} is specified, use its value;
* otherwise, use the same base name as the target file.
*
* @return output file path
*/
private String getOutputFilePath()
{
if (config.getOutput() == null)
return FilenameUtils.removeExtension(config.getTargetFile()).concat(SWF_EXT);
else
return config.getOutput();
}
private void verboseMessage(String s)
{
if (config.isVerbose())
println(s);
}
/**
* Convert file path strings to {@code File} objects. Null values are
* discarded.
*
* @param paths list of paths.
* @return List of File objects. No null values will be returned.
*/
public static List toFiles(final List paths)
{
final List result = new ArrayList();
for (final String path : paths)
{
if (path != null)
result.add(new File(path));
}
return result;
}
/**
* Resolve a list of normalized paths to {@link IFileSpecification} objects
* from the given {@code workspace}.
*
* @param paths A list of normalized paths.
* @param workspace Workspace.
* @return A list of file specifications.
*/
public static List toFileSpecifications(
final List paths,
final Workspace workspace)
{
return Lists.transform(paths, new Function()
{
@Override
public IFileSpecification apply(final String path)
{
if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.WORKSPACE) == CompilerDiagnosticsConstants.WORKSPACE)
System.out.println("MXMLC waiting for lock in toFileSpecifications");
IFileSpecification ret = workspace.getFileSpecification(path);
if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.WORKSPACE) == CompilerDiagnosticsConstants.WORKSPACE)
System.out.println("MXMLC waiting for lock in toFileSpecifications");
return ret;
}
});
}
/**
* Get my program name.
*
* @return always "mxmlc".
*/
protected String getProgramName()
{
return "mxmlc";
}
/**
* Get the start up message that contains the program name
* with the copyright notice.
*
* @return The startup message.
*/
protected String getStartMessage()
{
// This message should not be localized.
String message = "Apache Royale MXML and ActionScript Compiler (mxmlc)" + NEWLINE +
VersionInfo.buildMessage() + NEWLINE;
return message;
}
/**
* Print detailed help information if -help is provided.
*/
private void processHelp(final List helpVar)
{
final Set keywords = new LinkedHashSet();
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);
println(getStartMessage());
println(usages);
}
/**
* "compc" subclass will override this method.
*
* @return False if the client is not "compc".
*/
protected boolean isCompc()
{
return false;
}
private void dumpDependencyGraphIfNeeded() throws IOException, InterruptedException
{
File dependencyGraphOutput = config.getDependencyGraphOutput();
if (dependencyGraphOutput != null)
{
LinkageChecker linkageChecker = new LinkageChecker(project, getTargetSettings());
Target.RootedCompilationUnits rootedCompilationUnits = target.getRootedCompilationUnits();
problems.addAll(rootedCompilationUnits.getProblems());
GraphMLWriter dependencyGraphWriter =
new GraphMLWriter(project.getDependencyGraph(),
rootedCompilationUnits.getUnits(), true,
linkageChecker);
BufferedOutputStream graphStream = new BufferedOutputStream(new FileOutputStream(dependencyGraphOutput));
LinkedList problemList = new LinkedList();
Iterables.addAll(problemList, rootedCompilationUnits.getProblems());
dependencyGraphWriter.writeToStream(graphStream, problemList);
problems.addAll(problemList);
}
}
public ProblemQuery getProblems()
{
return problems;
}
public List getSourceList()
{
ArrayList list = new ArrayList();
LinkedList problemList = new LinkedList();
try
{
ImmutableList units = target.getReachableCompilationUnits(problemList);
for (ICompilationUnit unit : units)
{
UnitType ut = unit.getCompilationUnitType();
if (ut == UnitType.AS_UNIT || ut == UnitType.MXML_UNIT)
{
list.add(unit.getAbsoluteFilename());
}
}
}
catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
return list;
}
public String getMainSource()
{
if (mainCU == null) return "";
return mainCU.getAbsoluteFilename();
}
public ISWF getSWFTarget()
{
return swfTarget;
}
public int writeSWF(OutputStream outputStream)
{
final Header.Compression compression = Header.decideCompression(
targetSettings.useCompression(),
targetSettings.getSWFVersion(),
targetSettings.isDebugEnabled());
final ISWFWriterFactory writerFactory = SizeReportWritingSWFWriter.getSWFWriterFactory(
targetSettings.getSizeReport());
final ISWFWriter writer = writerFactory.createSWFWriter(swfTarget, compression,
targetSettings.isDebugEnabled(), targetSettings.isTelemetryEnabled());
// Write out the SWF, counting how many bytes were written.
final CountingOutputStream output =
new CountingOutputStream(outputStream);
writer.writeTo(output);
final int swfSize = output.getCount();
return swfSize;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy