| /* |
| * |
| * 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.clients; |
| |
| 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.ImmutableSet; |
| import com.google.common.collect.Iterables; |
| import com.google.common.collect.Lists; |
| import com.google.javascript.jscomp.*; |
| import com.google.javascript.jscomp.Compiler; |
| import org.apache.commons.io.FilenameUtils; |
| import org.apache.commons.io.output.CountingOutputStream; |
| 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.VersionInfo; |
| import org.apache.flex.compiler.config.*; |
| import org.apache.flex.compiler.config.RSLSettings.RSLAndPolicyFileURLPair; |
| import org.apache.flex.compiler.definitions.IDefinition; |
| import org.apache.flex.compiler.exceptions.ConfigurationException; |
| import org.apache.flex.compiler.exceptions.ConfigurationException.IOError; |
| import org.apache.flex.compiler.exceptions.ConfigurationException.MustSpecifyTarget; |
| import org.apache.flex.compiler.exceptions.ConfigurationException.OnlyOneSource; |
| import org.apache.flex.compiler.filespecs.IFileSpecification; |
| import org.apache.flex.compiler.internal.as.codegen.*; |
| import org.apache.flex.compiler.internal.config.localization.LocalizationManager; |
| import org.apache.flex.compiler.internal.definitions.ClassDefinition; |
| import org.apache.flex.compiler.internal.driver.IBackend; |
| import org.apache.flex.compiler.internal.driver.JSBackend; |
| import org.apache.flex.compiler.internal.driver.JSTarget; |
| import org.apache.flex.compiler.internal.graph.GoogDepsWriter; |
| import org.apache.flex.compiler.internal.graph.GraphMLWriter; |
| import org.apache.flex.compiler.internal.projects.*; |
| import org.apache.flex.compiler.internal.projects.DefinitionPriority.BasePriority; |
| import org.apache.flex.compiler.internal.resourcebundles.ResourceBundleUtils; |
| import org.apache.flex.compiler.internal.scopes.ASProjectScope; |
| import org.apache.flex.compiler.internal.scopes.ASProjectScope.DefinitionPromise; |
| import org.apache.flex.compiler.internal.targets.LinkageChecker; |
| import org.apache.flex.compiler.internal.targets.Target; |
| import org.apache.flex.compiler.internal.tree.mxml.MXMLClassDefinitionNode; |
| import org.apache.flex.compiler.internal.units.*; |
| import org.apache.flex.compiler.internal.workspaces.Workspace; |
| import org.apache.flex.compiler.problems.*; |
| import org.apache.flex.compiler.projects.ICompilerProject; |
| import org.apache.flex.compiler.targets.ISWFTarget; |
| import org.apache.flex.compiler.targets.ITarget.TargetType; |
| import org.apache.flex.compiler.targets.ITargetReport; |
| import org.apache.flex.compiler.targets.ITargetSettings; |
| import org.apache.flex.compiler.tree.as.IASNode; |
| import org.apache.flex.compiler.units.ICompilationUnit; |
| import org.apache.flex.compiler.units.ICompilationUnit.UnitType; |
| import org.apache.flex.compiler.units.requests.IFileScopeRequestResult; |
| import org.apache.flex.swc.ISWC; |
| import org.apache.flex.swf.ISWF; |
| import org.apache.flex.swf.io.ISWFWriter; |
| import org.apache.flex.swf.types.Rect; |
| import org.apache.flex.utils.ArgumentUtil; |
| import org.apache.flex.utils.FilenameNormalization; |
| |
| import java.io.*; |
| import java.util.*; |
| |
| /** |
| * The entry-point class for the FalconJS version of mxmlc. |
| */ |
| public class MXMLJSC |
| { |
| private static final String DEFAULT_VAR = "file-specs"; |
| private static final String L10N_CONFIG_PREFIX = "flex2.configuration"; |
| private static final int TWIPS_PER_PIXEL = 20; |
| |
| /* |
| * Exit code enumerations. |
| */ |
| static enum ExitCode |
| { |
| SUCCESS(0), |
| PRINT_HELP(1), |
| FAILED_WITH_PROBLEMS(2), |
| FAILED_WITH_EXCEPTIONS(3), |
| FAILED_WITH_CONFIG_PROBLEMS(4); |
| |
| ExitCode(int code) |
| { |
| this.code = code; |
| } |
| |
| final int code; |
| } |
| |
| /** |
| * Java program entry point. |
| * |
| * @param args command line arguments |
| */ |
| public static void main(final String[] args) |
| { |
| long startTime = System.nanoTime(); |
| |
| final IBackend backend = new JSBackend(); |
| final MXMLJSC mxmlc = new MXMLJSC(backend); |
| final Set<ICompilerProblem> problems = new HashSet<ICompilerProblem>(); |
| final int exitCode = mxmlc.mainNoExit(args, problems, true); |
| |
| long endTime = System.nanoTime(); |
| JSSharedData.instance.stdout((endTime - startTime) / 1e9 + " seconds"); |
| |
| System.exit(exitCode); |
| } |
| |
| public static int mainNoExit(final String[] args, List<ICompilerProblem> problemList) |
| { |
| final IBackend backend = new JSBackend(); |
| final MXMLJSC mxmlc = new MXMLJSC(backend); |
| final Set<ICompilerProblem> problems = new HashSet<ICompilerProblem>(); |
| final int exitCode = mxmlc.mainNoExit(args, problems, problemList == null); |
| if (problemList != null) |
| problemList.addAll(problems); |
| return exitCode; |
| } |
| |
| public int mainNoExit(final String[] args, Set<ICompilerProblem> problems, Boolean printProblems) |
| { |
| int exitCode = -1; |
| try |
| { |
| exitCode = _mainNoExit(ArgumentUtil.fixArgs(args), problems); |
| } |
| catch (Exception e) |
| { |
| JSSharedData.instance.stderr(e.toString()); |
| } |
| finally |
| { |
| if (problems != null && !problems.isEmpty()) |
| { |
| if (printProblems) |
| { |
| final WorkspaceProblemFormatter formatter = new WorkspaceProblemFormatter(workspace); |
| final ProblemPrinter printer = new ProblemPrinter(formatter); |
| printer.printProblems(problems); |
| } |
| } |
| } |
| return exitCode; |
| } |
| |
| /** |
| * Entry point that doesn't call <code>System.exit()</code>. This is for |
| * unit testing. |
| * |
| * @param args command line arguments |
| * @return exit code |
| */ |
| private int _mainNoExit(final String[] args, Set<ICompilerProblem> outProblems) |
| { |
| ExitCode exitCode = ExitCode.SUCCESS; |
| try |
| { |
| final boolean continueCompilation = configure(args); |
| |
| if (outProblems != null && !config.isVerbose()) |
| JSSharedData.STDOUT = JSSharedData.STDERR = null; |
| |
| if (continueCompilation) |
| { |
| compile(); |
| if (problems.hasFilteredProblems()) |
| exitCode = ExitCode.FAILED_WITH_PROBLEMS; |
| } |
| else if (problems.hasFilteredProblems()) |
| { |
| exitCode = ExitCode.FAILED_WITH_CONFIG_PROBLEMS; |
| } |
| else |
| { |
| exitCode = ExitCode.PRINT_HELP; |
| } |
| } |
| catch (Exception e) |
| { |
| if (outProblems == null) |
| JSSharedData.instance.stderr(e.getMessage()); |
| else |
| { |
| final ICompilerProblem unexpectedExceptionProblem = new UnexpectedExceptionProblem(e); |
| problems.add(unexpectedExceptionProblem); |
| } |
| exitCode = ExitCode.FAILED_WITH_EXCEPTIONS; |
| } |
| finally |
| { |
| waitAndClose(); |
| |
| if (outProblems != null && problems.hasFilteredProblems()) |
| { |
| for (ICompilerProblem problem : problems.getFilteredProblems()) |
| { |
| outProblems.add(problem); |
| } |
| } |
| } |
| return exitCode.code; |
| } |
| |
| protected MXMLJSC(IBackend backend) |
| { |
| JSSharedData.backend = backend; |
| workspace = new Workspace(); |
| project = new FlexJSProject(workspace); |
| MXMLClassDefinitionNode.GENERATED_ID_BASE = "$ID"; |
| problems = new ProblemQuery(); |
| JSSharedData.OUTPUT_EXTENSION = backend.getOutputExtension(); |
| JSSharedData.workspace = workspace; |
| asFileHandler = backend.getSourceFileHandlerInstance(); |
| } |
| |
| protected Workspace workspace; |
| protected FlexProject project; |
| protected Configuration config; |
| protected ProblemQuery problems; |
| private ConfigurationBuffer configBuffer; |
| |
| protected Configurator projectConfigurator; |
| |
| protected ICompilationUnit mainCU; |
| protected JSTarget target; |
| private ITargetSettings targetSettings; |
| private ISWF swfTarget; |
| |
| private Collection<ICompilationUnit> includedResourceBundleCompUnits; |
| protected ISourceFileHandler asFileHandler; |
| |
| /** |
| * Print a message. |
| * |
| * @param msg Message text. |
| */ |
| public void println(final String msg) |
| { |
| JSSharedData.instance.stdout(msg); |
| } |
| |
| /** |
| * Wait till the workspace to finish compilation and close. |
| */ |
| protected void waitAndClose() |
| { |
| workspace.startIdleState(); |
| try |
| { |
| workspace.close(); |
| } |
| finally |
| { |
| workspace.endIdleState(Collections.<ICompilerProject, Set<ICompilationUnit>> 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 JSSharedData.backend.createConfigurator(); |
| } |
| |
| /** |
| * 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) |
| { |
| project.getSourceCompilationUnitFactory().addHandler(asFileHandler); |
| CodeGeneratorManager.setFactory(JSGenerator.getABCGeneratorFactory()); |
| 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); |
| if (usage != null) |
| println(usage); |
| return false; |
| } |
| |
| projectConfigurator.setConfiguration(args, ICompilerSettingsConstants.FILE_SPECS_VAR); |
| projectConfigurator.applyToProject(project); |
| problems = new ProblemQuery(projectConfigurator.getCompilerProblemSettings()); |
| |
| // Get the configuration and configBuffer which are now initialized. |
| config = projectConfigurator.getConfiguration(); |
| configBuffer = projectConfigurator.getConfigurationBuffer(); |
| problems.addAll(projectConfigurator.getConfigurationProblems()); |
| |
| // Print version if "-version" is present. |
| if (configBuffer.getVar("version") != null) //$NON-NLS-1$ |
| { |
| println(VersionInfo.buildMessage() + " (" + JSSharedData.COMPILER_VERSION + ")"); |
| return false; |
| } |
| |
| // Print help if "-help" is present. |
| final List<ConfigurationValue> helpVar = configBuffer.getVar("help"); //$NON-NLS-1$ |
| if (helpVar != null) |
| { |
| processHelp(helpVar); |
| return false; |
| } |
| |
| for (String fileName : projectConfigurator.getLoadedConfigurationFiles()) |
| { |
| JSSharedData.instance.stdout("Loading configuration: " + fileName); |
| } |
| |
| if (config.isVerbose()) |
| { |
| for (final IFileSpecification themeFile : project.getThemeFiles()) |
| { |
| JSSharedData.instance.stdout(String.format("Found theme file %s", themeFile.getPath())); |
| } |
| } |
| |
| // If we have configuration errors then exit before trying to |
| // validate the target. |
| if (problems.hasErrors()) |
| return false; |
| |
| validateTargetFile(); |
| 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; |
| } |
| finally |
| { |
| // If we couldn't create a configuration, then create a default one |
| // so we can exit without throwing an exception. |
| if (config == null) |
| { |
| config = new Configuration(); |
| configBuffer = new ConfigurationBuffer(Configuration.class, Configuration.getAliases()); |
| } |
| } |
| } |
| |
| /** |
| * Validate target file. |
| * |
| * @throws MustSpecifyTarget |
| * @throws IOError |
| */ |
| 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 |
| * @throws IOException |
| * @throws InterruptedException |
| */ |
| protected boolean compile() |
| { |
| ArrayList<String> otherFiles = new ArrayList<String>(); |
| |
| boolean compilationSuccess = false; |
| try |
| { |
| setupJS(); |
| if (!setupTargetFile()) |
| return false; |
| |
| if (config.isDumpAst()) |
| dumpAST(); |
| |
| buildArtifact(); |
| |
| final File outputFile = new File(getOutputFilePath()); |
| final File outputFolder = outputFile.getParentFile(); |
| if (!outputFolder.exists()) |
| { |
| outputFolder.mkdirs(); |
| } |
| |
| if (swfTarget != null) |
| { |
| Collection<ICompilerProblem> errors = new ArrayList<ICompilerProblem>(); |
| Collection<ICompilerProblem> warnings = new ArrayList<ICompilerProblem>(); |
| |
| // Don't create a swf if there are errors unless a |
| // developer requested otherwise. |
| if (!config.getCreateTargetWithErrors()) |
| { |
| problems.getErrorsAndWarnings(errors, warnings); |
| if (errors.size() > 0) |
| return false; |
| } |
| |
| final int swfSize = writeSWF(swfTarget, outputFile); |
| |
| println(String.format("%d bytes written to %s", swfSize, outputFile.getCanonicalPath())); |
| |
| if (JSSharedData.OUTPUT_ISOLATED) |
| { |
| List<ICompilationUnit> reachableCompilationUnits = project.getReachableCompilationUnitsInSWFOrder(ImmutableSet.of(mainCU)); |
| for (final ICompilationUnit cu : reachableCompilationUnits) |
| { |
| if ((cu.getCompilationUnitType() == UnitType.AS_UNIT || |
| cu.getCompilationUnitType() == UnitType.MXML_UNIT) && cu != mainCU) |
| { |
| final File outputClassFile = new File(outputFolder.getAbsolutePath() + File.separator + cu.getShortNames().get(0) + ".js"); |
| System.out.println(outputClassFile.getAbsolutePath()); |
| otherFiles.add(outputClassFile.getAbsolutePath()); |
| final ISWFWriter swfWriter = JSSharedData.backend.createJSWriter(project, (List<ICompilerProblem>) errors, ImmutableSet.of(cu), false); |
| |
| if (swfWriter instanceof JSWriter) |
| { |
| final JSWriter writer = (JSWriter)swfWriter; |
| |
| final CountingOutputStream output = |
| new CountingOutputStream(new BufferedOutputStream(new FileOutputStream(outputClassFile))); |
| writer.writeTo(output); |
| output.flush(); |
| output.close(); |
| writer.close(); |
| } |
| } |
| } |
| } |
| } |
| |
| dumpDependencyGraphIfNeeded(); |
| |
| generateGoogDepsIfNeeded(outputFile.getParentFile()); |
| |
| compilationSuccess = true; |
| |
| if (JSSharedData.OPTIMIZE) |
| { |
| compilationSuccess = closureCompile(outputFile, problems); |
| if (compilationSuccess) |
| { |
| for (String fn : otherFiles) |
| { |
| File f = new File(fn); |
| f.delete(); |
| } |
| } |
| } |
| |
| |
| } |
| catch (Exception e) |
| { |
| 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(String.format("Found theme file %s", themeFile.getPath())); |
| } |
| } |
| } |
| |
| /** |
| * Setup {@code -compatibility-version} level. Falcon only support Flex 3+. |
| */ |
| protected void setupCompatibilityVersion() |
| { |
| final int compatibilityVersion = config.getCompilerMxmlCompatibilityVersion(); |
| if (compatibilityVersion < Configuration.MXML_VERSION_3_0) |
| throw new UnsupportedOperationException("Unsupported compatibility version: " + config.getCompilerCompatibilityVersionString()); |
| this.project.setCompatibilityVersion( |
| config.getCompilerMxmlMajorCompatibilityVersion(), |
| config.getCompilerMxmlMinorCompatibilityVersion(), |
| config.getCompilerMxmlRevisionCompatibilityVersion()); |
| } |
| |
| /** |
| * Parse all source files and dumpAST |
| * |
| * @throws InterruptedException |
| */ |
| private void dumpAST() throws InterruptedException |
| { |
| final List<String> astDump = new ArrayList<String>(); |
| final ImmutableList<ICompilationUnit> compilationUnits = getReachableCompilationUnits(); |
| for (final ICompilationUnit compilationUnit : compilationUnits) |
| { |
| final IASNode ast = compilationUnit.getSyntaxTreeRequest().get().getAST(); |
| astDump.add(ast.toString()); |
| } |
| |
| println(Joiner.on("\n\n").join(astDump)); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Build target artifact. |
| * |
| * @throws InterruptedException threading error |
| * @throws IOException IO error |
| * @throws ConfigurationException |
| */ |
| protected void buildArtifact() throws InterruptedException, IOException, ConfigurationException |
| { |
| 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 |
| * @throws ConfigurationException |
| * @throws FileNotFoundException |
| */ |
| private ISWF buildSWFModel() throws InterruptedException, FileNotFoundException, ConfigurationException |
| { |
| final List<ICompilerProblem> problemsBuildingSWF = |
| new ArrayList<ICompilerProblem>(); |
| |
| final ISWF swf = buildSWF(project, config.getMainDefinition(), mainCU, problemsBuildingSWF); |
| problems.addAll(problemsBuildingSWF); |
| if (swf == null) |
| { |
| ICompilerProblem problem = new UnableToBuildSWFProblem(getOutputFilePath()); |
| problems.add(problem); |
| } |
| else |
| { |
| swf.setFrameRate(config.getDefaultFrameRate()); |
| final int swfWidth = config.getDefaultWidth() * TWIPS_PER_PIXEL; |
| final int swfHeight = config.getDefaultHeight() * TWIPS_PER_PIXEL; |
| swf.setFrameSize(new Rect(swfWidth, swfHeight)); |
| swf.setVersion(config.getSwfVersion()); |
| swf.setTopLevelClass(config.getMainDefinition()); |
| swf.setUseAS3(true); |
| } |
| |
| reportRequiredRSLs(target); |
| |
| return swf; |
| } |
| |
| private void reportRequiredRSLs(ISWFTarget target) throws FileNotFoundException, InterruptedException, ConfigurationException |
| { |
| // Report the required RSLs: |
| if (hasRSLs()) |
| { |
| ITargetReport report = target.getTargetReport(); |
| |
| if (report == null) |
| return; // target must not have been built. |
| |
| // TODO (dloverin): localize messages |
| JSSharedData.instance.stdout("Required RSLs:"); |
| |
| // loop thru the RSLs and print out the required RSLs. |
| for (RSLSettings rslSettings : report.getRequiredRSLs()) |
| { |
| List<RSLAndPolicyFileURLPair> rslUrls = rslSettings.getRSLURLs(); |
| |
| switch (rslUrls.size()) |
| { |
| case 0: |
| assert false; // One RSL URL is required. |
| break; |
| case 1: |
| JSSharedData.instance.stdout(" " + rslUrls.get(0).getRSLURL()); |
| //ThreadLocalToolkit.log(new RequiredRSLUrl(rslUrls.get(0))); |
| break; |
| case 2: |
| JSSharedData.instance.stdout(" " + rslUrls.get(0).getRSLURL() + " with 1 failover."); |
| //ThreadLocalToolkit.log(new RequiredRSLUrlWithFailover(rslUrls.get(0))); |
| break; |
| default: |
| JSSharedData.instance.stdout(" " + rslUrls.get(0).getRSLURL() + " with " + (rslUrls.size() - 1) + " failovers."); |
| // ThreadLocalToolkit.log(new RequiredRSLUrlWithMultipleFailovers( |
| // rslUrls.get(0), |
| // rslUrls.size() - 1)); |
| break; |
| } |
| |
| } |
| |
| // All -runtime-shared-libraries are required |
| for (String rslUrl : targetSettings.getRuntimeSharedLibraries()) |
| { |
| JSSharedData.instance.stdout(" " + rslUrl); |
| //ThreadLocalToolkit.log(new RequiredRSLUrl(rslUrls.get(0))); |
| } |
| } |
| } |
| |
| private ITargetSettings getTargetSettings() |
| { |
| if (targetSettings == null) |
| targetSettings = projectConfigurator.getTargetSettings(TargetType.SWF); |
| |
| return targetSettings; |
| } |
| |
| private boolean hasRSLs() throws FileNotFoundException, InterruptedException, ConfigurationException |
| { |
| 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 FileNotFoundException error |
| * @throws IOException error |
| */ |
| private int writeSWF(final ISWF swf, final File outputFile) throws FileNotFoundException, IOException |
| { |
| int swfSize = 0; |
| final List<ICompilerProblem> problemList = new ArrayList<ICompilerProblem>(); |
| final ISWFWriter swfWriter = JSSharedData.backend.createSWFWriter(project, problemList, swf, false, config.debug()); |
| |
| if (swfWriter instanceof JSWriter) |
| { |
| final JSWriter writer = (JSWriter)swfWriter; |
| |
| final CountingOutputStream output = |
| new CountingOutputStream(new BufferedOutputStream(new FileOutputStream(outputFile))); |
| writer.writeTo(output); |
| output.flush(); |
| output.close(); |
| writer.close(); |
| swfSize = output.getCount(); |
| } |
| |
| /* |
| * W#3047880 falconjs_cs6: internal compiler error generated with |
| * optimize enabled compiling as3_enumerate.fla and fails to release the |
| * JS file http://watsonexp.corp.adobe.com/#bug=3047880 This is part #3 |
| * of the fix: The closure compiler throws RTEs on internal compiler |
| * errors, that don't get caught until they bubble up to MXMLJSC's |
| * scope. On their way out files remain unclosed and cause problems, |
| * because Flame cannot delete open files. We now get |
| * InternalCompilerProblems, which we need to transfer to our problem |
| * list. |
| */ |
| problems.addAll(problemList); |
| |
| return swfSize; |
| } |
| |
| /** |
| * Computes the set of compilation units that root the dependency walk. The |
| * returned set of compilation units and their dependencies will be |
| * compiled. |
| * <p> |
| * This method can be overriden by sub-classes. |
| * |
| * @return The set of rooted {@link ICompilationUnit}'s. |
| */ |
| protected ImmutableSet<ICompilationUnit> getRootedCompilationUnits() |
| { |
| return ImmutableSet.of(mainCU); |
| } |
| |
| /** |
| * @return All the reachable compilation units in this job. |
| */ |
| protected ImmutableList<ICompilationUnit> getReachableCompilationUnits() |
| { |
| final Set<ICompilationUnit> root = getRootedCompilationUnits(); |
| final List<ICompilationUnit> reachableCompilationUnitsInSWFOrder = |
| project.getReachableCompilationUnitsInSWFOrder(root); |
| final ImmutableList<ICompilationUnit> compilationUnits = ImmutableList.<ICompilationUnit> copyOf(reachableCompilationUnitsInSWFOrder); |
| return compilationUnits; |
| } |
| |
| /** |
| * 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 OnlyOneSource |
| * @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")) //$NON-NLS-1$ |
| { |
| mainCU = new StyleModuleCompilationUnit( |
| project, |
| workspace.getFileSpecification(normalizedMainFileName), |
| BasePriority.SOURCE_LIST); |
| // TODO: Use CSS file name once CSS module runtime code is finalized. (scai) |
| config.setMainDefinition("CSSModule2Main"); //$NON-NLS-1$ |
| project.addCompilationUnitsAndUpdateDefinitions( |
| Collections.singleton(mainCU)); |
| } |
| else |
| { |
| final SourceCompilationUnitFactory compilationUnitFactory = |
| project.getSourceCompilationUnitFactory(); |
| |
| File normalizedMainFile = new File(normalizedMainFileName); |
| if (compilationUnitFactory.canCreateCompilationUnit(normalizedMainFile)) |
| { |
| project.addIncludeSourceFile(normalizedMainFile); |
| |
| // just using the basename is obviously wrong: |
| // final String mainQName = FilenameUtils.getBaseName(normalizedMainFile); |
| |
| final List<String> sourcePath = config.getCompilerSourcePath(); |
| String mainQName = null; |
| if (sourcePath != null && !sourcePath.isEmpty()) |
| { |
| for (String path : sourcePath) |
| { |
| final String otherPath = new File(path).getAbsolutePath(); |
| if (mainFileName.startsWith(otherPath)) |
| { |
| mainQName = mainFileName.substring(otherPath.length() + 1); |
| mainQName = mainQName.replaceAll("\\\\", "/"); |
| mainQName = mainQName.replaceAll("\\/", "."); |
| if (mainQName.endsWith(".as")) |
| mainQName = mainQName.substring(0, mainQName.length() - 3); |
| break; |
| } |
| } |
| } |
| |
| if (mainQName == null) |
| mainQName = FilenameUtils.getBaseName(mainFileName); |
| |
| Collection<ICompilationUnit> mainFileCompilationUnits = |
| workspace.getCompilationUnits(normalizedMainFileName, project); |
| |
| assert mainFileCompilationUnits.size() == 1; |
| mainCU = Iterables.getOnlyElement(mainFileCompilationUnits); |
| |
| assert ((DefinitionPriority)mainCU.getDefinitionPriority()).getBasePriority() == DefinitionPriority.BasePriority.SOURCE_LIST; |
| |
| // Use main source file name as the root class name. |
| config.setMainDefinition(mainQName); |
| } |
| } |
| } |
| else |
| { |
| final List<ICompilerProblem> resourceBundleProblems = new ArrayList<ICompilerProblem>(); |
| Collection<ICompilationUnit> 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", //$NON-NLS-1$ |
| includedResourceBundles, |
| BasePriority.SOURCE_LIST); |
| config.setMainDefinition("GeneratedResourceModule"); //$NON-NLS-1$ |
| project.addCompilationUnitsAndUpdateDefinitions( |
| Collections.singleton(mainCU)); |
| } |
| } |
| |
| Preconditions.checkNotNull(mainCU, "Main compilation unit can't be null"); //$NON-NLS-1$ |
| |
| /* |
| * final String mainFileName = new |
| * File(config.getTargetFile()).getAbsolutePath(); final |
| * SourceCompilationUnitFactory compilationUnitFactory = |
| * project.getSourceCompilationUnitFactory(); final File mainFile = new |
| * File(mainFileName); // just using the basename is obviously wrong: // |
| * final String mainQName = FilenameUtils.getBaseName(mainFileName); |
| * final List<String> sourcePath = config.getCompilerSourcePath(); |
| * String mainQName = null; if( sourcePath != null && |
| * !sourcePath.isEmpty() ) { for( String path : sourcePath ) { final |
| * String otherPath = new File(path).getAbsolutePath(); if( |
| * mainFileName.startsWith(otherPath) ) { mainQName = |
| * mainFileName.substring(otherPath.length() + 1); mainQName = |
| * mainQName.replaceAll("\\\\", "/"); mainQName = |
| * mainQName.replaceAll("\\/", "."); if( mainQName.endsWith(".as") ) |
| * mainQName = mainQName.substring(0, mainQName.length() - 3); break; } |
| * } } if( mainQName == null ) mainQName = |
| * FilenameUtils.getBaseName(mainFileName); mainCU = |
| * compilationUnitFactory.createCompilationUnit( mainFile, |
| * DefinitionPriority.BasePriority.SOURCE_LIST, mainQName, null); |
| * Preconditions.checkNotNull(mainCU, |
| * "Main compilation unit can't be null"); |
| * project.addCompilationUnitsAndUpdateDefinitions( |
| * Collections.singleton(mainCU)); // Use main source file name as the |
| * root class name. config.setMainDefinition(mainQName); |
| */ |
| |
| // target = (FlexSWFTarget)project.createSWFTarget(getTargetSettings(), null); |
| if (getTargetSettings() == null) |
| return false; |
| |
| project.setTargetSettings(getTargetSettings()); |
| target = (JSTarget)JSSharedData.backend.createSWFTarget(project, getTargetSettings(), null); |
| |
| return true; |
| } |
| |
| /** |
| * @return a list of resource bundle compilation units that are included |
| * into the build process by -include-resource-bundles compiler argument. |
| */ |
| protected Collection<ICompilationUnit> getIncludedResourceBundlesCompUnits() throws InterruptedException |
| { |
| Collection<ICompilerProblem> bundleProblems = new ArrayList<ICompilerProblem>(); |
| if (includedResourceBundleCompUnits == null) |
| { |
| includedResourceBundleCompUnits = new HashSet<ICompilationUnit>(); |
| |
| for (String bundleName : config.getIncludeResourceBundles()) |
| { |
| includedResourceBundleCompUnits.addAll(ResourceBundleUtils.findCompilationUnits(bundleName, project, bundleProblems)); |
| problems.addAll(bundleProblems); |
| } |
| } |
| |
| return includedResourceBundleCompUnits; |
| } |
| |
| /** |
| * Setup the source paths. |
| * |
| * @throws InterruptedException |
| */ |
| /** |
| * Setup the source paths. |
| * |
| * @throws InterruptedException |
| */ |
| protected void setupSourcePath() throws InterruptedException |
| { |
| project.setSourcePath(toFiles(config.getCompilerSourcePath())); |
| } |
| |
| /** |
| * 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) |
| { |
| final String extension = "." + JSSharedData.OUTPUT_EXTENSION; |
| return FilenameUtils.removeExtension(config.getTargetFile()).concat(extension); |
| } |
| 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 fileSpecs file specifications |
| * @return List of File objects. No null values will be returned. |
| */ |
| public static List<File> toFiles(final List<String> paths) |
| { |
| final List<File> result = new ArrayList<File>(); |
| 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<IFileSpecification> toFileSpecifications( |
| final List<String> paths, |
| final Workspace workspace) |
| { |
| return Lists.transform(paths, new Function<String, IFileSpecification>() |
| { |
| @Override |
| public IFileSpecification apply(final String path) |
| { |
| return workspace.getFileSpecification(path); |
| } |
| }); |
| } |
| |
| /** |
| * Get my program name. |
| * |
| * @return always "mxmlc". |
| */ |
| protected String getProgramName() |
| { |
| return "mxmljsc"; //$NON-NLS-1$ |
| } |
| |
| /** |
| * 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("-")) //$NON-NLS-1$ |
| keyword = keyword.substring(1); |
| keywords.add(keyword); |
| } |
| } |
| |
| if (keywords.size() == 0) |
| keywords.add("help"); //$NON-NLS-1$ |
| |
| final String usages = CommandLineConfigurator.usage( |
| getProgramName(), |
| DEFAULT_VAR, |
| configBuffer, |
| keywords, |
| LocalizationManager.get(), |
| L10N_CONFIG_PREFIX); |
| 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, ConfigurationException |
| { |
| File dependencyGraphOutput = config.getDependencyGraphOutput(); |
| if (dependencyGraphOutput != null) |
| { |
| LinkedList<ICompilerProblem> problemList = new LinkedList<ICompilerProblem>(); |
| LinkageChecker linkageChecker = new LinkageChecker(project, getTargetSettings()); |
| final 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)); |
| dependencyGraphWriter.writeToStream(graphStream, problemList); |
| problems.addAll(problemList); |
| } |
| } |
| |
| // see http://blog.bolinfest.com/2009/11/calling-closure-compiler-from-java.html |
| private boolean closureCompile(File outputFile, ProblemQuery problems) throws IOException |
| { |
| /* |
| * <arg value="--compilation_level=ADVANCED_OPTIMIZATIONS"/> <arg value= |
| * "--externs=${falcon-sdk}/lib/google/closure-compiler/contrib/externs/jquery-1.5.js" |
| * /> <arg value= |
| * "--externs=${falcon-sdk}/lib/google/closure-compiler/contrib/externs/svg.js" |
| * /> <arg value= |
| * "--externs=${falcon-sdk}/lib/google/closure-compiler/contrib/externs/jsTestDriver.js" |
| * /> <arg value="--formatting=PRETTY_PRINT"/> <arg |
| * value="--js=${falcon-sdk}/frameworks/javascript/goog/base.js"/> <arg |
| * value="--js=${build.target.js}"/> <arg |
| * value="--js_output_file=${build.target.compiled.js}"/> <arg |
| * value="--create_source_map=${build.target.compiled.map}"/> |
| */ |
| Compiler compiler = new Compiler(); |
| |
| CompilerOptions options = new CompilerOptions(); |
| |
| if (JSSharedData.CLOSURE_compilation_level.equals("ADVANCED_OPTIMIZATIONS")) |
| CompilationLevel.ADVANCED_OPTIMIZATIONS.setOptionsForCompilationLevel(options); |
| else if (JSSharedData.CLOSURE_compilation_level.equals("WHITESPACE_ONLY")) |
| CompilationLevel.WHITESPACE_ONLY.setOptionsForCompilationLevel(options); |
| else |
| CompilationLevel.SIMPLE_OPTIMIZATIONS.setOptionsForCompilationLevel(options); |
| |
| final List<JSSourceFile> extern = CommandLineRunner.getDefaultExterns(); |
| |
| final List<JSSourceFile> input = new ArrayList<JSSourceFile>(); |
| String googHome = System.getenv("GOOG_HOME"); |
| if (googHome == null || googHome.length() == 0) |
| System.out.println("GOOG_HOME not defined. Should point to goog folder containing base.js."); |
| |
| input.add(JSSourceFile.fromFile(googHome + "/base.js")); |
| |
| GoogDepsWriter dependencyGraphWriter = |
| new GoogDepsWriter(mainCU, outputFile.getParentFile()); |
| |
| ArrayList<String> files; |
| try { |
| files = dependencyGraphWriter.getListOfFiles(); |
| for (String fileName : files) |
| { |
| input.add(JSSourceFile.fromFile(fileName)); |
| } |
| } catch (InterruptedException e1) { |
| // TODO Auto-generated catch block |
| e1.printStackTrace(); |
| } |
| |
| if (JSSharedData.CLOSURE_create_source_map != null) |
| options.sourceMapOutputPath = JSSharedData.CLOSURE_create_source_map; |
| |
| if (JSSharedData.CLOSURE_formatting != null) |
| { |
| if (JSSharedData.CLOSURE_formatting.equals("PRETTY_PRINT")) |
| options.prettyPrint = true; |
| else if (JSSharedData.CLOSURE_formatting.equals("PRINT_INPUT_DELIMITER")) |
| options.prettyPrint = true; |
| else |
| throw new RuntimeException("Unknown formatting option: " + JSSharedData.CLOSURE_formatting); |
| } |
| else if (JSSharedData.DEBUG) |
| { |
| options.prettyPrint = true; |
| } |
| |
| try |
| { |
| // compile() returns a Result, but it is not needed here. |
| compiler.compile(extern, input, options); |
| |
| if (compiler.getErrorCount() == 0) |
| { |
| // The compiler is responsible for generating the compiled code; it is not |
| // accessible via the Result. |
| final String optimizedCode = compiler.toSource(); |
| BufferedOutputStream outputbuffer; |
| try { |
| String mainName = mainCU.getShortNames().get(0); |
| File outputFolder = outputFile.getParentFile(); |
| outputbuffer = new BufferedOutputStream(new FileOutputStream(outputFile)); |
| outputbuffer.write(optimizedCode.getBytes()); |
| outputbuffer.flush(); |
| outputbuffer.close(); |
| File htmlFile = new File(outputFolder.getAbsolutePath() + File.separator + mainName + ".example.html"); |
| outputbuffer = new BufferedOutputStream(new FileOutputStream(htmlFile)); |
| String html = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"; |
| html += "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"; |
| html += "<head>\n"; |
| html += "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n"; |
| html += "<script type=\"text/javascript\" src=\"" + mainName + ".js" + "\" ></script>\n"; |
| html += "<script type=\"text/javascript\">\n"; |
| html += " var app = new " + mainName + "();\n"; |
| html += "</script>\n"; |
| html += "<title>" + mainName + "</title>\n"; |
| html += "</head>\n"; |
| html += "<body onload=\"app.start()\">\n"; |
| html += "</body>\n"; |
| html += "</html>\n"; |
| outputbuffer.write(html.getBytes()); |
| outputbuffer.flush(); |
| outputbuffer.close(); |
| } catch (FileNotFoundException e) { |
| // TODO Auto-generated catch block |
| e.printStackTrace(); |
| } catch (IOException e) { |
| // TODO Auto-generated catch block |
| e.printStackTrace(); |
| } catch (InterruptedException e) { |
| // TODO Auto-generated catch block |
| e.printStackTrace(); |
| } |
| } |
| else if (problems != null) |
| { |
| final JSError[] errors = compiler.getErrors(); |
| for (JSError err : errors) |
| problems.add(new ClosureProblem(err)); |
| |
| return false; |
| } |
| } |
| |
| /* |
| * internal compiler error generated with optimize enabled compiling |
| * as3_enumerate.fla and fails to release the JS file |
| * http://watsonexp.corp.adobe.com/#bug=3047880 This is part #3 of the |
| * fix: The closure compiler throws RTEs on internal compiler errors, |
| * that don't get caught until they bubble up to MXMLJSC's scope. On |
| * their way out files remain unclosed and cause problems, because Flame |
| * cannot delete open files. The change below addresses this problem. |
| */ |
| catch (RuntimeException rte) |
| { |
| if (problems != null) |
| { |
| final ICompilerProblem problem = new InternalCompilerProblem(rte); |
| problems.add(problem); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| private void generateGoogDepsIfNeeded(File outputFolder) throws IOException, InterruptedException, ConfigurationException |
| { |
| final File depsOutput = new File(outputFolder.getAbsolutePath() + File.separator + mainCU.getShortNames().get(0) + "Deps.js"); |
| if (!JSSharedData.OPTIMIZE) |
| { |
| GoogDepsWriter dependencyGraphWriter = |
| new GoogDepsWriter(mainCU, outputFolder); |
| BufferedOutputStream graphStream = new BufferedOutputStream(new FileOutputStream(depsOutput)); |
| dependencyGraphWriter.writeToStream(graphStream); |
| graphStream.flush(); |
| graphStream.close(); |
| } |
| } |
| |
| /** |
| * Get the current project. |
| * |
| * @return project |
| */ |
| protected FlexProject getProject() |
| { |
| return this.project; |
| } |
| |
| /** |
| * sets up JavaScript specific options |
| * |
| * @throws IOException |
| * @throws InterruptedException |
| */ |
| protected void setupJS() throws IOException, InterruptedException |
| { |
| JSGeneratingReducer.validate(); |
| |
| JSSharedData.instance.reset(); |
| project.getSourceCompilationUnitFactory().addHandler(asFileHandler); |
| |
| if (isCompc()) |
| JSSharedData.COMPILER_NAME = "COMPJSC"; |
| else |
| JSSharedData.COMPILER_NAME = "MXMLJSC"; |
| |
| JSSharedData.instance.setVerbose(config.isVerbose()); |
| |
| JSSharedData.DEBUG = config.debug(); |
| JSSharedData.OPTIMIZE = !config.debug() && config.optimize(); |
| |
| // workaround for Falcon bug: getCompilerLibraryPath() is not supported yet. |
| /* |
| * if( config.getCompilerLibraryPath() != null ) { final List<String> |
| * libs = config.getCompilerLibraryPath(); final File libPaths[] = new |
| * File[libs.size()]; int nthPath = 0; for( String lib: libs ) { if( lib |
| * == null ) throw JSSharedData.backend.createException( |
| * "Invalid swc path in -compiler.library-path"); final String pathname |
| * = lib; final File libPath = new File(pathname); libPaths[nthPath++] = |
| * libPath; if( JSSharedData.SDK_PATH == null && |
| * libPath.getName().equals("browserglobal.swc")) { // |
| * ../sdk/frameworks/libs/browser/browserglobal.swc File sdkFolder = |
| * libPath.getParentFile(); if( sdkFolder != null ) { sdkFolder = |
| * sdkFolder.getParentFile(); if( sdkFolder != null ) { sdkFolder = |
| * sdkFolder.getParentFile(); if( sdkFolder != null ) { sdkFolder = |
| * sdkFolder.getParentFile(); if( sdkFolder != null ) |
| * JSSharedData.SDK_PATH = sdkFolder.getAbsolutePath(); } } } } } // |
| * Setting the library path into the project // causes an ISWC to be |
| * built for each SWC on the library path. // It also causes an |
| * MXMLManifestManager to be built for the project // from the manifest |
| * info in the project's SWCs. project.setInternalLibraryPath(libPaths); |
| * } |
| */ |
| |
| final Set<ICompilationUnit> compilationUnits = new HashSet<ICompilationUnit>(); |
| |
| // workaround for Falcon bug: getCompilerIncludeLibraries() is not supported yet. |
| /* |
| * if( config.getCompilerIncludeLibraries() != null ) { // see |
| * LibraryPathManager.computeUnitsToAdd() for( String swcSpec: |
| * config.getCompilerIncludeLibraries() ) { final ISWCManager swcManager |
| * = project.getWorkspace().getSWCManager(); final String swcFilePath = |
| * swcSpec; final ISWC swc = swcManager.get(new File(swcFilePath)); |
| * final boolean isExternal = true; for (final ISWCLibrary library : |
| * swc.getLibraries()) { for (final ISWCScript script : |
| * library.getScripts()) { // Multiple definition in a script share the |
| * same compilation unit // with the same ABC byte code block. final |
| * List<String> qnames = new |
| * ArrayList<String>(script.getDefinitions().size()); for (final String |
| * definitionQName : script.getDefinitions()) { final String defName = |
| * definitionQName.replace(":", "."); qnames.add(defName); //$NON-NLS-1$ |
| * //$NON-NLS-2$ } final ICompilationUnit cu = new SWCCompilationUnit( |
| * project, swc, library, script, qnames, isExternal); |
| * compilationUnits.add(cu); } } } } |
| */ |
| |
| // add builtins |
| final File builtin = new File(JSSharedData.BUILT_IN); |
| if (builtin.canRead()) |
| { |
| if (config.isVerbose()) |
| JSSharedData.instance.stdout("[abc] found: " + builtin); |
| ABCCompilationUnit cu = new ABCCompilationUnit(project, builtin.getPath()); |
| compilationUnits.add(cu); |
| } |
| |
| if (!compilationUnits.isEmpty()) |
| { |
| final List<ICompilationUnit> units = new LinkedList<ICompilationUnit>(); |
| units.addAll(compilationUnits); |
| project.addCompilationUnitsAndUpdateDefinitions(units); |
| |
| if (config.isVerbose()) |
| { |
| for (final ISWC swc : project.getLibraries()) |
| { |
| JSSharedData.instance.stdout(String.format("[lib] found library %s", swc.getSWCFile().getPath())); |
| } |
| } |
| } |
| |
| registerSWCs(project); |
| } |
| |
| /** |
| * Replaces FlexApplicationProject::buildSWF() |
| * |
| * @param applicationProject |
| * @param rootClassName |
| * @param problems |
| * @return |
| * @throws InterruptedException |
| */ |
| |
| private ISWF buildSWF(CompilerProject applicationProject, String rootClassName, ICompilationUnit mainCU, Collection<ICompilerProblem> problems) throws |
| InterruptedException, ConfigurationException, FileNotFoundException |
| { |
| Collection<ICompilerProblem> fatalProblems = applicationProject.getFatalProblems(); |
| if (!fatalProblems.isEmpty()) |
| { |
| problems.addAll(fatalProblems); |
| return null; |
| } |
| |
| return target.build(mainCU, problems); |
| } |
| |
| protected void verboseMessage(PrintStream strm, String s) |
| { |
| if (strm != null && config.isVerbose()) |
| strm.println(s); |
| } |
| |
| /** |
| * Scans JavaScript code for @requires tags and registers class |
| * dependencies. |
| * |
| * @param cu current CompilationUnit |
| * @param classDef ClassDefinition of the JavaScript code |
| * @param jsCode JavaScript code of the ClassDefinition. private static void |
| * registerDependencies( ICompilationUnit cu, ClassDefinition classDef, |
| * String jsCode ) { final JSSharedData sharedData = JSSharedData.instance; |
| * final String requiresTag = "@requires"; // extract @requires class names |
| * and register dependencies. if( jsCode.contains(requiresTag) ) { final |
| * String line = jsCode.substring( jsCode.indexOf(requiresTag) + |
| * requiresTag.length() ); for( String part : line.split(requiresTag) ) { |
| * final String[] names = part.split("\\s+"); if( names.length > 1 ) { final |
| * String depClassName = names[1]; ASProjectScope projectScope = |
| * (ASProjectScope)cu.getProject().getScope(); IDefinition depClassDef = |
| * projectScope.findDefinitionByName(depClassName); if(depClassDef != null) |
| * { sharedData.addDependency(classDef, depClassDef); } } } } } |
| */ |
| |
| public static Boolean addDependency(ICompilationUnit cu, String className, DependencyType dt) |
| { |
| if (JSGeneratingReducer.isReservedDataType(className)) |
| return false; |
| |
| final ICompilationUnit fromCU = cu; |
| final CompilerProject compilerProject = (CompilerProject)cu.getProject(); |
| final ASProjectScope projectScope = compilerProject.getScope(); |
| |
| final IDefinition classDef = projectScope.findDefinitionByName(className); |
| if (classDef == null) |
| return false; |
| |
| final ICompilationUnit toCU = projectScope.getCompilationUnitForDefinition(classDef); |
| if (fromCU == toCU) |
| return false; |
| |
| // sharedData.verboseMessage( "Adding dependency: " + className ); |
| compilerProject.addDependency(fromCU, toCU, dt); |
| JSSharedData.instance.registerDefinition(classDef); |
| return true; |
| } |
| |
| public static List<IDefinition> getDefinitions(ICompilationUnit cu, Boolean onlyClasses) throws InterruptedException |
| { |
| final List<IDefinition> classDefs = new ArrayList<IDefinition>(); |
| // populate the IDefinition to ClassDefinition map |
| final List<IDefinition> defs = cu.getDefinitionPromises(); |
| for (IDefinition def : defs) |
| { |
| if (def instanceof DefinitionPromise) |
| { |
| // see DefinitionPromise::getActualDefinition |
| final String qname = def.getQualifiedName(); |
| final IFileScopeRequestResult fileScopeRequestResult = cu.getFileScopeRequest().get(); |
| def = fileScopeRequestResult.getMainDefinition(qname); |
| } |
| |
| if (def != null && !onlyClasses || (def instanceof ClassDefinition)) |
| { |
| classDefs.add(def); |
| } |
| } |
| return classDefs; |
| } |
| |
| public static void registerSWCs(CompilerProject project) throws InterruptedException |
| { |
| final JSSharedData sharedData = JSSharedData.instance; |
| |
| // collect all SWCCompilationUnit in swcUnits |
| final List<ICompilationUnit> swcUnits = new ArrayList<ICompilationUnit>(); |
| for (ICompilationUnit cu : project.getCompilationUnits()) |
| { |
| if (cu instanceof SWCCompilationUnit) |
| swcUnits.add(cu); |
| |
| final List<IDefinition> defs = getDefinitions(cu, false); |
| for (IDefinition def : defs) |
| { |
| sharedData.registerDefinition(def); |
| } |
| } |
| |
| } |
| |
| protected String getFlexHomePath() |
| { |
| final String loadConfig = config.getLoadConfig(); |
| if (loadConfig == null || loadConfig.isEmpty()) |
| return null; |
| // throw new Error("Cannot find load configuration file: " + loadConfig.getPath() ); |
| |
| final File loadConfigFile = new File(loadConfig); |
| if (!loadConfigFile.isFile()) |
| return null; |
| // throw new Error("Cannot find load configuration file: " + loadConfigFile.getAbsolutePath() ); |
| |
| final File frameworksFolder = new File(loadConfigFile.getParent()); |
| if (!frameworksFolder.isDirectory()) |
| return null; |
| // throw new Error("Cannot find framework folder: " + frameworksFolder.getAbsolutePath() ); |
| |
| final String flexHome = frameworksFolder.getParent(); |
| if (flexHome == null || flexHome.isEmpty()) |
| return null; |
| // throw new Error("Cannot find FLEX_HOME environment variable."); |
| |
| return flexHome; |
| } |
| |
| protected JSCommandLineConfiguration getConfiguration() |
| { |
| if (config instanceof JSCommandLineConfiguration) |
| return (JSCommandLineConfiguration)config; |
| return null; |
| } |
| |
| public class ClosureProblem implements ICompilerProblem |
| { |
| private JSError m_error; |
| |
| public ClosureProblem(JSError error) |
| { |
| m_error = error; |
| } |
| |
| /** |
| * Returns a unique identifier for this type of problem. |
| * <p> |
| * Clients can use this identifier to look up, in a .properties file, a |
| * localized template string describing the problem. The template string |
| * can have named placeholders such as ${name} to be filled in, based on |
| * correspondingly-named fields in the problem instance. |
| * <p> |
| * Clients can also use this identifier to decide whether the problem is |
| * an error, a warning, or something else; for example, they might keep |
| * a list of error ids and a list of warning ids. |
| * <p> |
| * The unique identifier happens to be the fully-qualified classname of |
| * the problem class. |
| * |
| * @return A unique identifier for the type of problem. |
| */ |
| public String getID() |
| { |
| // Return the fully-qualified classname of the CompilerProblem subclass |
| // as a String to identify the type of problem that occurred. |
| return getClass().getName(); |
| } |
| |
| /** |
| * Gets the path of the file in which the problem occurred. |
| * |
| * @return The path of the source file, or null if unknown. |
| */ |
| public String getFilePath() |
| { |
| return m_error.sourceName; |
| } |
| |
| /** |
| * Gets the offset within the source buffer at which the problem starts. |
| * |
| * @return The starting offset, or -1 if unknown. |
| */ |
| public int getStart() |
| { |
| return m_error.getCharno(); |
| } |
| |
| /** |
| * Gets the offset within the source buffer at which the problem ends. |
| * |
| * @return The ending offset, or -1 if unknown. |
| */ |
| public int getEnd() |
| { |
| return -1; |
| } |
| |
| /** |
| * Gets the line number within the source buffer at which the problem |
| * starts. Line numbers start at 0, not 1. |
| * |
| * @return The line number, or -1 if unknown. |
| */ |
| public int getLine() |
| { |
| return m_error.lineNumber; |
| } |
| |
| /** |
| * Gets the column number within the source buffer at which the problem |
| * starts. Column numbers start at 0, not 1. |
| * |
| * @return The column number, of -1 if unknown. |
| */ |
| public int getColumn() |
| { |
| return -1; |
| } |
| |
| /** |
| * Returns a readable description of the problem, by substituting field |
| * values for named placeholders such as ${name} in the localized |
| * template. |
| * |
| * @param template A localized template string describing the problem, |
| * determined by the client from the problem ID. If this parameter is |
| * null, an English template string, stored as the DESCRIPTION of the |
| * problem class, will be used. |
| * @return A readable description of the problem. |
| */ |
| public String getDescription(String template) |
| { |
| return m_error.description; |
| } |
| |
| /** |
| * Compares this problem to another problem by path, line, and column so |
| * that problems can be sorted. |
| */ |
| final public int compareTo(final ICompilerProblem other) |
| { |
| if (getFilePath() != null && other.getSourcePath() != null) |
| { |
| final int pathCompare = getFilePath().compareTo(other.getSourcePath()); |
| if (pathCompare != 0) |
| return pathCompare; |
| } |
| else if (getFilePath() != null && other.getSourcePath() == null) |
| { |
| return 1; |
| } |
| else if (getFilePath() == null && other.getSourcePath() != null) |
| { |
| return -1; |
| } |
| |
| if (getLine() < other.getLine()) |
| return -1; |
| else if (getLine() > other.getLine()) |
| return 1; |
| |
| if (getColumn() < other.getColumn()) |
| return -1; |
| else if (getColumn() > other.getColumn()) |
| return 1; |
| |
| return 0; |
| } |
| |
| public int getAbsoluteEnd() |
| { |
| // TODO Auto-generated method stub |
| return 0; |
| } |
| |
| public int getAbsoluteStart() |
| { |
| // TODO Auto-generated method stub |
| return 0; |
| } |
| |
| public String getSourcePath() |
| { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| } |
| } |