| /* |
| * |
| * 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.internal.targets; |
| |
| import java.io.File; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.TreeSet; |
| |
| import org.apache.royale.compiler.css.ICSSDocument; |
| import org.apache.royale.compiler.css.ICSSManager; |
| import org.apache.royale.compiler.definitions.IDefinition; |
| import org.apache.royale.compiler.internal.css.semantics.ActivatedStyleSheets; |
| import org.apache.royale.compiler.internal.driver.js.royale.JSCSSCompilationSession; |
| import org.apache.royale.compiler.internal.projects.RoyaleJSProject; |
| import org.apache.royale.compiler.internal.units.SWCCompilationUnit; |
| import org.apache.royale.compiler.problems.FileNotFoundProblem; |
| import org.apache.royale.compiler.problems.ICompilerProblem; |
| import org.apache.royale.compiler.targets.IJSTarget; |
| import org.apache.royale.compiler.targets.ITargetProgressMonitor; |
| import org.apache.royale.compiler.targets.ITargetSettings; |
| import org.apache.royale.compiler.units.ICompilationUnit; |
| import org.apache.royale.compiler.units.ICompilationUnit.UnitType; |
| |
| import com.google.common.collect.ImmutableList; |
| |
| public class RoyaleJSTarget extends JSTarget implements IJSTarget |
| { |
| /** |
| * Initialize a JS target with the owner project and root compilation units. |
| * |
| * @param project the owner project |
| */ |
| public RoyaleJSTarget(RoyaleJSProject project, ITargetSettings targetSettings, |
| ITargetProgressMonitor progressMonitor) |
| { |
| super(project, targetSettings, progressMonitor); |
| royaleProject = project; |
| } |
| |
| private final RoyaleJSProject royaleProject; |
| |
| /////////// |
| // |
| // Copied from RoyaleAppSWFTarget.java then modified |
| // |
| /////////// |
| /** |
| * Discovers dependent compilation units from a set of root compilation |
| * units. |
| * <p> |
| * For each public visible definition in all the compilation units, if |
| * there's an applicable CSS rule, check if the CSS rule pulls in any |
| * dependencies. (i.e. embedded assets, skin classes) Add the dependencies |
| * to the list of compilation units, and check if they have any applicable |
| * CSS rules which could pull in more dependencies. Loop until we reach a |
| * stable set of compilation units. |
| * <p> |
| * CSS rules in these CSS documents can introduce class dependencies. If any |
| * candidate rule matches a class known to be linked into the target, the |
| * candidate rule's dependencies are selected for linking. Those selected |
| * dependencies will be included in the next iteration of the dependency |
| * discovery loop. |
| * <p> |
| * Once a CSS document is "activated", it stays in this collection and its |
| * rules are tested against all classes introduced in the |
| * "dependency discovery loop". |
| * <p> |
| * For example: Suppose in project P, there are "A.as" and "styles.css", and |
| * class "A" is selected for linking.<br> |
| * In "styles.css", there're two rules: |
| * |
| * <pre> |
| * A { xSkin : ClassReference("B"); } |
| * K { xSkin : ClassReference("L"); } |
| * </pre> |
| * |
| * In the 1st iteration, rule "A" is matched, which introduces dependency on |
| * "B". <br> |
| * "B" is defined in a SWC library "myskins.swc", and there's a |
| * "defaults.css" in "myskins.swc". |
| * |
| * <pre> |
| * B { ySkin : ClassReference("C"); } |
| * A { ySkin : ClassReference("D"); } |
| * K { ySkin : ClassReference("M"); } |
| * </pre> |
| * |
| * In the 2nd iteration, rule "A" and rule "B" in "defaults.css" are |
| * matched, which introduces dependencies on "C" and "D". However, "K" has |
| * not been selected so far, so "L" and "M" are not selected. |
| * <p> |
| * Now imagine, "C" is defined in "anotherSkin.swc", and there's a |
| * "defaults.css" in "anotherSkin.swc" as well. |
| * |
| * <pre> |
| * C { zSkin : ClassReference("K"); } |
| * </pre> |
| * |
| * In the 3rd iteration, rule "C" is matched because "C" was selected in the |
| * previous iteration, which makes "K" the selected dependency. |
| * <p> |
| * At the beginning of the 4th iteration, the classes selected for linking |
| * are "A", "B", "C", "D" and "K". In this iteration, these classes will be |
| * tested against all the "activated style sheets" - "styles.css" and two |
| * "defaults.css" in "myskins.swc" and "anotherSkin.swc". "K" rules in |
| * "styles.css" and "myskins.swc" are now matched, which introduces new |
| * dependencies on "L" and "M". |
| * <p> |
| * In the 5th iteration, the classes to link are "A", "B", "C", "D", "K", |
| * "L" and "M". They are tested against all the activate CSS. No more |
| * dependencies are introduced by CSS rules, making it the last iteration of |
| * the loop. |
| * |
| * @param compilationUnits Collection of compilation units known to be |
| * linked in. |
| * @param problems Collection of {@link ICompilerProblem}'s that the each |
| * found {@link ICompilationUnit} is added to. |
| * @return All compilation units which were compiled |
| * @throws InterruptedException |
| */ |
| @Override |
| protected Set<ICompilationUnit> findAllCompilationUnitsToLink(final Collection<ICompilationUnit> compilationUnits, |
| final Collection<ICompilerProblem> problems) |
| throws InterruptedException |
| { |
| JSCSSCompilationSession cssCompilationSession = (JSCSSCompilationSession) royaleProject.getCSSCompilationSession(); |
| cssCompilationSession.setKeepAllTypeSelectors(targetSettings.keepAllTypeSelectors()); |
| cssCompilationSession.setExcludeDefaultsCSSFiles(targetSettings.getExcludeDefaultsCSSFiles()); |
| |
| // Performance heuristic: let's start compilation on all of the compilation |
| // units we know about up front. This is particularly useful on SWC projects where |
| // we are using "include-sources" to force a bunch of possibly unrelated classes to be |
| // compiled. |
| // Note that by putting the code here, we will start aggressive early compilation for |
| // all projects. Limited data so far shows this this is a good thing. But down the |
| // road it's possible that we might find tests cases that force us to reconsider / refine |
| // this "shotgun" approach. |
| for (ICompilationUnit cu : compilationUnits) |
| cu.startBuildAsync(getTargetType()); |
| |
| |
| assert compilationUnits != null : "compilation units can't be null"; |
| assert problems != null : "problems can't be null"; |
| |
| // Collection of all the compilation units that will be linked in the target. |
| final Set<ICompilationUnit> allCompilationUnitsInTarget = |
| new HashSet<ICompilationUnit>(compilationUnits); |
| |
| // Collection of all the referenced CSS. Once a CSS is activated, it's always |
| // included in the dependency checking, even none of its rules are matched. |
| final ActivatedStyleSheets activatedStyleSheets = new ActivatedStyleSheets(); |
| |
| final ICSSManager cssManager = royaleProject.getCSSManager(); |
| |
| collectThemes(cssManager, activatedStyleSheets, problems); |
| collectDefaultCSS(cssManager, activatedStyleSheets, problems); |
| |
| // The dependency discovery loop. |
| // It terminates when no more dependencies are introduced by CSS. |
| boolean done = false; |
| while (!done) |
| { |
| //LoggingProfiler.onStartIteration(); |
| |
| // Get all non-CSS dependencies. |
| final Set<ICompilationUnit> dependencies = |
| getDependentCompilationUnits(allCompilationUnitsInTarget, problems); |
| //LoggingProfiler.onCompilationUnitDependenciesChanged(allCompilationUnitsInTarget, dependencies); |
| allCompilationUnitsInTarget.addAll(dependencies); |
| |
| // Get all activated defaults.css from SWCs. |
| final Map<ICSSDocument, File> activatedDefaultCSSList = |
| getAllDefaultCSS(cssManager, allCompilationUnitsInTarget); |
| for (final Map.Entry<ICSSDocument, File> entry : activatedDefaultCSSList.entrySet()) |
| { |
| activatedStyleSheets.addLibraryCSS(entry.getKey(), entry.getValue().getAbsolutePath()); |
| } |
| //LoggingProfiler.onDefaultsCSSCollectionChanged(activatedStyleSheets); |
| |
| // Get all dependencies introduced by defaults.css from SWCs. |
| final ImmutableList<IDefinition> definitions = |
| Target.getAllExternallyVisibleDefinitions(allCompilationUnitsInTarget); |
| final Collection<ICompilationUnit> cssDependencies = new HashSet<ICompilationUnit>(); |
| // activate rules in user specified CSS |
| for (ICSSDocument cssDocument : cssCompilationSession.cssDocuments) |
| { |
| // Side-effects of this method: |
| // 1. Resolve all type selectors in the CSS model to IClassDefinition definitions. |
| // 2. Activate CSS rules whose subject is in the definition set. |
| final Collection<ICompilationUnit> dependentCUListFromCSS = |
| cssManager.getDependentCompilationUnitsFromCSS( |
| cssCompilationSession, |
| cssDocument, |
| definitions, |
| problems); |
| cssDependencies.addAll(dependentCUListFromCSS); |
| } |
| for (final ICSSDocument cssDocument : activatedStyleSheets.all()) |
| { |
| // Side-effects of this method: |
| // 1. Resolve all type selectors in the CSS model to IClassDefinition definitions. |
| // 2. Activate CSS rules whose subject is in the definition set. |
| final Collection<ICompilationUnit> dependentCUListFromCSS = |
| cssManager.getDependentCompilationUnitsFromCSS( |
| cssCompilationSession, |
| cssDocument, |
| definitions, |
| problems); |
| cssDependencies.addAll(dependentCUListFromCSS); |
| //LoggingProfiler.onCSSDependenciesChanged(dependentCUListFromCSS); |
| } |
| |
| // If there's more dependencies introduced by CSS, the loop continues. |
| done = !allCompilationUnitsInTarget.addAll(cssDependencies); |
| } |
| |
| // add to front so user specified css overrides defaults |
| cssCompilationSession.cssDocuments.addAll(0, activatedStyleSheets.sort()); |
| |
| return super.findAllCompilationUnitsToLink(compilationUnits, problems); |
| } |
| |
| /** |
| * Collect CSS from themes. |
| */ |
| private void collectThemes( |
| final ICSSManager cssManager, |
| final ActivatedStyleSheets activatedStyleSheets, |
| final Collection<ICompilerProblem> problems) |
| { |
| final Collection<ICSSDocument> cssFromThemes = cssManager.getCSSFromThemes(problems); |
| for (final ICSSDocument themeCSS : cssFromThemes) |
| { |
| // Theme files are sorted by declaration order instead of filenames, so we needn't |
| // their filenames here. |
| activatedStyleSheets.addThemeCSS(themeCSS); |
| } |
| } |
| |
| /** |
| * Collect CSS from 'defaults-css-files' configuration option. |
| */ |
| private void collectDefaultCSS( |
| final ICSSManager cssManager, |
| final ActivatedStyleSheets activatedStyleSheets, |
| final Collection<ICompilerProblem> problems) |
| { |
| for (final String defaultsCSSPath : getTargetSettings().getDefaultsCSSFiles()) |
| { |
| final ICSSDocument defaultsCSSModel = cssManager.getCSS(defaultsCSSPath); |
| if (defaultsCSSModel == null) |
| problems.add(new FileNotFoundProblem(defaultsCSSPath)); |
| else |
| activatedStyleSheets.addDefaultCSS(defaultsCSSModel); |
| } |
| } |
| |
| /** |
| * Find all the {@link SWCCompilationUnit}'s, and return the default CSS |
| * model in the SWCs. |
| * |
| * @param cssManager Project-level CSS manager. |
| * @param compilationUnits All the compilation units. Non-SWC compilation |
| * units are ignored. |
| * @return Model of the default CSS in the SWCs. The map keys are CSS |
| * models; the values are the enclosing SWC file. |
| */ |
| private static Map<ICSSDocument, File> getAllDefaultCSS( |
| final ICSSManager cssManager, |
| final Collection<ICompilationUnit> compilationUnits) |
| { |
| assert cssManager != null : "Expected CSS manager."; |
| assert compilationUnits != null : "Expected collection of compilation units."; |
| |
| final Map<ICSSDocument, File> result = new HashMap<ICSSDocument, File>(); |
| for (final ICompilationUnit compilationUnit : compilationUnits) |
| { |
| if (compilationUnit.getCompilationUnitType() == UnitType.SWC_UNIT) |
| { |
| final File swcFile = new File(compilationUnit.getAbsoluteFilename()); |
| final ICSSDocument defaultCSS = cssManager.getDefaultCSS(swcFile); |
| if (defaultCSS != null) |
| result.put(defaultCSS, swcFile); |
| } |
| } |
| return result; |
| } |
| |
| public void collectMixinMetaData(TreeSet<String> mixinClassNames, List<ICompilationUnit> units) |
| { |
| for (ICompilationUnit unit : units) |
| { |
| try { |
| RoyaleApplicationFrame1Info.collectMixinMetaData(mixinClassNames, unit); |
| } catch (InterruptedException e) { |
| // TODO Auto-generated catch block |
| e.printStackTrace(); |
| } |
| } |
| } |
| |
| public void collectRemoteClassMetaData(Map<String, String> remoteClassAliasMap, List<ICompilationUnit> units) |
| { |
| for (ICompilationUnit unit : units) |
| { |
| try { |
| RoyaleApplicationFrame1Info.collectRemoteClassMetaData(remoteClassAliasMap, unit); |
| } catch (InterruptedException e) { |
| // TODO Auto-generated catch block |
| e.printStackTrace(); |
| } |
| } |
| } |
| |
| } |