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]); }
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]); }
/** 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 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()); }
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 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()); }
/** * 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; }
/** 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; }