private void validateServletTags( TreeLogger logger, ServletValidator servletValidator, ServletWriter servletWriter, ModuleDef module) { String[] servletPaths = module.getServletPaths(); if (servletPaths.length == 0) { return; } TreeLogger servletLogger = logger.branch( TreeLogger.DEBUG, "Validating <servlet> tags for module '" + module.getName() + "'", null, new InstalledHelpInfo("servletMappings.html")); for (String servletPath : servletPaths) { String servletClass = module.findServletForPath(servletPath); assert (servletClass != null); // Prefix module name to convert module mapping to global mapping. servletPath = "/" + module.getName() + servletPath; if (servletValidator == null) { servletWriter.addMapping(servletClass, servletPath); } else { servletValidator.validate(servletLogger, servletClass, servletPath); } } }
/** * Creates a dummy output directory without compiling the module. Either this method or {@link * #precompile} should be called first. */ synchronized Job.Result initWithoutPrecompile(TreeLogger logger) throws UnableToCompleteException { long startTime = System.currentTimeMillis(); CompileDir compileDir = outboxDir.makeCompileDir(logger); TreeLogger compileLogger = makeCompileLogger(compileDir, logger); ModuleDef module; try { module = loadModule(compileLogger); logger.log(TreeLogger.INFO, "Loading Java files in " + inputModuleName + "."); CompilerOptions loadOptions = new CompilerOptionsImpl(compileDir, inputModuleName, options); compilerContext = compilerContextBuilder.options(loadOptions).unitCache(unitCache).build(); // Loads and parses all the Java files in the GWT application using the JDT. // (This is warmup to make compiling faster later; we stop at this point to avoid // needing to know the binding properties.) module.getCompilationState(compileLogger, compilerContext); setUpCompileDir(compileDir, module, compileLogger); if (launcherDir != null) { launcherDir.update(module, compileDir, compileLogger); } outputModuleName.set(module.getName()); } finally { // Make the compile log available no matter what happens. lastBuild.set(compileDir); } long elapsedTime = System.currentTimeMillis() - startTime; compileLogger.log(TreeLogger.Type.INFO, "Module setup completed in " + elapsedTime + " ms"); return new Result(compileDir, module.getName(), null); }
@Override protected synchronized void produceOutput( TreeLogger logger, StandardLinkerContext linkerStack, ArtifactSet artifacts, ModuleDef module, boolean isRelink) throws UnableToCompleteException { TreeLogger linkLogger = logger.branch(TreeLogger.DEBUG, "Linking module '" + module.getName() + "'"); OutputFileSetOnDirectory outFileSet = new OutputFileSetOnDirectory(options.getWarDir(), module.getName() + "/"); OutputFileSetOnDirectory deployFileSet = new OutputFileSetOnDirectory(options.getDeployDir(), module.getName() + "/"); OutputFileSet extraFileSet = new NullOutputFileSet(); if (options.getExtraDir() != null) { extraFileSet = new OutputFileSetOnDirectory(options.getExtraDir(), module.getName() + "/"); } linkerStack.produceOutput(linkLogger, artifacts, Visibility.Public, outFileSet); linkerStack.produceOutput(linkLogger, artifacts, Visibility.Deploy, deployFileSet); linkerStack.produceOutput(linkLogger, artifacts, Visibility.Private, extraFileSet); outFileSet.close(); deployFileSet.close(); try { extraFileSet.close(); } catch (IOException e) { linkLogger.log(TreeLogger.ERROR, "Error emiting extra files", e); throw new UnableToCompleteException(); } }
@Override protected boolean doSlowStartup() { tempWorkDir = options.getWorkDir() == null; if (tempWorkDir) { try { options.setWorkDir(Utility.makeTemporaryDirectory(null, "gwtc")); } catch (IOException e) { System.err.println("Unable to create hosted mode work directory"); e.printStackTrace(); return false; } } TreeLogger branch = getTopLogger().branch(TreeLogger.TRACE, "Linking modules"); Event slowStartupEvent = SpeedTracerLogger.start(DevModeEventType.SLOW_STARTUP); try { for (ModuleDef module : startupModules.values()) { TreeLogger loadLogger = branch.branch( TreeLogger.DEBUG, "Bootstrap link for command-line module '" + module.getCanonicalName() + "'"); link(loadLogger, module); } } catch (UnableToCompleteException e) { // Already logged. return false; } finally { slowStartupEvent.end(); } return true; }
public void testTwoDimensionPermWithExpansion() { ModuleDef md = new ModuleDef("testTwoDimensionsWithExpansion"); Properties properties = md.getProperties(); { BindingProperty bindingProperty = properties.createBinding("user.agent"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "moz"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "ie6"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "ie8"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "opera"); } { BindingProperty bindingProperty = properties.createBinding("stackTraces"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "false"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "true"); // <set-property name="stackTraces" value="false" /> bindingProperty.setValues(bindingProperty.getRootCondition(), "false"); /* * <set-property name="stackTraces" value="true,false"> <when user.agent * is ie6 or ie 8> </set-property> */ ConditionAny cond = new ConditionAny(); cond.getConditions().add(new ConditionWhenPropertyIs("user.agent", "ie6")); cond.getConditions().add(new ConditionWhenPropertyIs("user.agent", "ie8")); bindingProperty.setValues(cond, "true", "false"); } validateTwoDimensionPerm(properties, md.getActiveLinkerNames()); }
public void testTwoDimensionPermWithRestriction() { // This is what you'd get with a conditional <set-property value="false"> ModuleDef md = new ModuleDef("testTwoDimensionsWithRestriction"); Properties properties = md.getProperties(); { BindingProperty bindingProperty = properties.createBinding("user.agent"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "moz"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "ie6"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "ie8"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "opera"); } { BindingProperty bindingProperty = properties.createBinding("stackTraces"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "false"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "true"); ConditionAny cond = new ConditionAny(); cond.getConditions().add(new ConditionWhenPropertyIs("user.agent", "moz")); cond.getConditions().add(new ConditionWhenPropertyIs("user.agent", "opera")); bindingProperty.setValues(cond, "false"); } validateTwoDimensionPerm(properties, md.getActiveLinkerNames()); }
/** Make sure that a cycle doesn't cause an infinite loop. */ public void testCycle() { // This is what you'd get with a conditional <set-property value="false"> ModuleDef md = new ModuleDef("testCycle"); Properties properties = md.getProperties(); { BindingProperty bindingProperty = properties.createBinding("A"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "a1"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "a2"); bindingProperty.addDefinedValue(new ConditionWhenPropertyIs("B", "b3"), "a3"); } { BindingProperty bindingProperty = properties.createBinding("B"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "b1"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "b2"); bindingProperty.addDefinedValue(new ConditionWhenPropertyIs("A", "a3"), "b3"); } try { new PropertyCombinations(properties, md.getActiveLinkerNames()); fail(); } catch (IllegalStateException e) { // OK } }
public void testOneDimensionPermWithCollapse() { ModuleDef md = new ModuleDef("testOneDimensionPerm"); Properties properties = md.getProperties(); { BindingProperty bindingProperty = properties.createBinding("debug"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "false"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "true"); bindingProperty.addCollapsedValues("true", "false"); } // Permutations and their values are in stable alphabetical order. // PropertyCombinations permutations = new PropertyCombinations(md.getProperties(), md.getActiveLinkerNames()); List<PropertyCombinations> collapsed = permutations.collapseProperties(); assertEquals("size", 1, collapsed.size()); permutations = collapsed.get(0); String[] permutation; Iterator<String[]> it = permutations.iterator(); assertTrue(it.hasNext()); permutation = it.next(); assertEquals("false", permutation[0]); assertTrue(it.hasNext()); permutation = it.next(); assertEquals("true", permutation[0]); }
/** * Prepares a stub compile directory. It will include all "public" resources and a nocache.js file * that invokes the compiler. */ private void setUpCompileDir(CompileDir compileDir, ModuleDef module, TreeLogger compileLogger) throws UnableToCompleteException { try { String currentModuleName = module.getName(); // Create the directory. File outputDir = new File(compileDir.getWarDir().getCanonicalPath() + "/" + currentModuleName); if (!outputDir.exists()) { if (!outputDir.mkdir()) { compileLogger.log(Type.WARN, "cannot create directory: " + outputDir); } } LauncherDir.writePublicResources(outputDir, module, compileLogger); // write no cache that will inject recompile.nocache.js String stub = LauncherDir.generateStubNocacheJs(module.getName(), options); File noCacheJs = new File(outputDir.getCanonicalPath(), module.getName() + ".nocache.js"); Files.write(stub, noCacheJs, Charsets.UTF_8); // Create a "module_name.recompile.nocache.js" that calculates the permutation // and forces a recompile. String recompileNoCache = generateModuleRecompileJs(module, compileLogger); writeRecompileNoCacheJs(outputDir, currentModuleName, recompileNoCache, compileLogger); } catch (IOException e) { compileLogger.log(Type.ERROR, "Error creating stub compile directory.", e); UnableToCompleteException wrapped = new UnableToCompleteException(); wrapped.initCause(e); throw wrapped; } }
@Override protected boolean doStartup() { // Background scan the classpath to warm the cache. Thread scanThread = new Thread( new Runnable() { public void run() { ResourceOracleImpl.preload(TreeLogger.NULL); } }); scanThread.setDaemon(true); scanThread.setPriority((Thread.MIN_PRIORITY + Thread.NORM_PRIORITY) / 2); scanThread.start(); File persistentCacheDir = null; if (options.getWarDir() != null && !options.getWarDir().getName().endsWith(".jar")) { persistentCacheDir = new File(options.getWarDir(), "../"); } if (!super.doStartup(persistentCacheDir)) { return false; } ServletValidator servletValidator = null; ServletWriter servletWriter = null; File webXml = new File(options.getWarDir(), "WEB-INF/web.xml"); if (!options.isNoServer()) { if (webXml.exists()) { servletValidator = ServletValidator.create(getTopLogger(), webXml); } else { servletWriter = new ServletWriter(); } } TreeLogger branch = getTopLogger().branch(TreeLogger.TRACE, "Loading modules"); try { for (String moduleName : options.getModuleNames()) { TreeLogger moduleBranch = branch.branch(TreeLogger.TRACE, moduleName); ModuleDef module = loadModule(moduleBranch, moduleName, false); // Create a hard reference to the module to avoid gc-ing it until we // actually load the module from the browser. startupModules.put(module.getName(), module); if (!options.isNoServer()) { validateServletTags(moduleBranch, servletValidator, servletWriter, module); } } if (servletWriter != null) { servletWriter.realize(webXml); } } catch (IOException e) { getTopLogger().log(TreeLogger.WARN, "Unable to generate '" + webXml.getAbsolutePath() + "'"); } catch (UnableToCompleteException e) { // Already logged. return false; } return true; }
public void testTwoDimensionPerm() { ModuleDef md = new ModuleDef("testTwoDimensionPerm"); Properties properties = md.getProperties(); { BindingProperty bindingProperty = properties.createBinding("user.agent"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "moz"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "ie6"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "opera"); } { BindingProperty bindingProperty = properties.createBinding("debug"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "false"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "true"); } // String[]s and their values are in stable alphabetical order. // PropertyCombinations permutations = new PropertyCombinations(md.getProperties(), md.getActiveLinkerNames()); String[] permutation; Iterator<String[]> it = permutations.iterator(); assertTrue(it.hasNext()); permutation = it.next(); assertEquals("false", permutation[0]); assertEquals("ie6", permutation[1]); assertTrue(it.hasNext()); permutation = it.next(); assertEquals("false", permutation[0]); assertEquals("moz", permutation[1]); assertTrue(it.hasNext()); permutation = it.next(); assertEquals("false", permutation[0]); assertEquals("opera", permutation[1]); assertTrue(it.hasNext()); permutation = it.next(); assertEquals("true", permutation[0]); assertEquals("ie6", permutation[1]); assertTrue(it.hasNext()); permutation = it.next(); assertEquals("true", permutation[0]); assertEquals("moz", permutation[1]); assertTrue(it.hasNext()); permutation = it.next(); assertEquals("true", permutation[0]); assertEquals("opera", permutation[1]); }
/** Generates the nocache.js file to use when precompile is not on. */ private static String generateModuleRecompileJs(ModuleDef module, TreeLogger compileLogger) throws UnableToCompleteException { String outputModuleName = module.getName(); try { String templateJs = Resources.toString( Resources.getResource(Recompiler.class, "recompile_template.js"), Charsets.UTF_8); String propertyProviders = PropertiesUtil.generatePropertiesSnippet(module, compileLogger); String libJs = Resources.toString( Resources.getResource(Recompiler.class, "recompile_lib.js"), Charsets.UTF_8); String recompileJs = Resources.toString( Resources.getResource(Recompiler.class, "recompile_main.js"), Charsets.UTF_8); templateJs = templateJs.replace("__MODULE_NAME__", "'" + outputModuleName + "'"); templateJs = templateJs.replace("__PROPERTY_PROVIDERS__", propertyProviders); templateJs = templateJs.replace("__LIB_JS__", libJs); templateJs = templateJs.replace("__MAIN__", recompileJs); return templateJs; } catch (IOException e) { compileLogger.log(Type.ERROR, "Can not generate + " + outputModuleName + " recompile js", e); throw new UnableToCompleteException(); } }
public BuildResultStatus build(TreeLogger logger) { try { logger = logger.branch(TreeLogger.INFO, "Performing an incremental build"); CompilerContext compilerContext = new CompilerContext.Builder() .compileMonolithic(false) .libraryGroup(LibraryGroup.fromLibraries(Lists.<Library>newArrayList(), false)) .build(); long beforeLoadRootModuleMs = System.currentTimeMillis(); rootModule = ModuleDefLoader.loadFromResources( logger, compilerContext, rootModuleName, resourceLoader, false); finalProperties = rootModule.getProperties(); long loadRootModuleDurationMs = System.currentTimeMillis() - beforeLoadRootModuleMs; logger.log( TreeLogger.INFO, String.format( "%.3fs -- Parsing and loading root module definition in %s", loadRootModuleDurationMs / 1000d, rootModuleName)); long beforeCreateTargetGraphMs = System.currentTimeMillis(); rootBuildTarget = createBuildTarget(logger, rootModuleName); rootBuildTarget.setModule(rootModule); long createdTargetGraphDurationMs = System.currentTimeMillis() - beforeCreateTargetGraphMs; logger.log( TreeLogger.INFO, String.format( "%.3fs -- Creating target graph (%s targets)", createdTargetGraphDurationMs / 1000d, buildTargetsByCanonicalModuleName.size())); if (!circularReferenceModuleNameLoops.isEmpty()) { for (List<String> circularReferenceModuleNameLoop : circularReferenceModuleNameLoops) { logger.log( TreeLogger.ERROR, formatCircularModulePathMessage(circularReferenceModuleNameLoop)); } throw new UnableToCompleteException(); } logLoadedBuildTargetGraph(logger, buildTargetsByCanonicalModuleName); long beforeComputeOutputFreshnessMs = System.currentTimeMillis(); ModuleDefLoader.clearModuleCache(); rootBuildTarget.computeOutputFreshness(logger); long computeOutputFreshnessDurationMs = System.currentTimeMillis() - beforeComputeOutputFreshnessMs; logger.log( TreeLogger.INFO, String.format( "%.3fs -- Computing per-target output freshness", computeOutputFreshnessDurationMs / 1000d)); TreeLogger branch = logger.branch(TreeLogger.INFO, "Compiling target graph"); boolean success = rootBuildTarget.link(branch); return BuildResultStatus.get(success); } catch (UnableToCompleteException e) { // The real cause has been logged. return BuildResultStatus.FAILED; } }
private static boolean maybeOverrideConfig(ModuleDef module, String propName, String newValue) { ConfigurationProperty config = module.getProperties().findConfigProp(propName); if (config != null) { config.setValue(newValue); return true; } return false; }
/** Sets a binding even if it's set to a different value in the GWT application. */ private static void overrideBinding(ModuleDef module, String propName, String newValue) { BindingProperty binding = module.getProperties().findBindingProp(propName); if (binding != null) { // This sets both allowed and generated values, which is needed since the module // might have explicitly disallowed the value. // It persists over multiple compiles but that's okay since we set it the same way // every time. binding.setValues(binding.getRootCondition(), newValue); } }
public void testTwoDimensionPermWithCollapse() { ModuleDef md = new ModuleDef("testTwoDimensionPerm"); Properties properties = md.getProperties(); { BindingProperty bindingProperty = properties.createBinding("user.agent"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "moz"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "ie6"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "opera"); bindingProperty.addCollapsedValues("moz", "ie6", "opera"); } { BindingProperty bindingProperty = properties.createBinding("debug"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "false"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "true"); } // String[]s and their values are in stable alphabetical order. // PropertyCombinations permutations = new PropertyCombinations(md.getProperties(), md.getActiveLinkerNames()); List<PropertyCombinations> collapsed = permutations.collapseProperties(); assertEquals("size", 2, collapsed.size()); Iterator<String[]> it = collapsed.get(0).iterator(); assertEquals(Arrays.asList("false", "ie6"), Arrays.asList(it.next())); assertEquals(Arrays.asList("false", "moz"), Arrays.asList(it.next())); assertEquals(Arrays.asList("false", "opera"), Arrays.asList(it.next())); assertFalse(it.hasNext()); it = collapsed.get(1).iterator(); assertEquals(Arrays.asList("true", "ie6"), Arrays.asList(it.next())); assertEquals(Arrays.asList("true", "moz"), Arrays.asList(it.next())); assertEquals(Arrays.asList("true", "opera"), Arrays.asList(it.next())); assertFalse(it.hasNext()); }
private boolean isCircularlyReferent(String potentialDuplicateModuleName) { if (knownCircularlyReferentModuleNames.contains(potentialDuplicateModuleName)) { return true; } if (!moduleReferencePath.contains(potentialDuplicateModuleName)) { return false; } List<String> circularModuleReferencePath = Lists.newArrayList(moduleReferencePath); // Attach the duplicate module name to the end of the loop. circularModuleReferencePath.add(potentialDuplicateModuleName); List<String> annotatedCircularModuleReferencePath = Lists.newArrayList(); // The current module path only includes libraries but the connections between libraries might // be silently flowing through filesets. Add filesets to the path so that the output is more // readable. for (int moduleNameIndex = 0; moduleNameIndex < circularModuleReferencePath.size() - 1; moduleNameIndex++) { String thisModuleName = circularModuleReferencePath.get(moduleNameIndex); String nextModuleName = circularModuleReferencePath.get(moduleNameIndex + 1); annotatedCircularModuleReferencePath.add( thisModuleName + (thisModuleName.equals(potentialDuplicateModuleName) ? " <loop>" : "")); List<String> fileSetPath = rootModule.getFileSetPathBetween(thisModuleName, nextModuleName); if (fileSetPath != null) { for (String fileSetModuleName : fileSetPath) { annotatedCircularModuleReferencePath.add(fileSetModuleName + " <fileset>"); } } } // Attach the duplicate module name to the end of the loop. annotatedCircularModuleReferencePath.add(potentialDuplicateModuleName + " <loop>"); knownCircularlyReferentModuleNames.addAll(annotatedCircularModuleReferencePath); circularReferenceModuleNameLoops.add(annotatedCircularModuleReferencePath); return true; }
/** * Attempts to set a binding property to the given value. If the value is not allowed, see if we * can find a value that will work. There is a special case for "locale". * * @return the value actually set, or null if unable to set the property */ private static String maybeSetBinding( TreeLogger logger, ModuleDef module, String propName, String newValue) { logger = logger.branch(TreeLogger.Type.INFO, "binding: " + propName + "=" + newValue); BindingProperty binding = module.getProperties().findBindingProp(propName); if (binding == null) { logger.log(TreeLogger.Type.WARN, "undefined property: '" + propName + "'"); return null; } if (!binding.isAllowedValue(newValue)) { String[] allowedValues = binding.getAllowedValues(binding.getRootCondition()); logger.log( TreeLogger.Type.WARN, "property '" + propName + "' cannot be set to '" + newValue + "'"); logger.log(TreeLogger.Type.INFO, "allowed values: " + Joiner.on(", ").join(allowedValues)); // See if we can fall back on a reasonable default. if (allowedValues.length == 1) { // There is only one possibility, so use it. newValue = allowedValues[0]; } else if (binding.getName().equals("locale")) { // TODO: come up with a more general solution. Perhaps fail // the compile and give the user a way to override the property? newValue = chooseDefault(binding, "default", "en", "en_US"); } else { // There is more than one. Continue and possibly compile multiple permutations. logger.log( TreeLogger.Type.INFO, "continuing without " + propName + ". Sourcemaps may not work."); return null; } logger.log(TreeLogger.Type.INFO, "recovered with " + propName + "=" + newValue); } binding.setRootGeneratedValues(newValue); return newValue; }
private BuildTarget createBuildTarget(TreeLogger logger, String moduleName) throws UnableToCompleteException { if (isCircularlyReferent(moduleName)) { // Allow the target graph creation to continue so that all of the circular reference loops can // be gathered. return null; } if (buildTargetsByCanonicalModuleName.containsKey(moduleName)) { return buildTargetsByCanonicalModuleName.get(moduleName); } logger.log(TreeLogger.SPAM, String.format("Adding target %s to build graph.", moduleName)); moduleReferencePath.add(moduleName); List<BuildTarget> dependencyBuildTargets = Lists.newArrayList(); for (String dependencyModuleName : rootModule.getDirectDependencies(moduleName)) { dependencyBuildTargets.add(createBuildTarget(logger, dependencyModuleName)); } moduleReferencePath.remove(moduleName); return createBuildTarget(moduleName, dependencyBuildTargets.toArray(new BuildTarget[0])); }
public boolean run(TreeLogger logger, ModuleDef... modules) throws UnableToCompleteException { boolean tempWorkDir = false; try { if (options.getWorkDir() == null) { options.setWorkDir(Utility.makeTemporaryDirectory(null, "gwtc")); tempWorkDir = true; } if (options.isSoycEnabled() && options.getExtraDir() == null) { options.setExtraDir(new File("extras")); } File persistentUnitCacheDir = null; if (options.getWarDir() != null && !options.getWarDir().getName().endsWith(".jar")) { persistentUnitCacheDir = new File(options.getWarDir(), "../"); } CompilationStateBuilder.init(logger, persistentUnitCacheDir); for (ModuleDef module : modules) { String moduleName = module.getCanonicalName(); if (options.isValidateOnly()) { if (!Precompile.validate(logger, options, module, options.getGenDir())) { return false; } } else { long compileStart = System.currentTimeMillis(); TreeLogger branch = logger.branch(TreeLogger.INFO, "Compiling module " + moduleName); // Optimize early since permutation compiles will run in process. options.setOptimizePrecompile(true); Precompilation precompilation = Precompile.precompile(branch, options, module, options.getGenDir()); if (precompilation == null) { return false; } Event compilePermutationsEvent = SpeedTracerLogger.start(CompilerEventType.COMPILE_PERMUTATIONS); Permutation[] allPerms = precompilation.getPermutations(); List<FileBackedObject<PermutationResult>> resultFiles = CompilePerms.makeResultFiles(options.getCompilerWorkDir(moduleName), allPerms); CompilePerms.compile( branch, precompilation, allPerms, options.getLocalWorkers(), resultFiles); compilePermutationsEvent.end(); ArtifactSet generatedArtifacts = precompilation.getGeneratedArtifacts(); JJSOptions precompileOptions = precompilation.getUnifiedAst().getOptions(); precompilation = null; // No longer needed, so save the memory Event linkEvent = SpeedTracerLogger.start(CompilerEventType.LINK); File absPath = new File(options.getWarDir(), module.getName()); absPath = absPath.getAbsoluteFile(); String logMessage = "Linking into " + absPath; if (options.getExtraDir() != null) { File absExtrasPath = new File(options.getExtraDir(), module.getName()); absExtrasPath = absExtrasPath.getAbsoluteFile(); logMessage += "; Writing extras to " + absExtrasPath; } Link.link( logger.branch(TreeLogger.TRACE, logMessage), module, generatedArtifacts, allPerms, resultFiles, options.getWarDir(), options.getDeployDir(), options.getExtraDir(), precompileOptions); linkEvent.end(); long compileDone = System.currentTimeMillis(); long delta = compileDone - compileStart; if (branch.isLoggable(TreeLogger.INFO)) { branch.log( TreeLogger.INFO, "Compilation succeeded -- " + String.format("%.3f", delta / 1000d) + "s"); } } } } 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; }
/** * according to .gwt.xml files generates a LinkedMap which has interfaces as keys array of * generators as values keys are sorted according order of <generate-with> elements in .gwt.xml * files * * @param context * @throws UnableToCompleteException */ private void fillUpGeneratorChainMap(TreeLogger logger, GeneratorContext context) throws UnableToCompleteException { GeneratorChain.customGenerators = new LinkedList<AbstractGenerator>(); GeneratorChain.replacers = new LinkedList<AbstractGenerator>(); GeneratorChain.thirdPartyGenerators = new LinkedHashMap<Generator, AbstractGenerator>(); ModuleDef moduleDef = ((CompilerContext) getPrivateField(context, "compilerContext")).getModule(); Rules rules = moduleDef.getRules(); Iterator<Rule> rulesIter = rules.iterator(); while (rulesIter.hasNext()) { Rule rul = rulesIter.next(); Generator gen = null; // =================replace with if (rul instanceof RuleReplaceWith) { String replaceClass = (String) getPrivateField(rul, "replacementTypeName"); gen = new ReplaceByGenerator(replaceClass); // gen = null; // =================generate with } else if (rul instanceof RuleGenerateWith) { Class<? extends Generator> generatorClass = (Class<? extends Generator>) getPrivateField(rul, "generatorClass"); Constructor<?> constructor; try { constructor = generatorClass.getDeclaredConstructor(); } catch (Exception e) { logger.log( Type.ERROR, "Unable to obtain default constructor of generator " + generatorClass); throw new UnableToCompleteException(); } constructor.setAccessible(true); try { gen = (Generator) constructor.newInstance(); } catch (Exception e) { logger.log(Type.ERROR, "Unable to create instance of generator " + generatorClass); throw new UnableToCompleteException(); } } if (gen != null) { if (gen instanceof AbstractGenerator) { GenPredicGroup newGroup = null; AbstractGenerator myGen = (AbstractGenerator) gen; if (GeneratorChain.customGenerators.contains(gen) || GeneratorChain.replacers.contains(gen)) { newGroup = addPredicsToExisting(rul, myGen.getConditions()); myGen.setConditions(newGroup); } else { newGroup = getGroupConditions(rul.getRootCondition().getConditions(), null); myGen.setConditions(newGroup); if (gen instanceof ReplaceByGenerator) { GeneratorChain.replacers.addFirst(myGen); } else { GeneratorChain.customGenerators.addFirst(myGen); } } } else { if (GeneratorChain.thirdPartyGenerators.containsKey(gen)) { AbstractGenerator myGen = GeneratorChain.thirdPartyGenerators.get(gen); GenPredicGroup newGroup = addPredicsToExisting(rul, myGen.getConditions()); myGen.setConditions(newGroup); GeneratorChain.thirdPartyGenerators.put(gen, myGen); } else { AbstractGenerator myGen = new AbstractGenerator() { @Override public String doGenerate( TreeLogger logger, GeneratorContext context, String typeName) throws UnableToCompleteException { return null; } }; myGen.setConditions(getGroupConditions(rul.getRootCondition().getConditions(), null)); GeneratorChain.thirdPartyGenerators.put(gen, myGen); } } } } }
/** Loads the module and configures it for SuperDevMode. (Does not restrict permutations.) */ private ModuleDef loadModule(TreeLogger logger) throws UnableToCompleteException { // make sure we get the latest version of any modified jar ZipFileClassPathEntry.clearCache(); ResourceOracleImpl.clearCache(); ResourceLoader resources = ResourceLoaders.forClassLoader(Thread.currentThread()); resources = ResourceLoaders.forPathAndFallback(options.getSourcePath(), resources); this.resourceLoader.set(resources); // ModuleDefLoader.loadFromResources() checks for modified .gwt.xml files. ModuleDef moduleDef = ModuleDefLoader.loadFromResources( logger, compilerContext, inputModuleName, resources, true); compilerContext = compilerContextBuilder.module(moduleDef).build(); // Undo all permutation restriction customizations from previous compiles. for (BindingProperty bindingProperty : moduleDef.getProperties().getBindingProperties()) { String[] allowedValues = bindingProperty.getAllowedValues(bindingProperty.getRootCondition()); bindingProperty.setRootGeneratedValues(allowedValues); } // A snapshot of the module's configuration before we modified it. ConfigProps config = new ConfigProps(moduleDef); // We need a cross-site linker. Automatically replace the default linker. if (IFrameLinker.class.isAssignableFrom(moduleDef.getActivePrimaryLinker())) { moduleDef.addLinker("xsiframe"); } // Check that we have a compatible linker. Class<? extends Linker> linker = moduleDef.getActivePrimaryLinker(); if (!CrossSiteIframeLinker.class.isAssignableFrom(linker)) { logger.log( TreeLogger.ERROR, "linkers other than CrossSiteIFrameLinker aren't supported. Found: " + linker.getName()); throw new UnableToCompleteException(); } // Deactivate precompress linker. if (moduleDef.deactivateLinker("precompress")) { logger.log(TreeLogger.WARN, "Deactivated PrecompressLinker"); } // Print a nice error if the superdevmode hook isn't present if (config.getStrings("devModeRedirectEnabled").isEmpty()) { throw new RuntimeException( "devModeRedirectEnabled isn't set for module: " + moduleDef.getName()); } // Disable the redirect hook here to make sure we don't have an infinite loop. // (There is another check in the JavaScript, but just in case.) overrideConfig(moduleDef, "devModeRedirectEnabled", "false"); // Turn off "installCode" if it's on because it makes debugging harder. // (If it's already off, don't change anything.) if (config.getBoolean("installCode", true)) { overrideConfig(moduleDef, "installCode", "false"); // Make sure installScriptJs is set to the default for compiling without installCode. overrideConfig( moduleDef, "installScriptJs", "com/google/gwt/core/ext/linker/impl/installScriptDirect.js"); } // override computeScriptBase.js to enable the "Compile" button overrideConfig( moduleDef, "computeScriptBaseJs", "com/google/gwt/dev/codeserver/computeScriptBase.js"); // Fix bug with SDM and Chrome 24+ where //@ sourceURL directives cause X-SourceMap header to be // ignored // Frustratingly, Chrome won't canonicalize a relative URL overrideConfig( moduleDef, "includeSourceMapUrl", "http://" + serverPrefix + SourceHandler.sourceMapLocationTemplate(moduleDef.getName())); // If present, set some config properties back to defaults. // (Needed for Google's server-side linker.) maybeOverrideConfig(moduleDef, "includeBootstrapInPrimaryFragment", "false"); maybeOverrideConfig( moduleDef, "permutationsJs", "com/google/gwt/core/ext/linker/impl/permutations.js"); maybeOverrideConfig( moduleDef, "propertiesJs", "com/google/gwt/core/ext/linker/impl/properties.js"); if (options.isIncrementalCompileEnabled()) { // CSSResourceGenerator needs to produce stable, unique naming for its input. // Currently on default settings CssResourceGenerator's obfuscation depends on // whole world knowledge and thus will produce collision in obfuscated mode, since in // incremental compiles that information is not available. // // TODO(dankurka): Once we do proper stable hashing of classes in CssResourceGenerator, we // can probably replace / remove this. maybeOverrideConfig(moduleDef, "CssResource.style", "stable"); } overrideBinding(moduleDef, "compiler.useSourceMaps", "true"); overrideBinding(moduleDef, "compiler.useSymbolMaps", "false"); overrideBinding(moduleDef, "superdevmode", "on"); return moduleDef; }
InputSummary(Map<String, String> bindingProperties, ModuleDef module) { this.bindingProperties = ImmutableMap.copyOf(bindingProperties); this.moduleLastModified = module.lastModified(); this.resourcesLastModified = module.getResourceLastModified(); this.filenameHash = module.getInputFilenameHash(); }
public String getRootModuleName() { if (rootModule == null) { return "UNKNOWN"; } return rootModule.getName(); }
private boolean doCompile(TreeLogger compileLogger, CompileDir compileDir, Job job) throws UnableToCompleteException { job.onProgress("Loading modules"); CompilerOptions loadOptions = new CompilerOptionsImpl(compileDir, inputModuleName, options); compilerContext = compilerContextBuilder.options(loadOptions).build(); ModuleDef module = loadModule(compileLogger); if (!Compiler.maybeRestrictProperties(compileLogger, module, loadOptions.getProperties())) { return false; } // We need to generate the stub before restricting permutations String recompileJs = generateModuleRecompileJs(module, compileLogger); Map<String, String> bindingProperties = restrictPermutations(compileLogger, module, job.getBindingProperties()); // Propagates module rename. String newModuleName = module.getName(); outputModuleName.set(newModuleName); // Check if we can skip the compile altogether. InputSummary input = new InputSummary(bindingProperties, module); if (input.equals(lastBuildInput)) { compileLogger.log(Type.INFO, "skipped compile because no input files have changed"); job.setCompileStrategy(CompileStrategy.SKIPPED); return true; } // Force a recompile if we don't succeed. lastBuildInput = null; job.onProgress("Compiling"); // TODO: use speed tracer to get more compiler events? CompilerOptions runOptions = new CompilerOptionsImpl(compileDir, newModuleName, options); compilerContext = compilerContextBuilder.options(runOptions).build(); MinimalRebuildCache minimalRebuildCache = new NullRebuildCache(); if (options.isIncrementalCompileEnabled()) { // Returns a copy of the intended cache, which is safe to modify in this compile. minimalRebuildCache = minimalRebuildCacheManager.getCache(inputModuleName, bindingProperties); } job.setCompileStrategy( minimalRebuildCache.isPopulated() ? CompileStrategy.INCREMENTAL : CompileStrategy.FULL); boolean success = new Compiler(runOptions, minimalRebuildCache).run(compileLogger, module); if (success) { publishedCompileDir = compileDir; lastBuildInput = input; if (options.isIncrementalCompileEnabled()) { minimalRebuildCacheManager.putCache( inputModuleName, bindingProperties, minimalRebuildCache); } String moduleName = outputModuleName.get(); writeRecompileNoCacheJs( new File(publishedCompileDir.getWarDir(), moduleName), moduleName, recompileJs, compileLogger); if (launcherDir != null) { launcherDir.update(module, compileDir, compileLogger); } } return success; }