blob: 23e524fdb7e2b84ac2f4a405e8f8d53480862be2 [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.swc;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
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.DependencyType;
import org.apache.royale.compiler.common.DependencyTypeSet;
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.annotations.Arguments;
import org.apache.royale.compiler.internal.config.annotations.Config;
import org.apache.royale.compiler.internal.config.annotations.InfiniteArguments;
import org.apache.royale.compiler.internal.config.annotations.Mapping;
import org.apache.royale.compiler.internal.config.annotations.SoftPrerequisites;
import org.apache.royale.compiler.internal.config.localization.LocalizationManager;
import org.apache.royale.compiler.internal.projects.RoyaleProject;
import org.apache.royale.compiler.internal.projects.LibraryDependencyGraph;
import org.apache.royale.compiler.internal.targets.SWFTarget;
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.targets.ITargetSettings;
import org.apache.royale.compiler.units.ICompilationUnit;
import org.apache.royale.swc.catalog.XMLFormatter;
/**
* swcdepends command line utility.
*
* Use this utility to see the dependency order of all SWCs in your configuration.
* You can also see:
*
* - the swcs a swc is dependent on (on by default).
* - the scripts that are causing the dependency (off by default).
* - the dependency types of the scripts (off by default).
*
*/
public class SWCDepends
{
// XML names
public static final String SWC_DEPENDENCY_ORDER_ELEMENT = "swc-dependency-order";
public static final String SWC_DEPENDENCIES_ELEMENT = "swc-dependencies";
public static final String SWC_ELEMENT = "swc";
public static final String DEFINITION_DEPENDENCIES_ELEMENT = "definition-dependencies";
public static final String DEFINITION_ELEMENT = "def";
public static final String PATH_ATTRIBUTE = "path";
public static final String ID_ATTRIBUTE = "id";
static final String NEWLINE = System.getProperty("line.separator");
private static final String DEFAULT_VAR = "no-default-arg";
private static final String L10N_CONFIG_PREFIX = "org.apache.royale.compiler.internal.config.configuration";
protected Workspace workspace;
protected RoyaleProject project;
protected DependencyConfiguration config;
protected ProblemQuery problems;
protected ConfigurationBuffer configBuffer;
protected Configurator projectConfigurator;
protected ICompilationUnit mainCU;
protected SWFTarget target;
protected ITargetSettings targetSettings;
private List<String> dependencyOrder; // of all discovered swcs
/**
* Entry point for <code>swcdepends</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 SWCDepends swcDepends = new SWCDepends();
return swcDepends.mainNoExit(args);
}
public SWCDepends()
{
workspace = new Workspace();
project = new RoyaleProject(workspace);
problems = new ProblemQuery();
}
public int mainNoExit(final String[] args)
{
int result = 0;
if (configure(args))
{
XMLStreamWriter xmlWriter = null;
try
{
List<String> showSwcs = config.getShowSwcs();
DependencyTypeSet dependencyTypes = getDependencyTypeSet();
LibraryDependencyGraph graph = project.createLibraryDependencyGraph(dependencyTypes);
xmlWriter = new XMLFormatter(getXMLWriter());
xmlWriter.writeStartDocument();
// write SWCDependencyOrder
xmlWriter.writeStartElement(SWC_DEPENDENCY_ORDER_ELEMENT);
dependencyOrder = graph.getDependencyOrder();
for (String swcName : dependencyOrder)
{
if (!showSwc(swcName, showSwcs))
continue;
println(swcName + ":");
xmlWriter.writeStartElement(SWC_ELEMENT);
xmlWriter.writeAttribute(PATH_ATTRIBUTE, swcName);
// Show the dependencies of this swc.
printDependencies(swcName, xmlWriter, graph);
xmlWriter.writeEndElement(); // SWC
}
xmlWriter.writeEndElement(); // SWC_DEPENDENCY_ORDER
xmlWriter.writeEndDocument();
xmlWriter.flush();
xmlWriter.close();
}
catch (IOException e)
{
final FileIOProblem problem = new FileIOProblem(e);
problems.add(problem);
}
catch (Exception e)
{
final ICompilerProblem problem = new InternalCompilerProblem(e);
problems.add(problem);
}
finally
{
if (xmlWriter != null)
{
try
{
xmlWriter.close();
}
catch (XMLStreamException e)
{
// ignore
}
}
}
}
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;
}
/**
* @param swcName name of swc
* @param swcs list of swcs to show. An empty list means show all.
* @return true if the swc should be shown, false otherwise.
*/
private boolean showSwc(String swcName, List<String>swcs)
{
// filter the swcs that are shown
boolean show = true;
if (swcs.size() != 0)
{
show = false;
for (String showSwc : swcs)
{
if (swcName.endsWith(showSwc))
{
show = true;
break;
}
}
}
return show;
}
/**
* Print the dependencies of a SWC.
*
* @param xmlWriter
* @param graph
* @throws XMLStreamException
*/
private void printDependencies(String swcName, XMLStreamWriter xmlWriter, LibraryDependencyGraph graph) throws XMLStreamException
{
// show swc dependencies
if (config.getShowDependencyList())
{
Set<String> dependencies = graph.getDependencies(swcName);
if (dependencies.size() > 0)
{
xmlWriter.writeStartElement(SWC_DEPENDENCIES_ELEMENT);
// Sort by dependency order so the swcs are always
// in the same order.
List<String> swcDependencies = new ArrayList<String>(graph.getDependencies(swcName));
Collections.sort(swcDependencies,
new Comparator<String>()
{
@Override
public int compare(String o1, String o2)
{
return dependencyOrder.indexOf(o1) - dependencyOrder.indexOf(o2);
}
});
List<String> swcFilter = config.getShowDependentSwcs();
for (String swcDependency : swcDependencies)
{
if (!showSwc(swcDependency, swcFilter))
continue;
println("\t" + swcDependency);
xmlWriter.writeStartElement(SWC_ELEMENT);
xmlWriter.writeAttribute(PATH_ATTRIBUTE, swcDependency);
printExterns(swcName, swcDependency, xmlWriter, graph);
xmlWriter.writeEndElement(); // SWC
}
xmlWriter.writeEndElement(); // SWC_DEPENDENCY_ORDER
}
}
}
/**
* Print the external scripts that are causing the dependency.
*
* @param swcDependency
* @param xmlWriter
* @param graph
* @throws XMLStreamException
*/
private void printExterns(String swcName, String swcDependency,
XMLStreamWriter xmlWriter, LibraryDependencyGraph graph)
throws XMLStreamException
{
// list the external scripts that caused the dependencies between
// swcLocation and swcDepName.
if (config.getShowExterns())
{
xmlWriter.writeStartElement(DEFINITION_DEPENDENCIES_ELEMENT);
Map<String, DependencyTypeSet> dependencyMap = graph.getDependencySet(swcName, swcDependency);
for (Map.Entry<String, DependencyTypeSet> entry : dependencyMap.entrySet())
{
xmlWriter.writeStartElement(DEFINITION_ELEMENT);
xmlWriter.writeAttribute(ID_ATTRIBUTE, entry.getKey());
if (config.getShowTypes())
{
System.out.print("\t\t" + entry.getKey() + "\t");
StringBuilder sb = new StringBuilder();
for (Iterator<DependencyType>iter = entry.getValue().iterator();
iter.hasNext();)
{
DependencyType type = iter.next();
System.out.print(type.getSymbol());
sb.append(type.getSymbol());
if (iter.hasNext())
{
sb.append(",");
System.out.print(" ");
}
}
println("");
xmlWriter.writeAttribute("types", sb.toString());
}
else
{
println("\t\t" + entry.getKey());
}
xmlWriter.writeEndElement(); // SCRIPT_NAME
}
xmlWriter.writeEndElement(); // DEPENDENT_SCRIPTS
}
}
/**
* @return A writer for the xml-based dependency report.
*
* @throws XMLStreamException
* @throws IOException
*/
private XMLStreamWriter getXMLWriter() throws XMLStreamException, IOException
{
Writer writer = null;
if (config.getDependencyReport() != null)
writer = new FileWriter(config.getDependencyReport());
else
writer = new StringWriter(); // throw away output
final XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newInstance();
assert xmlOutputFactory != null : "Expect XMLOutputFactory implementation.";
return xmlOutputFactory.createXMLStreamWriter(writer);
}
/**
* @return the dependency set requested by the user. The default to
* to show all dependency types.
*/
private DependencyTypeSet getDependencyTypeSet()
{
List<String> desiredDependencies = config.getDesiredScriptDependencyTypes();
if (desiredDependencies != null)
{
DependencyTypeSet dependencySet = DependencyTypeSet.noneOf();
List<String> validDependencies = new ArrayList<String>();
for (DependencyType type : DependencyTypeSet.allOf())
{
validDependencies.add(String.valueOf(type.getSymbol()));
}
// convert strings to a enum set.
for (String desiredDependency : desiredDependencies)
{
if (validDependencies.contains(desiredDependency))
dependencySet.add(DependencyType.get(desiredDependency.charAt(0)));
}
return dependencySet;
}
return DependencyTypeSet.allOf();
}
/**
* Load configurations from all the sources.
*
* @param args command line arguments
* @return True if mxmlc should continue with compilation.
*/
protected boolean configure(final String[] args)
{
projectConfigurator = createConfigurator();
try
{
ConfigurationPathResolver resolver = new ConfigurationPathResolver(System.getProperty("user.dir"));
projectConfigurator.setConfigurationPathResolver(resolver);
projectConfigurator.setConfiguration(args, getConfigurationDefaultVariable());
projectConfigurator.applyToProject(project);
problems = new ProblemQuery(projectConfigurator.getCompilerProblemSettings());
// Get the configuration and configBuffer which are now initialized.
config = (DependencyConfiguration)projectConfigurator.getConfiguration();
Messages.setLocale(config.getToolsLocale());
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<ConfigurationValue> helpVar = configBuffer.getVar("help");
if (helpVar != null)
{
processHelp(helpVar);
return false;
}
for (String fileName : projectConfigurator.getLoadedConfigurationFiles())
{
println(Messages.getString("MXMLC.Loading_configuration_format",
Collections.<String,Object>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 we have configuration errors then exit before trying to
// validate the target.
if (problems.hasErrors())
return false;
return true;
}
catch (Exception e)
{
final ICompilerProblem problem = new ConfigurationProblem(null, -1, -1, -1, -1, e.getMessage());
problems.add(problem);
return false;
}
}
/**
* 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);
println(getStartMessage());
println(usages);
}
/**
* Print a message.
*
* @param msg Message text.
*/
protected void println(final String msg)
{
System.out.println(msg);
}
/**
* Get the start up message that contains the program name with the
* copyright notice.
*
* @return the start up message.
*/
private String getStartMessage()
{
// This must not be localized.
String message = "SWC Dependency utility (swcdepends)" + NEWLINE +
VersionInfo.buildMessage() + NEWLINE;
return message;
}
private String getConfigurationDefaultVariable()
{
return DEFAULT_VAR;
}
private Configurator createConfigurator()
{
return new Configurator(DependencyConfiguration.class);
}
/**
* @return always "swcdepends"
*/
private String getProgramName()
{
return "swcdepends";
}
/**
* dependency.* configuration options
*
*/
public static class DependencyConfiguration extends Configuration
{
//
// 'show-external-classes' option
//
// Should we show the external scripts
//
private boolean showExterns = false;
public boolean getShowExterns()
{
return showExterns;
}
@Config
@Mapping({"dependency", "show-external-classes"})
public void setShowExternalClasses(ConfigurationValue cv, boolean showExterns) throws ConfigurationException
{
this.showExterns = showExterns;
}
//
// 'show-dependency-list' option
//
// Should swc dependencies be shown in addition to the dependency
// order?
//
private boolean showDependencyList = true;
public boolean getShowDependencyList()
{
return showDependencyList;
}
@Config
@Mapping({"dependency", "show-dependency-list"})
public void setShowDependencyList(ConfigurationValue cv, boolean showSwcDependencies) throws ConfigurationException
{
this.showDependencyList = showSwcDependencies;
}
//
// 'show-types' option
//
// Should we show the external scripts
//
private boolean showTypes = false;
public boolean getShowTypes()
{
return showTypes;
}
@Config
@Mapping({"dependency", "show-types"})
@SoftPrerequisites("show-external-classes")
public void setShowTypes(ConfigurationValue cv, boolean showTypes) throws ConfigurationException
{
this.showTypes = showTypes;
// if showTypes is set, then turn on show-external-classes show the types will be seen.
if (showTypes)
showExterns = true;
}
//
// 'types' option
//
private List<String> desiredTypes = new LinkedList<String>();
public List<String> getDesiredScriptDependencyTypes()
{
return desiredTypes;
}
@Config(allowMultiple=true)
@Mapping({"dependency", "types"})
@Arguments("type")
@InfiniteArguments
public void setTypes( ConfigurationValue cfgval, String[] types ) throws ConfigurationException
{
for (int i = 0; i < types.length; ++i)
{
desiredTypes.add( types[i] );
}
}
//
// 'show-swcs' option
//
// Filter which SWCs to show in the dependency order.
//
private List<String> showSwcs = new LinkedList<String>();
public List<String> getShowSwcs()
{
return showSwcs;
}
@Config(allowMultiple = true)
@Mapping({"dependency", "show-swcs"})
@Arguments("swc-name")
@InfiniteArguments
public void setShowSwcs(ConfigurationValue cfgval, String[] swcs) throws ConfigurationException
{
for (int i = 0; i < swcs.length; ++i)
{
showSwcs.add(swcs[i]);
}
}
//
// 'show-swcs' option
//
// Filter which SWCs to show in the "dependent SWCs" list.
//
private List<String> showDependentSwcs = new LinkedList<String>();
public List<String> getShowDependentSwcs()
{
return showDependentSwcs;
}
@Config(allowMultiple = true)
@Mapping({"dependency", "show-dependent-swcs"})
@Arguments("swc-name")
@InfiniteArguments
public void setShowDependentSwcs(ConfigurationValue cfgval, String[] swcs) throws ConfigurationException
{
for (int i = 0; i < swcs.length; ++i)
{
showDependentSwcs.add(swcs[i]);
}
}
//
// 'minimize-dependency-set' option
//
// Removes a SWC from the dependency set if the scripts resolved in a SWC are a subset of the scripts resolved in another dependent SWC.
// No longer supported, the dependency set is always minimized.
//
@Config(removed=true)
@Mapping({"dependency", "minimize-dependency-set"})
public void setMinimizeDependencySet(ConfigurationValue cv, boolean minimumSet) throws ConfigurationException
{
// leave configuration option here so old scripts do not error.
}
//
// -dependency-report option
//
// Takes a path that specifies a filename where an xml version of the report
// is written.
private String dependencyReportFileName;
public File getDependencyReport()
{
return dependencyReportFileName != null ? new File(dependencyReportFileName) : null;
}
@Config()
@Mapping({"dependency", "dependency-report"})
@Arguments("filename")
public void setDependencyReport(ConfigurationValue cv, String filename)
{
this.dependencyReportFileName = getOutputPath(cv, filename);
}
}
}