/* * Copyright 2008 Google Inc. * * Licensed 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 com.google.gwt.dev; import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.linker.ArtifactSet; import com.google.gwt.dev.CompileTaskRunner.CompileTask; import com.google.gwt.dev.cfg.ModuleDef; import com.google.gwt.dev.cfg.ModuleDefLoader; import com.google.gwt.dev.javac.UnitCache; import com.google.gwt.dev.javac.UnitCacheSingleton; import com.google.gwt.dev.jjs.PermutationResult; import com.google.gwt.dev.js.JsNamespaceOption; import com.google.gwt.dev.shell.CheckForUpdates; import com.google.gwt.dev.shell.CheckForUpdates.UpdateResult; import com.google.gwt.dev.util.Memory; import com.google.gwt.dev.util.PersistenceBackedObject; import com.google.gwt.dev.util.Util; import com.google.gwt.dev.util.arg.ArgHandlerDeployDir; import com.google.gwt.dev.util.arg.ArgHandlerExtraDir; import com.google.gwt.dev.util.arg.ArgHandlerIncrementalCompile; import com.google.gwt.dev.util.arg.ArgHandlerLocalWorkers; import com.google.gwt.dev.util.arg.ArgHandlerSaveSourceOutput; import com.google.gwt.dev.util.arg.ArgHandlerWarDir; import com.google.gwt.dev.util.arg.ArgHandlerWorkDirOptional; import com.google.gwt.dev.util.arg.OptionOptimize; import com.google.gwt.dev.util.log.speedtracer.CompilerEventType; import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger; import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event; import com.google.gwt.thirdparty.guava.common.collect.Sets; import com.google.gwt.util.tools.Utility; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.FutureTask; /** * The main executable entry point for the GWT Java to JavaScript compiler. */ public class Compiler { static class ArgProcessor extends PrecompileTaskArgProcessor { public ArgProcessor(CompilerOptions options) { super(options); registerHandler(new ArgHandlerLocalWorkers(options)); // Override the ArgHandlerWorkDirRequired in the super class. registerHandler(new ArgHandlerWorkDirOptional(options)); registerHandler(new ArgHandlerIncrementalCompile(options)); registerHandler(new ArgHandlerWarDir(options)); registerHandler(new ArgHandlerDeployDir(options)); registerHandler(new ArgHandlerExtraDir(options)); registerHandler(new ArgHandlerSaveSourceOutput(options)); } @Override protected String getName() { return Compiler.class.getName(); } } /** * Locates the unit cache dir relative to the war dir and returns a UnitCache instance. */ public static UnitCache getOrCreateUnitCache(TreeLogger logger, CompilerOptions options) { File persistentUnitCacheDir = null; if (options.getWarDir() != null && options.getWarDir().isDirectory()) { persistentUnitCacheDir = new File(options.getWarDir(), "../"); } // TODO: returns the same UnitCache even if the passed directory changes. Make this less // surprising. return UnitCacheSingleton.get(logger, null, persistentUnitCacheDir, options); } public static void main(String[] args) { Memory.initialize(); if (System.getProperty("gwt.jjs.dumpAst") != null) { System.out.println("Will dump AST to: " + System.getProperty("gwt.jjs.dumpAst")); } SpeedTracerLogger.init(); /* * NOTE: main always exits with a call to System.exit to terminate any * non-daemon threads that were started in Generators. Typically, this is to * shutdown AWT related threads, since the contract for their termination is * still implementation-dependent. */ final CompilerOptions options = new CompilerOptionsImpl(); if (new ArgProcessor(options).processArgs(args)) { CompileTask task = new CompileTask() { @Override public boolean run(TreeLogger logger) throws UnableToCompleteException { FutureTask<UpdateResult> updater = null; if (!options.isUpdateCheckDisabled()) { updater = CheckForUpdates.checkForUpdatesInBackgroundThread(logger, CheckForUpdates.ONE_DAY); } boolean success = Compiler.compile(logger, options); if (success) { CheckForUpdates.logUpdateAvailable(logger, updater); } return success; } }; if (CompileTaskRunner.runWithAppropriateLogger(options, task)) { // Exit w/ success code. System.exit(0); } } // Exit w/ non-success code. System.exit(1); } public static boolean compile( TreeLogger logger, CompilerOptions compilerOptions) throws UnableToCompleteException { List<ModuleDef> moduleDefs = new ArrayList<>(); for (String moduleName : compilerOptions.getModuleNames()) { moduleDefs.add(ModuleDefLoader.loadFromClassPath(logger, moduleName, true)); } boolean result = true; for (ModuleDef moduleDef : moduleDefs) { result &= compile(logger, compilerOptions, moduleDef); } return result; } public static boolean compile( TreeLogger logger, CompilerOptions compilerOptions, ModuleDef moduleDef) throws UnableToCompleteException { MinimalRebuildCache minimalRebuildCache = compilerOptions.isIncrementalCompileEnabled() ? new MinimalRebuildCache() : new NullRebuildCache(); return compile(logger, compilerOptions, minimalRebuildCache, moduleDef); } public static boolean compile( TreeLogger logger, CompilerOptions compilerOptions, MinimalRebuildCache minimalRebuildCache, ModuleDef moduleDef) throws UnableToCompleteException { CompilerOptionsImpl options = new CompilerOptionsImpl(compilerOptions); boolean tempWorkDir = false; try { if (options.getWorkDir() == null) { options.setWorkDir(Utility.makeTemporaryDirectory(null, "gwtc")); tempWorkDir = true; } if ((options.isSoycEnabled() || options.isJsonSoycEnabled()) && options.getExtraDir() == null) { options.setExtraDir(new File("extras")); } if (options.isIncrementalCompileEnabled()) { // Disable options that disrupt contiguous output JS source per class. options.setClusterSimilarFunctions(false); options.setOptimizationLevel(OptionOptimize.OPTIMIZE_LEVEL_DRAFT); options.setRunAsyncEnabled(false); // Disable options that disrupt reference consistency across multiple compiles. // TODO(stalcup): preserve Namespace state in MinimalRebuildCache across compiles. options.setNamespace(JsNamespaceOption.NONE); } CompilerContext compilerContext = new CompilerContext.Builder() .options(options) .minimalRebuildCache(minimalRebuildCache) .unitCache(getOrCreateUnitCache(logger, options)) .module(moduleDef) .build(); String moduleName = moduleDef.getCanonicalName(); if (options.isValidateOnly()) { if (!Precompile.validate(logger, compilerContext)) { return false; } } else { long beforeCompileMs = System.currentTimeMillis(); TreeLogger branch = logger.branch(TreeLogger.INFO, "Compiling module " + moduleName); Precompilation precompilation = Precompile.precompile(branch, compilerContext); if (precompilation == null) { return false; } // TODO: move to precompile() after params are refactored if (!options.shouldSaveSource()) { precompilation.removeSourceArtifacts(branch); } Event compilePermutationsEvent = SpeedTracerLogger.start(CompilerEventType.COMPILE_PERMUTATIONS); Permutation[] allPerms = precompilation.getPermutations(); List<PersistenceBackedObject<PermutationResult>> resultFiles = CompilePerms.makeResultFiles( options.getCompilerWorkDir(moduleName), allPerms, options); CompilePerms.compile(branch, compilerContext, precompilation, allPerms, options.getLocalWorkers(), resultFiles); compilePermutationsEvent.end(); ArtifactSet generatedArtifacts = precompilation.getGeneratedArtifacts(); PrecompileTaskOptions precompileOptions = precompilation.getUnifiedAst().getOptions(); precompilation = null; // No longer needed, so save the memory long afterCompileMs = System.currentTimeMillis(); double compileSeconds = (afterCompileMs - beforeCompileMs) / 1000d; branch.log(TreeLogger.INFO, String.format("Compilation succeeded -- %.3fs", compileSeconds)); long beforeLinkMs = System.currentTimeMillis(); Event linkEvent = SpeedTracerLogger.start(CompilerEventType.LINK); File absPath = new File(options.getWarDir(), moduleDef.getName()); absPath = absPath.getAbsoluteFile(); String logMessage = "Linking into " + absPath; if (options.getExtraDir() != null) { File absExtrasPath = new File(options.getExtraDir(), moduleDef.getName()); absExtrasPath = absExtrasPath.getAbsoluteFile(); logMessage += "; Writing extras to " + absExtrasPath; } Link.link(logger.branch(TreeLogger.TRACE, logMessage), moduleDef, moduleDef.getPublicResourceOracle(), generatedArtifacts, allPerms, resultFiles, Sets.<PermutationResult>newHashSet(), precompileOptions, options); linkEvent.end(); long afterLinkMs = System.currentTimeMillis(); double linkSeconds = (afterLinkMs - beforeLinkMs) / 1000d; branch.log(TreeLogger.INFO, String.format("Linking succeeded -- %.3fs", linkSeconds)); } } catch (IOException e) { logger.log(TreeLogger.ERROR, "Unable to create compiler work directory", e); return false; } finally { if (tempWorkDir) { Util.recursiveDelete(options.getWorkDir(), false); } } return true; } }