blob: dcf2b1379b43209e622198a0ad0b17ae4a90a1a2 [file] [log] [blame]
/*
* Copyright 2011 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.codeserver;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.TreeLogger.Type;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.dev.MinimalRebuildCacheManager;
import com.google.gwt.dev.javac.UnitCache;
import com.google.gwt.dev.javac.UnitCacheSingleton;
import com.google.gwt.dev.util.DiskCachingUtil;
import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
import com.google.gwt.thirdparty.guava.common.collect.ImmutableMap;
import com.google.gwt.util.tools.Utility;
import java.io.File;
import java.io.IOException;
/**
* <p>This class contains the {@link #main main method} that starts the code server for
* "Super Dev Mode", a replacement for developer mode that doesn't require
* browser plugins.</p>
*
* <p>This tool is EXPERIMENTAL. There is no authentication, no encryption, no XSS
* protection, and it makes all source code on the GWT compiler's classpath available
* via HTTP. It is only safe to run on localhost (the default).</p>
*/
public class CodeServer {
/**
* Starts the code server. Shuts down the JVM if startup fails.
* @param args Command-line options that can be parsed by {@link Options}.
*/
public static void main(String[] args) throws Exception {
Options options = new Options();
if (!options.parseArgs(args)) {
System.exit(1);
}
main(options);
}
/**
* Starts the code server with the given options. Shuts down the JVM if startup fails.
*/
public static void main(Options options) {
if (options.isCompileTest()) {
PrintWriterTreeLogger logger = new PrintWriterTreeLogger();
logger.setMaxDetail(options.getLogLevel());
OutboxTable outboxTable;
try {
File baseCacheDir =
DiskCachingUtil.computePreferredCacheDir(options.getModuleNames(), logger);
UnitCache unitCache = UnitCacheSingleton.get(logger, null, baseCacheDir,
new CompilerOptionsImpl(options));
MinimalRebuildCacheManager minimalRebuildCacheManager =
createMinimalRebuildCacheManager(logger, options, baseCacheDir);
outboxTable = makeOutboxTable(options, logger, unitCache, minimalRebuildCacheManager);
} catch (Throwable t) {
t.printStackTrace();
System.out.println("FAIL");
System.exit(1);
return;
}
int retries = options.getCompileTestRecompiles();
for (int i = 0; i < retries; i++) {
System.out.println("\n### Recompile " + (i + 1) + "\n");
try {
// TODO: actually test recompiling here.
// (This is just running precompiles repeatedly.)
outboxTable.defaultCompileAll(logger);
} catch (Throwable t) {
t.printStackTrace();
System.out.println("FAIL");
System.exit(1);
}
}
System.out.println("PASS");
System.exit(0);
}
try {
start(options);
String url = "http://" + options.getPreferredHost() + ":" + options.getPort() + "/";
System.out.println();
System.out.println("The code server is ready at " + url);
} catch (UnableToCompleteException e) {
// Already logged.
System.exit(1);
} catch (Throwable t) {
t.printStackTrace();
System.exit(1);
}
}
private static MinimalRebuildCacheManager createMinimalRebuildCacheManager(
PrintWriterTreeLogger logger, Options options,File baseCacheDir) {
return new MinimalRebuildCacheManager(
logger,
baseCacheDir,
ImmutableMap.of(
"style", options.getOutput().name(),
"closureFormattedOutput", String.valueOf(options.isClosureFormattedOutput()),
"generateJsInteropExports", String.valueOf(options.shouldGenerateJsInteropExports()),
"exportFilters", options.getJsInteropExportFilter().toString(),
"methodDisplayMode", options.getMethodNameDisplayMode().name()));
}
/**
* Starts the code server with the given command line options. To shut it down, see
* {@link WebServer#stop}.
*
* <p>Only one code server should be started at a time because the GWT compiler uses
* a lot of static variables.</p>
*/
public static WebServer start(Options options) throws IOException, UnableToCompleteException {
PrintWriterTreeLogger topLogger = new PrintWriterTreeLogger();
topLogger.setMaxDetail(options.getLogLevel());
TreeLogger startupLogger = topLogger.branch(Type.INFO, "Super Dev Mode starting up");
File baseCacheDir =
DiskCachingUtil.computePreferredCacheDir(options.getModuleNames(), startupLogger);
UnitCache unitCache = UnitCacheSingleton.get(
startupLogger, null, baseCacheDir, new CompilerOptionsImpl(options));
MinimalRebuildCacheManager minimalRebuildCacheManager =
createMinimalRebuildCacheManager(topLogger, options, baseCacheDir);
OutboxTable outboxTable =
makeOutboxTable(options, startupLogger, unitCache, minimalRebuildCacheManager);
JobEventTable eventTable = new JobEventTable();
JobRunner runner = new JobRunner(eventTable, minimalRebuildCacheManager);
JsonExporter exporter = new JsonExporter(options, outboxTable);
SourceHandler sourceHandler = new SourceHandler(outboxTable, exporter);
SymbolMapHandler symbolMapHandler = new SymbolMapHandler(outboxTable);
WebServer webServer = new WebServer(sourceHandler, symbolMapHandler, exporter, outboxTable,
runner, eventTable, options.getBindAddress(), options.getPort());
webServer.start(topLogger);
return webServer;
}
/**
* Configures and compiles all the modules (unless {@link Options#getNoPrecompile} is false).
*/
private static OutboxTable makeOutboxTable(Options options, TreeLogger logger,
UnitCache unitCache, MinimalRebuildCacheManager minimalRebuildCacheManager)
throws IOException, UnableToCompleteException {
File workDir = ensureWorkDir(options);
logger.log(Type.INFO, "workDir: " + workDir);
LauncherDir launcherDir = LauncherDir.maybeCreate(options);
int nextOutboxId = 1;
OutboxTable outboxTable = new OutboxTable();
for (String moduleName : options.getModuleNames()) {
OutboxDir outboxDir = OutboxDir.create(new File(workDir, moduleName), logger);
Recompiler recompiler = new Recompiler(outboxDir, launcherDir, moduleName,
options, unitCache, minimalRebuildCacheManager);
// The id should be treated as an opaque string since we will change it again.
// TODO: change outbox id to include binding properties.
String outboxId = moduleName + "_" + nextOutboxId;
nextOutboxId++;
outboxTable.addOutbox(new Outbox(outboxId, recompiler, options, logger));
}
return outboxTable;
}
/**
* Ensures that we have a work directory. If specified via a flag, the
* directory must already exist. Otherwise, create a temp directory.
*/
private static File ensureWorkDir(Options options) throws IOException {
File workDir = options.getWorkDir();
if (workDir == null) {
workDir = Utility.makeTemporaryDirectory(null, "gwt-codeserver-");
} else {
if (!workDir.isDirectory()) {
throw new IOException("workspace directory doesn't exist: " + workDir);
}
}
return workDir;
}
}