/* * * 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.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.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.DependencyType; import org.apache.flex.compiler.common.DependencyTypeSet; 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.annotations.Arguments; import org.apache.flex.compiler.internal.config.annotations.Config; import org.apache.flex.compiler.internal.config.annotations.InfiniteArguments; import org.apache.flex.compiler.internal.config.annotations.Mapping; import org.apache.flex.compiler.internal.config.annotations.SoftPrerequisites; import org.apache.flex.compiler.internal.config.localization.LocalizationManager; import org.apache.flex.compiler.internal.projects.FlexProject; import org.apache.flex.compiler.internal.projects.LibraryDependencyGraph; import org.apache.flex.compiler.internal.targets.SWFTarget; import org.apache.flex.compiler.internal.workspaces.Workspace; import org.apache.flex.compiler.problems.ConfigurationProblem; import org.apache.flex.compiler.problems.FileIOProblem; import org.apache.flex.compiler.problems.ICompilerProblem; import org.apache.flex.compiler.problems.InternalCompilerProblem; import org.apache.flex.compiler.targets.ITargetSettings; import org.apache.flex.compiler.units.ICompilationUnit; import org.apache.flex.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.flex.compiler.internal.config.configuration"; protected Workspace workspace; protected FlexProject 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 FlexProject(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); } } }