http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/b568782b/compiler.jx/src/org/apache/flex/compiler/internal/codegen/mxml/vf2js/MXMLVF2JSPublisher.java ---------------------------------------------------------------------- diff --git a/compiler.jx/src/org/apache/flex/compiler/internal/codegen/mxml/vf2js/MXMLVF2JSPublisher.java b/compiler.jx/src/org/apache/flex/compiler/internal/codegen/mxml/vf2js/MXMLVF2JSPublisher.java new file mode 100644 index 0000000..4a0f6cc --- /dev/null +++ b/compiler.jx/src/org/apache/flex/compiler/internal/codegen/mxml/vf2js/MXMLVF2JSPublisher.java @@ -0,0 +1,505 @@ +/* + * + * 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.compiler.internal.codegen.mxml.vf2js; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Scanner; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.io.filefilter.DirectoryFileFilter; +import org.apache.commons.io.filefilter.FileFileFilter; +import org.apache.commons.io.filefilter.FileFilterUtils; +import org.apache.commons.io.filefilter.IOFileFilter; +import org.apache.commons.io.filefilter.RegexFileFilter; +import org.apache.flex.compiler.clients.problems.ProblemQuery; +import org.apache.flex.compiler.codegen.js.IJSPublisher; +import org.apache.flex.compiler.config.Configuration; +import org.apache.flex.compiler.internal.codegen.js.JSSharedData; +import org.apache.flex.compiler.internal.codegen.js.goog.JSGoogPublisher; +import org.apache.flex.compiler.internal.driver.js.goog.JSGoogConfiguration; +import org.apache.flex.compiler.internal.graph.GoogDepsWriter; +import org.apache.flex.compiler.internal.projects.FlexJSProject; +import org.apache.flex.compiler.utils.JSClosureCompilerWrapper; + +public class MXMLVF2JSPublisher extends JSGoogPublisher implements + IJSPublisher +{ + + public static final String FLEXJS_OUTPUT_DIR_NAME = "bin"; + public static final String FLEXJS_INTERMEDIATE_DIR_NAME = "js-debug"; + public static final String FLEXJS_RELEASE_DIR_NAME = "js-release"; + + class DependencyRecord + { + String path; + String deps; + String line; + int lineNumber; + } + + class DependencyLineComparator implements Comparator<DependencyRecord> { + @Override + public int compare(DependencyRecord o1, DependencyRecord o2) { + return new Integer(o1.lineNumber).compareTo(o2.lineNumber); + } + } + + public MXMLVF2JSPublisher(Configuration config, FlexJSProject project) + { + super(config); + + this.isMarmotinniRun = ((JSGoogConfiguration) configuration) + .getMarmotinni() != null; + this.outputPathParameter = ((JSGoogConfiguration) configuration) + .getOutput(); + this.useStrictPublishing = ((JSGoogConfiguration) configuration) + .getStrictPublish(); + + this.project = project; + } + + private FlexJSProject project; + + private boolean isMarmotinniRun; + private String outputPathParameter; + private boolean useStrictPublishing; + + @Override + public File getOutputFolder() + { + // (erikdebruin) - If there is a -marmotinni switch, we want + // the output redirected to the directory it specifies. + // - If there is an -output switch, use that path as the + // output parent folder. + if (isMarmotinniRun) + { + outputParentFolder = new File( + ((JSGoogConfiguration) configuration).getMarmotinni()); + } + else if (outputPathParameter != null) + { + outputParentFolder = new File(outputPathParameter); + // FB usually specified -output <project-path>/bin-release/app.swf + if (outputPathParameter.contains(".swf")) + outputParentFolder = outputParentFolder.getParentFile().getParentFile(); + } + else + { + outputParentFolder = new File( + configuration.getTargetFileDirectory()).getParentFile(); + } + + outputParentFolder = new File(outputParentFolder, + FLEXJS_OUTPUT_DIR_NAME); + + outputFolder = new File(outputParentFolder, File.separator + + FLEXJS_INTERMEDIATE_DIR_NAME); + + // (erikdebruin) Marmotinni handles file management, so we + // bypass the setup. + if (!isMarmotinniRun) + setupOutputFolder(); + + return outputFolder; + } + + @Override + public boolean publish(ProblemQuery problems) throws IOException + { + boolean ok = true; + boolean subsetGoog = true; + + final String intermediateDirPath = outputFolder.getPath(); + final File intermediateDir = new File(intermediateDirPath); + File srcDir = new File(configuration.getTargetFile()); + srcDir = srcDir.getParentFile(); + + final String projectName = FilenameUtils.getBaseName(configuration + .getTargetFile()); + final String outputFileName = projectName + + "." + JSSharedData.OUTPUT_EXTENSION; + + File releaseDir = new File(outputParentFolder, FLEXJS_RELEASE_DIR_NAME); + final String releaseDirPath = releaseDir.getPath(); + + if (!isMarmotinniRun) + { + if (releaseDir.exists()) + org.apache.commons.io.FileUtils.deleteQuietly(releaseDir); + + releaseDir.mkdirs(); + } + + final String closureLibDirPath = ((JSGoogConfiguration) configuration) + .getClosureLib(); + final String closureGoogSrcLibDirPath = closureLibDirPath + + "/closure/goog/"; + final String closureGoogTgtLibDirPath = intermediateDirPath + + "/library/closure/goog"; + final String depsSrcFilePath = intermediateDirPath + + "/library/closure/goog/deps.js"; + final String depsTgtFilePath = intermediateDirPath + "/deps.js"; + final String projectIntermediateJSFilePath = intermediateDirPath + + File.separator + outputFileName; + final String projectReleaseJSFilePath = releaseDirPath + + File.separator + outputFileName; + + appendExportSymbol(projectIntermediateJSFilePath, projectName); + appendEncodedCSS(projectIntermediateJSFilePath, projectName); + + if (!subsetGoog) + { + // (erikdebruin) We need to leave the 'goog' files and dependencies well + // enough alone. We copy the entire library over so the + // 'goog' dependencies will resolve without our help. + FileUtils.copyDirectory(new File(closureGoogSrcLibDirPath), new File(closureGoogTgtLibDirPath)); + } + + JSClosureCompilerWrapper compilerWrapper = new JSClosureCompilerWrapper(); + + GoogDepsWriter gdw = new GoogDepsWriter(intermediateDir, projectName, (JSGoogConfiguration) configuration); + try + { + StringBuilder depsFileData = new StringBuilder(); + ok = gdw.generateDeps(problems, depsFileData); + if (!subsetGoog) + { + writeFile(depsTgtFilePath, depsFileData.toString(), false); + } + else + { + String s = depsFileData.toString(); + int c = s.indexOf("'goog."); + ArrayList<String> googreqs = new ArrayList<String>(); + while (c != -1) + { + int c2 = s.indexOf("'", c + 1); + String googreq = s.substring(c, c2 + 1); + googreqs.add(googreq); + c = s.indexOf("'goog.", c2); + } + HashMap<String, DependencyRecord> defmap = new HashMap<String, DependencyRecord>(); + // read in goog's deps.js + FileInputStream fis = new FileInputStream(closureGoogSrcLibDirPath + "/deps.js"); + Scanner scanner = new Scanner(fis, "UTF-8"); + String addDependency = "goog.addDependency('"; + int currentLine = 0; + while (scanner.hasNextLine()) + { + String googline = scanner.nextLine(); + if (googline.indexOf(addDependency) == 0) + { + int c1 = googline.indexOf("'", addDependency.length() + 1); + String googpath = googline.substring(addDependency.length(), c1); + String googdefs = googline.substring(googline.indexOf("[") + 1, googline.indexOf("]")); + String googdeps = googline.substring(googline.lastIndexOf("[") + 1, googline.lastIndexOf("]")); + String[] thedefs = googdefs.split(","); + DependencyRecord deprec = new DependencyRecord(); + deprec.path = googpath; + deprec.deps = googdeps; + deprec.line = googline; + deprec.lineNumber = currentLine; + for (String def : thedefs) + { + def = def.trim(); + defmap.put(def, deprec); + } + } + currentLine++; + } + // (erikdebruin) Prevent 'Resource leak' warning on line 212: + scanner.close(); + ArrayList<DependencyRecord> subsetdeps = new ArrayList<DependencyRecord>(); + HashMap<String, String> gotgoog = new HashMap<String, String>(); + for (String req : googreqs) + { + DependencyRecord deprec = defmap.get(req); + // if we've already processed this file, skip + if (!gotgoog.containsKey(deprec.path)) + { + gotgoog.put(deprec.path, null); + subsetdeps.add(deprec); + addDeps(subsetdeps, gotgoog, defmap, deprec.deps); + } + } + // now we should have the subset of files we need in the order needed + StringBuilder sb = new StringBuilder(); + sb.append("goog.addDependency('base.js', ['goog'], []);\n"); + File file = new File(closureGoogSrcLibDirPath + "/base.js"); + FileUtils.copyFileToDirectory(file, new File(closureGoogTgtLibDirPath)); + compilerWrapper.addJSSourceFile(file.getCanonicalPath()); + Collections.sort(subsetdeps, new DependencyLineComparator()); + for (DependencyRecord subsetdeprec : subsetdeps) + { + sb.append(subsetdeprec.line + "\n"); + } + writeFile(depsTgtFilePath, sb.toString() + depsFileData.toString(), false); + // copy the required files + for (String googfn : gotgoog.keySet()) + { + file = new File(closureGoogSrcLibDirPath + File.separator + googfn); + String dir = closureGoogTgtLibDirPath; + if (googfn.contains("/")) + { + dir += File.separator + googfn.substring(0, googfn.lastIndexOf("/")); + } + FileUtils.copyFileToDirectory(file, new File(dir)); + compilerWrapper.addJSSourceFile(file.getCanonicalPath()); + } + } + } + catch (InterruptedException e) + { + e.printStackTrace(); + return false; + } + + IOFileFilter pngSuffixFilter = FileFilterUtils.and(FileFileFilter.FILE, + FileFilterUtils.suffixFileFilter(".png")); + IOFileFilter gifSuffixFilter = FileFilterUtils.and(FileFileFilter.FILE, + FileFilterUtils.suffixFileFilter(".gif")); + IOFileFilter jpgSuffixFilter = FileFilterUtils.and(FileFileFilter.FILE, + FileFilterUtils.suffixFileFilter(".jpg")); + IOFileFilter assetFiles = FileFilterUtils.or(pngSuffixFilter, + jpgSuffixFilter, gifSuffixFilter); + + FileUtils.copyDirectory(srcDir, intermediateDir, assetFiles); + FileUtils.copyDirectory(srcDir, releaseDir, assetFiles); + + File srcDeps = new File(depsSrcFilePath); + + writeHTML("intermediate", projectName, intermediateDirPath, gdw.additionalHTML); + writeHTML("release", projectName, releaseDirPath, gdw.additionalHTML); + writeCSS(projectName, intermediateDirPath); + writeCSS(projectName, releaseDirPath); + + if (!subsetGoog) + { + // (erikdebruin) add 'goog' files + Collection<File> files = org.apache.commons.io.FileUtils.listFiles(new File( + closureGoogTgtLibDirPath), new RegexFileFilter("^.*(\\.js)"), + DirectoryFileFilter.DIRECTORY); + for (File file : files) + { + compilerWrapper.addJSSourceFile(file.getCanonicalPath()); + } + } + + // (erikdebruin) add project files + for (String filePath : gdw.filePathsInOrder) + { + compilerWrapper.addJSSourceFile( + new File(filePath).getCanonicalPath()); + } + + compilerWrapper.setOptions( + projectReleaseJSFilePath, useStrictPublishing); + + // (erikdebruin) Include the 'goog' deps to allow the compiler to resolve + // dependencies. + compilerWrapper.addJSSourceFile( + closureGoogSrcLibDirPath + File.separator + "deps.js"); + + List<String> externs = ((JSGoogConfiguration)configuration).getExternalJSLib(); + for (String extern : externs) + { + compilerWrapper.addJSExternsFile(extern); + } + + compilerWrapper.targetFilePath = projectReleaseJSFilePath; + compilerWrapper.compile(); + + appendSourceMapLocation(projectReleaseJSFilePath, projectName); + + if (!isMarmotinniRun) + { + String allDeps = ""; + if (!subsetGoog) + allDeps += FileUtils.readFileToString(srcDeps); + allDeps += FileUtils.readFileToString(new File(depsTgtFilePath)); + + FileUtils.writeStringToFile(srcDeps, allDeps); + + org.apache.commons.io.FileUtils.deleteQuietly(new File(depsTgtFilePath)); + } + + if (ok) + System.out.println("The project '" + + projectName + + "' has been successfully compiled and optimized."); + + return true; + } + + private void addDeps(ArrayList<DependencyRecord> subsetdeps, HashMap<String, String> gotgoog, + HashMap<String, DependencyRecord> defmap, String deps) + { + if (deps.length() == 0) + return; + + String[] deplist = deps.split(","); + for (String dep : deplist) + { + dep = dep.trim(); + DependencyRecord deprec = defmap.get(dep); + if (!gotgoog.containsKey(deprec.path)) + { + gotgoog.put(deprec.path, null); + // put addDependencyLine in subset file + subsetdeps.add(deprec); + addDeps(subsetdeps, gotgoog, defmap, deprec.deps); + } + } + } + + private void appendExportSymbol(String path, String projectName) + throws IOException + { + StringBuilder appendString = new StringBuilder(); + appendString + .append("\n\n// Ensures the symbol will be visible after compiler renaming.\n"); + appendString.append("goog.exportSymbol('"); + appendString.append(projectName); + appendString.append("', "); + appendString.append(projectName); + appendString.append(");\n"); + writeFile(path, appendString.toString(), true); + } + + private void appendEncodedCSS(String path, String projectName) + throws IOException + { + StringBuilder appendString = new StringBuilder(); + appendString.append("\n\n"); + appendString.append(projectName); + appendString.append(".prototype.cssData = ["); + String s = project.cssEncoding; + int reqidx = s.indexOf("goog.require"); + if (reqidx != -1) + { + String reqs = s.substring(reqidx); + s = s.substring(0, reqidx - 1); + String fileData = readCode(new File(path)); + reqidx = fileData.indexOf("goog.require"); + String after = fileData.substring(reqidx); + String before = fileData.substring(0, reqidx - 1); + s = before + reqs + after + appendString.toString() + s; + writeFile(path, s, false); + } + else + { + appendString.append(s); + writeFile(path, appendString.toString(), true); + } + } + + protected String readCode(File file) + { + String code = ""; + try + { + BufferedReader in = new BufferedReader(new InputStreamReader( + new FileInputStream(file), "UTF8")); + + String line = in.readLine(); + + while (line != null) + { + code += line + "\n"; + line = in.readLine(); + } + code = code.substring(0, code.length() - 1); + + in.close(); + } + catch (Exception e) + { + // nothing to see, move along... + } + + return code; + } + + private void writeHTML(String type, String projectName, String dirPath, List<String> additionalHTML) + throws IOException + { + StringBuilder htmlFile = new StringBuilder(); + htmlFile.append("<!DOCTYPE html>\n"); + htmlFile.append("<html>\n"); + htmlFile.append("<head>\n"); + htmlFile.append("\t<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge,chrome=1\">\n"); + htmlFile.append("\t<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n"); + htmlFile.append("\t<link rel=\"stylesheet\" type=\"text/css\" href=\"" + + projectName + ".css\">\n"); + + for (String s : additionalHTML) + htmlFile.append(s + "\n"); + + if (type == "intermediate") + { + htmlFile.append("\t<script type=\"text/javascript\" src=\"./library/closure/goog/base.js\"></script>\n"); + htmlFile.append("\t<script type=\"text/javascript\">\n"); + htmlFile.append("\t\tgoog.require(\""); + htmlFile.append(projectName); + htmlFile.append("\");\n"); + htmlFile.append("\t</script>\n"); + } + else + { + htmlFile.append("\t<script type=\"text/javascript\" src=\"./"); + htmlFile.append(projectName); + htmlFile.append(".js\"></script>\n"); + } + + htmlFile.append("</head>\n"); + htmlFile.append("<body>\n"); + htmlFile.append("\t<script type=\"text/javascript\">\n"); + htmlFile.append("\t\tnew "); + htmlFile.append(projectName); + htmlFile.append("()"); + htmlFile.append(".start();\n"); + htmlFile.append("\t</script>\n"); + htmlFile.append("</body>\n"); + htmlFile.append("</html>"); + + writeFile(dirPath + File.separator + "index.html", htmlFile.toString(), + false); + } + + private void writeCSS(String projectName, String dirPath) + throws IOException + { + StringBuilder cssFile = new StringBuilder(); + cssFile.append(project.cssDocument); + + writeFile(dirPath + File.separator + projectName + ".css", + cssFile.toString(), false); + } +}
http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/b568782b/compiler.jx/src/org/apache/flex/compiler/internal/driver/mxml/vf2js/MXMLVF2JSBackend.java ---------------------------------------------------------------------- diff --git a/compiler.jx/src/org/apache/flex/compiler/internal/driver/mxml/vf2js/MXMLVF2JSBackend.java b/compiler.jx/src/org/apache/flex/compiler/internal/driver/mxml/vf2js/MXMLVF2JSBackend.java new file mode 100644 index 0000000..8d95230 --- /dev/null +++ b/compiler.jx/src/org/apache/flex/compiler/internal/driver/mxml/vf2js/MXMLVF2JSBackend.java @@ -0,0 +1,121 @@ +/* + * + * 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.compiler.internal.driver.mxml.vf2js; + +import java.io.FilterWriter; +import java.util.List; + +import org.apache.flex.compiler.codegen.IDocEmitter; +import org.apache.flex.compiler.codegen.as.IASEmitter; +import org.apache.flex.compiler.codegen.js.IJSEmitter; +import org.apache.flex.compiler.codegen.js.IJSWriter; +import org.apache.flex.compiler.codegen.mxml.IMXMLEmitter; +import org.apache.flex.compiler.config.Configurator; +import org.apache.flex.compiler.driver.IBackend; +import org.apache.flex.compiler.internal.codegen.js.flexjs.JSFlexJSEmitter; +import org.apache.flex.compiler.internal.codegen.js.goog.JSGoogDocEmitter; +import org.apache.flex.compiler.internal.codegen.mxml.MXMLBlockWalker; +import org.apache.flex.compiler.internal.codegen.mxml.MXMLWriter; +import org.apache.flex.compiler.internal.codegen.mxml.flexjs.MXMLFlexJSBlockWalker; +import org.apache.flex.compiler.internal.codegen.mxml.vf2js.MXMLVF2JSEmitter; +import org.apache.flex.compiler.internal.driver.js.goog.JSGoogConfiguration; +import org.apache.flex.compiler.internal.driver.mxml.MXMLBackend; +import org.apache.flex.compiler.internal.targets.FlexJSTarget; +import org.apache.flex.compiler.internal.targets.JSTarget; +import org.apache.flex.compiler.internal.visitor.as.ASNodeSwitch; +import org.apache.flex.compiler.internal.visitor.mxml.MXMLNodeSwitch; +import org.apache.flex.compiler.problems.ICompilerProblem; +import org.apache.flex.compiler.projects.IASProject; +import org.apache.flex.compiler.targets.ITargetProgressMonitor; +import org.apache.flex.compiler.targets.ITargetSettings; +import org.apache.flex.compiler.tree.mxml.IMXMLFileNode; +import org.apache.flex.compiler.units.ICompilationUnit; +import org.apache.flex.compiler.visitor.IBlockVisitor; +import org.apache.flex.compiler.visitor.IBlockWalker; +import org.apache.flex.compiler.visitor.mxml.IMXMLBlockWalker; + +/** + * A concrete implementation of the {@link IBackend} API where the + * {@link MXMLBlockWalker} is used to traverse the {@link IMXMLFileNode} AST. + * + * @author Erik de Bruin + */ +public class MXMLVF2JSBackend extends MXMLBackend +{ + + @Override + public Configurator createConfigurator() + { + return new Configurator(JSGoogConfiguration.class); + } + + @Override + public IMXMLEmitter createMXMLEmitter(FilterWriter out) + { + return new MXMLVF2JSEmitter(out); + } + + @Override + public IMXMLBlockWalker createMXMLWalker(IASProject project, + List<ICompilerProblem> errors, IMXMLEmitter mxmlEmitter, + IASEmitter asEmitter, IBlockWalker asBlockWalker) + { + MXMLBlockWalker walker = new MXMLFlexJSBlockWalker(errors, project, + mxmlEmitter, asEmitter, asBlockWalker); + + ASNodeSwitch asStrategy = new ASNodeSwitch( + (IBlockVisitor) asBlockWalker); + walker.setASStrategy(asStrategy); + + MXMLNodeSwitch mxmlStrategy = new MXMLNodeSwitch(walker); + walker.setMXMLStrategy(mxmlStrategy); + + return walker; + } + + @Override + public IDocEmitter createDocEmitter(IASEmitter emitter) + { + return new JSGoogDocEmitter((IJSEmitter) emitter); + } + + @Override + public IJSEmitter createEmitter(FilterWriter out) + { + IJSEmitter emitter = new JSFlexJSEmitter(out); + emitter.setDocEmitter(createDocEmitter(emitter)); + return emitter; + } + + @Override + public IJSWriter createMXMLWriter(IASProject project, + List<ICompilerProblem> problems, ICompilationUnit compilationUnit, + boolean enableDebug) + { + return new MXMLWriter(project, problems, compilationUnit, enableDebug); + } + + @Override + public JSTarget createTarget(IASProject project, ITargetSettings settings, + ITargetProgressMonitor monitor) + { + return new FlexJSTarget(project, settings, monitor); + } +}