Merge remote-tracking branch 'origin/master'

This commit is contained in:
Alexander Lobas
2012-02-28 21:27:30 +04:00
94 changed files with 1678 additions and 915 deletions
@@ -92,8 +92,9 @@ public class CompileServerManager implements ApplicationComponent{
private static final String DEFAULT_LOGGER_CONFIG = "defaultLogConfig.xml";
private volatile OSProcessHandler myProcessHandler;
private final File mySystemDirectory;
private volatile CompileServerClient myClient = new CompileServerClient();
private final SequentialTaskExecutor myTaskExecutor = new SequentialTaskExecutor(new SequentialTaskExecutor.AsyncTaskExecutor() {
@Nullable
private volatile CompileServerClient myClient;
private final SequentialTaskExecutor myTaskExecutor = new SequentialTaskExecutor(new AsyncTaskExecutor() {
public void submit(Runnable runnable) {
ApplicationManager.getApplication().executeOnPooledThread(runnable);
}
@@ -102,6 +103,12 @@ public class CompileServerManager implements ApplicationComponent{
private static final int MAKE_TRIGGER_DELAY = 5 * 1000 /*5 seconds*/;
private final Map<RequestFuture, Project> myAutomakeFutures = new HashMap<RequestFuture, Project>();
private final CompileServerClasspathManager myClasspathManager = new CompileServerClasspathManager();
private final AsyncTaskExecutor myAsyncExec = new AsyncTaskExecutor() {
@Override
public void submit(Runnable runnable) {
ApplicationManager.getApplication().executeOnPooledThread(runnable);
}
};
public CompileServerManager(final ProjectManager projectManager) {
myProjectManager = projectManager;
@@ -394,7 +401,8 @@ public class CompileServerManager implements ApplicationComponent{
}
final int port = NetUtils.findAvailableSocketPort();
final Process process = launchServer(port);
final long serverPingInterval = Registry.intValue("compiler.server.ping.interval", -1) * 1000L;
final Process process = launchServer(port, serverPingInterval);
final OSProcessHandler processHandler = new OSProcessHandler(process, null) {
protected boolean shouldDestroyProcessRecursively() {
@@ -451,7 +459,7 @@ public class CompileServerManager implements ApplicationComponent{
throw new Exception("Server startup failed: " + startupMsg);
}
CompileServerClient client = new CompileServerClient();
CompileServerClient client = new CompileServerClient(serverPingInterval, myAsyncExec);
boolean connected = false;
try {
connected = client.connect(NetUtils.getLocalHostString(), port);
@@ -545,7 +553,7 @@ public class CompileServerManager implements ApplicationComponent{
// commandLine.add((launcherUsed? "-J" : "") + "-D" + CharsetToolkit.FILE_ENCODING_PROPERTY + "=" + CharsetToolkit.getDefaultSystemCharset().name());
//}
private Process launchServer(final int port) throws ExecutionException {
private Process launchServer(final int port, long pingInterval) throws ExecutionException {
// validate tools.jar presence
final JavaCompiler systemCompiler = ToolProvider.getSystemJavaCompiler();
if (systemCompiler == null) {
@@ -561,7 +569,10 @@ public class CompileServerManager implements ApplicationComponent{
cmdLine.addParameter("-XX:ReservedCodeCacheSize=64m");
cmdLine.addParameter("-Xmx" + Registry.intValue("compiler.server.heap.size") + "m");
cmdLine.addParameter("-Djava.awt.headless=true");
//cmdLine.addParameter("-DuseJavaUtilZip");
//noinspection ConstantConditions
if (pingInterval > 0L) {
cmdLine.addParameter("-D" + GlobalOptions.PING_INTERVAL_MS_OPTION + "=" + pingInterval);
}
final String additionalOptions = Registry.stringValue("compiler.server.vm.options");
if (!StringUtil.isEmpty(additionalOptions)) {
final StringTokenizer tokenizer = new StringTokenizer(additionalOptions, " ", false);
@@ -1414,13 +1414,35 @@ public class TranslatingCompilerFilesMonitor implements ApplicationComponent {
private class MyVfsListener extends VirtualFileAdapter {
public void propertyChanged(final VirtualFilePropertyEvent event) {
if (VirtualFile.PROP_NAME.equals(event.getPropertyName())) {
final VirtualFile file = event.getFile();
final VirtualFile eventFile = event.getFile();
final VirtualFile parent = event.getParent();
if (parent != null) {
final String oldName = (String)event.getOldValue();
CompileServerManager.getInstance().notifyFilesDeleted(Collections.singleton(parent.getPath() + "/" + oldName));
final String root = parent.getPath() + "/" + oldName;
final Set<String> toMark;
if (eventFile.isDirectory()) {
toMark = new HashSet<String>();
new Object() {
void process(VirtualFile file, String filePath) {
if (file.isDirectory()) {
for (VirtualFile child : file.getChildren()) {
process(child, filePath + "/" + child.getName());
}
}
else {
toMark.add(filePath);
}
}
}.process(eventFile, root);
}
else {
toMark = Collections.singleton(root);
}
if (!toMark.isEmpty()) {
CompileServerManager.getInstance().notifyFilesDeleted(toMark);
}
}
markDirtyIfSource(file, false);
markDirtyIfSource(eventFile, false);
}
}
@@ -45,6 +45,8 @@ import com.intellij.openapi.wm.WindowManager;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.*;
import com.intellij.psi.impl.PsiDiamondTypeUtil;
import com.intellij.psi.impl.source.jsp.jspJava.JspCodeBlock;
import com.intellij.psi.impl.source.jsp.jspJava.JspHolderMethod;
import com.intellij.psi.impl.source.resolve.DefaultParameterTypeInferencePolicy;
import com.intellij.psi.impl.source.tree.java.ReplaceExpressionUtil;
import com.intellij.psi.util.PsiExpressionTrimRenderer;
@@ -534,11 +536,11 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
supportProvider != null &&
editor.getSettings().isVariableInplaceRenameEnabled() &&
supportProvider.isInplaceIntroduceAvailable(expr, nameSuggestionContext) &&
!ApplicationManager.getApplication().isUnitTestMode();
!ApplicationManager.getApplication().isUnitTestMode() &&
!isInJspHolderMethod(expr);
final boolean inFinalContext = occurenceManager.isInFinalContext();
final InputValidator validator = new InputValidator(this, project, anchorStatementIfAll, anchorStatement, occurenceManager);
final TypeSelectorManagerImpl typeSelectorManager = new TypeSelectorManagerImpl(project, originalType, expr, occurrences);
final boolean[] wasSucceed = new boolean[]{true};
final Pass<OccurrencesChooser.ReplaceChoice> callback = new Pass<OccurrencesChooser.ReplaceChoice>() {
@Override
@@ -599,6 +601,17 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
return wasSucceed[0];
}
private static boolean isInJspHolderMethod(PsiExpression expr) {
final PsiElement parent1 = expr.getParent();
if (parent1 == null) {
return false;
}
final PsiElement parent2 = parent1.getParent();
if (!(parent2 instanceof JspCodeBlock)) return false;
final PsiElement parent3 = parent2.getParent();
return parent3 instanceof JspHolderMethod;
}
/**
* @return true if write usages found
*/
@@ -0,0 +1,16 @@
package org.jetbrains.jps.api;
/**
* @author Eugene Zhuravlev
* Date: 2/28/12
*/
public interface AsyncTaskExecutor {
AsyncTaskExecutor DEFAULT = new AsyncTaskExecutor() {
@Override
public void submit(Runnable runnable) {
new Thread(runnable).start();
}
};
void submit(Runnable runnable);
}
@@ -10,6 +10,5 @@ public interface GlobalOptions {
String USE_EXTERNAL_JAVAC_OPTION = "use.external.javac.process";
String HOSTNAME_OPTION = "localhost.name";
String VM_EXE_PATH_OPTION = "vm.executable.path";
long SERVER_PING_PERIOD = 2000L; // 2 sec
String PING_INTERVAL_MS_OPTION = "server.ping.interval";
}
@@ -31,10 +31,6 @@ public class SequentialTaskExecutor {
}
};
public interface AsyncTaskExecutor {
void submit(Runnable runnable);
}
public SequentialTaskExecutor(AsyncTaskExecutor executor) {
myExecutor = executor;
}
@@ -18,15 +18,17 @@ import java.util.concurrent.TimeUnit;
public class CompileServerClient extends SimpleProtobufClient<JpsServerResponseHandler> {
private static final ScheduledThreadPoolExecutor ourPingService = ConcurrencyUtil.newSingleScheduledThreadExecutor("Compile server ping thread", Thread.MIN_PRIORITY);
private volatile ScheduledFuture<?> myPingFuture;
private final long myServerPingInterval;
public CompileServerClient() {
super(JpsRemoteProto.Message.getDefaultInstance(), new UUIDGetter() {
public CompileServerClient(long serverPingInterval, final AsyncTaskExecutor asyncExec) {
super(JpsRemoteProto.Message.getDefaultInstance(), asyncExec, new UUIDGetter() {
@NotNull
public UUID getSessionUUID(@NotNull MessageEvent e) {
final JpsRemoteProto.Message message = (JpsRemoteProto.Message)e.getMessage();
return ProtoUtil.fromProtoUUID(message.getSessionId());
}
});
myServerPingInterval = serverPingInterval;
}
@NotNull
@@ -89,15 +91,17 @@ public class CompileServerClient extends SimpleProtobufClient<JpsServerResponseH
@Override
protected void onConnect() {
myPingFuture = ourPingService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
final JpsRemoteProto.Message.Request ping = ProtoUtil.createPingRequest();
if (isConnected()) {
sendRequest(ping, null);
if (myServerPingInterval > 0L) {
myPingFuture = ourPingService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
final JpsRemoteProto.Message.Request ping = ProtoUtil.createPingRequest();
if (isConnected()) {
sendRequest(ping, null);
}
}
}
}, GlobalOptions.SERVER_PING_PERIOD, GlobalOptions.SERVER_PING_PERIOD, TimeUnit.MILLISECONDS);
}, myServerPingInterval, myServerPingInterval, TimeUnit.MILLISECONDS);
}
}
@Override
@@ -6,6 +6,7 @@ import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.api.AsyncTaskExecutor;
import org.jetbrains.jps.api.RequestFuture;
import java.util.ArrayList;
@@ -21,10 +22,12 @@ final class ProtobufClientMessageHandler<T extends ProtobufResponseHandler> exte
@NotNull
private final UUIDGetter myUuidGetter;
private final SimpleProtobufClient myClient;
private final AsyncTaskExecutor myAsyncExec;
public ProtobufClientMessageHandler(@NotNull UUIDGetter uuidGetter, SimpleProtobufClient client) {
public ProtobufClientMessageHandler(@NotNull UUIDGetter uuidGetter, SimpleProtobufClient client, AsyncTaskExecutor asyncExec) {
myUuidGetter = uuidGetter;
myClient = client;
myAsyncExec = asyncExec;
}
public final void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
@@ -89,7 +92,12 @@ final class ProtobufClientMessageHandler<T extends ProtobufResponseHandler> exte
}
finally {
// make sure the client is in disconnected state
myClient.scheduleDisconnect();
myAsyncExec.submit(new Runnable() {
@Override
public void run() {
myClient.disconnect();
}
});
}
}
@@ -10,6 +10,7 @@ import org.jboss.netty.handler.codec.protobuf.ProtobufEncoder;
import org.jboss.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import org.jboss.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.api.AsyncTaskExecutor;
import org.jetbrains.jps.api.RequestFuture;
import java.net.InetSocketAddress;
@@ -36,8 +37,8 @@ public class SimpleProtobufClient<T extends ProtobufResponseHandler> {
protected ChannelFuture myConnectFuture;
private final ProtobufClientMessageHandler<T> myMessageHandler;
public SimpleProtobufClient(final MessageLite msgDefaultInstance, final UUIDGetter uuidGetter) {
myMessageHandler = new ProtobufClientMessageHandler<T>(uuidGetter, this);
public SimpleProtobufClient(final MessageLite msgDefaultInstance, final AsyncTaskExecutor asyncExec, final UUIDGetter uuidGetter) {
myMessageHandler = new ProtobufClientMessageHandler<T>(uuidGetter, this, asyncExec);
myChannelFactory = new NioClientSocketChannelFactory(ourExecutor, ourExecutor, 1);
myPipelineFactory = new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() throws Exception {
@@ -105,15 +106,6 @@ public class SimpleProtobufClient<T extends ProtobufResponseHandler> {
protected void onDisconnect() {
}
public final void scheduleDisconnect() {
ourExecutor.submit(new Runnable() {
@Override
public void run() {
disconnect();
}
});
}
public final void disconnect() {
if (myState.compareAndSet(State.CONNECTED, State.DISCONNECTING)) {
try {
@@ -154,23 +146,31 @@ public class SimpleProtobufClient<T extends ProtobufResponseHandler> {
public final RequestFuture<T> sendMessage(final UUID messageId, MessageLite message, @Nullable final T responseHandler, @Nullable final RequestFuture.CancelAction<T> cancelAction) {
final RequestFuture<T> requestFuture = new RequestFuture<T>(responseHandler, messageId, cancelAction);
myMessageHandler.registerFuture(messageId, requestFuture);
final ChannelFuture channelFuture = Channels.write(myConnectFuture.getChannel(), message);
channelFuture.addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
try {
myMessageHandler.removeFuture(messageId);
if (responseHandler != null) {
responseHandler.sessionTerminated();
}
}
finally {
requestFuture.setDone();
final Channel channel = myConnectFuture.getChannel();
if (channel.isConnected()) {
Channels.write(channel, message).addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
notifyTerminated(messageId, requestFuture, responseHandler);
}
}
}
});
});
}
else {
notifyTerminated(messageId, requestFuture, responseHandler);
}
return requestFuture;
}
private void notifyTerminated(UUID messageId, RequestFuture<T> requestFuture, @Nullable T responseHandler) {
try {
myMessageHandler.removeFuture(messageId);
if (responseHandler != null) {
responseHandler.sessionTerminated();
}
}
finally {
requestFuture.setDone();
}
}
}
@@ -170,11 +170,11 @@ public class IncProjectBuilder {
runTasks(context, myBuilderRegistry.getBeforeTasks());
context.setCompilingTests(false);
context.processMessage(new ProgressMessage("Building production sources"));
context.processMessage(new ProgressMessage("Checking production sources"));
buildChunks(context, myProductionChunks);
context.setCompilingTests(true);
context.processMessage(new ProgressMessage("Building test sources"));
context.processMessage(new ProgressMessage("Checking test sources"));
buildChunks(context, myTestChunks);
context.processMessage(new ProgressMessage("Building project"));
@@ -290,10 +290,12 @@ public class IncProjectBuilder {
if (context.isMake()) {
// cleanup outputs
final Set<String> allChunkRemovedSources = new HashSet<String>();
final SourceToFormMapping sourceToFormMap = context.getDataManager().getSourceToFormMap();
for (Module module : chunk.getModules()) {
final Collection<String> deletedPaths = myProjectDescriptor.fsState.getDeletedPaths(module, context.isCompilingTests());
if (deletedPaths.isEmpty()) {
continue;
}
allChunkRemovedSources.addAll(deletedPaths);
final String moduleName = module.getName().toLowerCase(Locale.US);
@@ -304,29 +306,31 @@ public class IncProjectBuilder {
// deleting outputs corresponding to non-existing source
final Collection<String> outputs = sourceToOutputStorage.getState(deletedSource);
if (LOG.isDebugEnabled()) {
if (outputs.size() > 0) {
final String[] buffer = new String[outputs.size()];
int i = 0;
for (final String o : outputs) {
buffer[i++] = o;
}
Arrays.sort(buffer);
LOG.info("Cleaning output files:");
for(final String o : buffer) {
LOG.info(o);
}
LOG.info("End of files");
}
}
if (outputs != null) {
if (LOG.isDebugEnabled()) {
if (outputs.size() > 0) {
final String[] buffer = new String[outputs.size()];
int i = 0;
for (final String o : outputs) {
buffer[i++] = o;
}
Arrays.sort(buffer);
LOG.info("Cleaning output files:");
for(final String o : buffer) {
LOG.info(o);
}
LOG.info("End of files");
}
}
for (String output : outputs) {
FileUtil.delete(new File(output));
}
sourceToOutputStorage.remove(deletedSource);
}
// check if deleted source was associated with a form
final SourceToFormMapping sourceToFormMap = context.getDataManager().getSourceToFormMap();
final String formPath = sourceToFormMap.getState(deletedSource);
if (formPath != null) {
final File formFile = new File(formPath);
@@ -92,6 +92,7 @@ public abstract class AbstractStateStorage<Key, T> {
}
}
@Nullable
public T getState(Key key) throws IOException {
synchronized (myDataLock) {
return myMap.get(key);
@@ -5,6 +5,7 @@ import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.EnumeratorStringDescriptor;
import com.intellij.util.io.IOUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.*;
import java.util.ArrayList;
@@ -46,6 +47,7 @@ public final class SourceToOutputMapping extends AbstractStateStorage<String, Co
super.remove(FileUtil.toSystemIndependentName(srcPath));
}
@Nullable
@Override
public Collection<String> getState(@NotNull String srcPath) throws IOException {
return super.getState(FileUtil.toSystemIndependentName(srcPath));
@@ -2,21 +2,31 @@ package org.jetbrains.jps.javac;
import org.jboss.netty.channel.MessageEvent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.api.AsyncTaskExecutor;
import org.jetbrains.jps.api.RequestFuture;
import org.jetbrains.jps.client.SimpleProtobufClient;
import org.jetbrains.jps.client.UUIDGetter;
import java.io.File;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author Eugene Zhuravlev
* Date: 1/22/12
*/
public class JavacServerClient extends SimpleProtobufClient<JavacServerResponseHandler>{
private static final ExecutorService ourExecutors = Executors.newCachedThreadPool();
private static final AsyncTaskExecutor ASYNC_EXEC = new AsyncTaskExecutor() {
@Override
public void submit(Runnable runnable) {
ourExecutors.submit(runnable);
}
};
public JavacServerClient() {
super(JavacRemoteProto.Message.getDefaultInstance(), new UUIDGetter() {
super(JavacRemoteProto.Message.getDefaultInstance(), ASYNC_EXEC, new UUIDGetter() {
@NotNull
public UUID getSessionUUID(@NotNull MessageEvent e) {
final JavacRemoteProto.Message message = (JavacRemoteProto.Message)e.getMessage();
@@ -38,6 +38,7 @@ public class Server {
public static final String SERVER_SUCCESS_START_MESSAGE = "Compile Server started successfully. Listening on port: ";
public static final String SERVER_ERROR_START_MESSAGE = "Error starting Compile Server: ";
private static final String LOG_FILE_NAME = "log.xml";
private static final long PING_INTERVAL = Long.parseLong(System.getProperty(GlobalOptions.PING_INTERVAL_MS_OPTION, "-1"));
private final ChannelGroup myAllOpenChannels = new DefaultChannelGroup("compile-server");
private final ChannelFactory myChannelFactory;
@@ -45,6 +46,7 @@ public class Server {
private final ExecutorService myBuildsExecutor;
private volatile long myLastPingTime = -1L;
private final ScheduledExecutorService myScheduler;
private final ServerMessageHandler myMessageHandler;
public Server(File systemDir) {
Paths.getInstance().setSystemRoot(systemDir);
@@ -53,7 +55,7 @@ public class Server {
myBuildsExecutor = Executors.newFixedThreadPool(MAX_SIMULTANEOUS_BUILD_SESSIONS);
myChannelFactory = new NioServerSocketChannelFactory(threadPool, threadPool, 1);
final ChannelRegistrar channelRegistrar = new ChannelRegistrar();
final ServerMessageHandler messageHandler = new ServerMessageHandler(myBuildsExecutor, this);
myMessageHandler = new ServerMessageHandler(myBuildsExecutor, this);
myPipelineFactory = new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() throws Exception {
return Channels.pipeline(
@@ -62,7 +64,7 @@ public class Server {
new ProtobufDecoder(JpsRemoteProto.Message.getDefaultInstance()),
new ProtobufVarint32LengthFieldPrepender(),
new ProtobufEncoder(),
messageHandler
myMessageHandler
);
}
};
@@ -76,11 +78,14 @@ public class Server {
final Channel serverChannel = bootstrap.bind(new InetSocketAddress(listenPort));
myAllOpenChannels.add(serverChannel);
startIdleMonitor();
startActivityMonitor();
}
private void startIdleMonitor() {
final long allowedIdlePeriod = 2 * GlobalOptions.SERVER_PING_PERIOD;
private void startActivityMonitor() {
if (PING_INTERVAL <= 0L) {
return;
}
final long allowedIdlePeriod = 2 * PING_INTERVAL;
myScheduler.scheduleAtFixedRate(new Runnable() {
private long myStartTime;
@Override
@@ -90,16 +95,16 @@ public class Server {
if (lastPing > 0L) {
final long elapsed = now - lastPing;
if (elapsed > allowedIdlePeriod) {
doStop();
doStop(elapsed);
}
}
else {
final long start = myStartTime;
if (start > 0) {
final long elapsed = now - start;
if (elapsed > 5 * GlobalOptions.SERVER_PING_PERIOD) {
if (elapsed > 5 * PING_INTERVAL) {
// no pings received since start
doStop();
doStop(elapsed);
}
}
else {
@@ -108,12 +113,15 @@ public class Server {
}
}
private void doStop() {
try {
stop();
}
finally {
System.exit(0);
private void doStop(long elapsedTime) {
if (!myMessageHandler.hasRunningBuilds()) {
try {
System.out.println("Stopping compile server; reason: no pings from client received in " + elapsedTime + " ms");
stop();
}
finally {
System.exit(0);
}
}
}
}, allowedIdlePeriod, allowedIdlePeriod, TimeUnit.MILLISECONDS);
@@ -36,6 +36,7 @@ class ServerMessageHandler extends SimpleChannelHandler {
}
public void messageReceived(final ChannelHandlerContext ctx, MessageEvent e) throws Exception {
myServer.pingReceived();
final JpsRemoteProto.Message message = (JpsRemoteProto.Message)e.getMessage();
final UUID sessionId = ProtoUtil.fromProtoUUID(message.getSessionId());
@@ -86,7 +87,6 @@ class ServerMessageHandler extends SimpleChannelHandler {
break;
case SHUTDOWN_COMMAND :
// todo pay attention to policy
myBuildsExecutor.submit(new Runnable() {
public void run() {
final List<RunnableFuture> futures = new ArrayList<RunnableFuture>();
@@ -139,7 +139,6 @@ class ServerMessageHandler extends SimpleChannelHandler {
reply = ProtoUtil.toMessage(sessionId, ProtoUtil.createCommandCompletedEvent(null));
break;
case PING:
myServer.pingReceived();
reply = ProtoUtil.toMessage(sessionId, ProtoUtil.createCommandCompletedEvent(null));
default:
reply = ProtoUtil.toMessage(sessionId, ProtoUtil.createFailure("Unknown request: " + message));
@@ -213,7 +212,7 @@ class ServerMessageHandler extends SimpleChannelHandler {
synchronized (myTaskExecutors) {
SequentialTaskExecutor executor = myTaskExecutors.get(projectId);
if (executor == null) {
executor = new SequentialTaskExecutor(new SequentialTaskExecutor.AsyncTaskExecutor() {
executor = new SequentialTaskExecutor(new AsyncTaskExecutor() {
@Override
public void submit(Runnable runnable) {
myBuildsExecutor.submit(runnable);
@@ -226,12 +225,14 @@ class ServerMessageHandler extends SimpleChannelHandler {
}
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
if (this == ctx.getPipeline().getLast()) {
LOG.error(e);
}
LOG.error(e);
ctx.sendUpstream(e);
}
public boolean hasRunningBuilds() {
return !myBuildsInProgress.isEmpty();
}
private class CompilationTask implements Runnable, CanceledStatus {
private final UUID mySessionId;
@@ -260,8 +260,10 @@ public class Mappings {
private abstract class PostPass {
boolean myPerformed = false;
abstract void perform();
void run () {
void run() {
if (!myPerformed) {
myPerformed = true;
perform();
@@ -1605,7 +1607,7 @@ public class Mappings {
}
}
if (delta.isDifferentiated() && false) {
if (delta.isDifferentiated()) {
for (DependencyContext.S c : delta.getChangedClasses()) {
myClassToSubclasses.remove(c);
@@ -1767,9 +1769,9 @@ public class Mappings {
@Override
public void registerImports(final String className, final Collection<String> imports, Collection<String> staticImports) {
for (String s: staticImports) {
for (String s : staticImports) {
int i = s.length() - 1;
for (; s.charAt(i) != '.'; i--);
for (; s.charAt(i) != '.'; i--) ;
imports.add(s.substring(0, i));
}
@@ -0,0 +1,22 @@
package org.jetbrains.jps.idea;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.Module;
import java.util.Collections;
import java.util.List;
/**
* @author nik
*/
public abstract class AdditionalRootsProviderService {
@NotNull
public List<String> getAdditionalSourceRoots(@NotNull Module module) {
return Collections.emptyList();
}
@NotNull
public List<String> getAdditionalTestSourceRoots(@NotNull Module module) {
return Collections.emptyList();
}
}
@@ -16,6 +16,7 @@ public class IdeaProjectLoader {
private Map<String, String> pathVariables
private ProjectMacroExpander projectMacroExpander
private ProjectLoadingErrorReporter errorReporter
private static final OwnServiceLoader<AdditionalRootsProviderService> rootsProviderLoader = OwnServiceLoader.load(AdditionalRootsProviderService.class)
public static String guessHome(Script script) {
File home = new File(script["gant.file"].substring("file:".length()))
@@ -508,6 +509,15 @@ public class IdeaProjectLoader {
currentModule.languageLevel = convertLanguageLevel(languageLevel)
}
rootsProviderLoader.each {AdditionalRootsProviderService service ->
def sourceRoots = service.getAdditionalSourceRoots(currentModule)
def testSourceRoots = service.getAdditionalTestSourceRoots(currentModule)
currentModule.sourceRoots.addAll(sourceRoots)
currentModule.testRoots.addAll(testSourceRoots)
if (!sourceRoots.isEmpty() || !testSourceRoots.isEmpty()) {
srcFolderExists = true
}
}
if (srcFolderExists) {
if (componentTag."@inherit-compiler-output" == "true") {
if (projectOutputPath == null) {
@@ -18,6 +18,7 @@ package com.intellij.psi.search.scope;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.search.ProjectScope;
import com.intellij.psi.search.scope.packageSet.AbstractPackageSet;
import com.intellij.psi.search.scope.packageSet.NamedScope;
import com.intellij.psi.search.scope.packageSet.NamedScopesHolder;
@@ -37,8 +38,7 @@ public class NonProjectFilesScope extends NamedScope {
super(NAME, new AbstractPackageSet("NonProject") {
public boolean contains(VirtualFile file, NamedScopesHolder holder) {
if (file == null) return true;
final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(holder.getProject()).getFileIndex();
return !(holder.getProject().isInitialized() && !fileIndex.isIgnored(file) && fileIndex.getContentRootForFile(file) != null);
return !ProjectScope.getProjectScope(holder.getProject()).contains(file);
}
});
}
@@ -107,7 +107,7 @@ public class OptimizeImportsAction extends AnAction {
if (ApplicationManager.getApplication().isUnitTestMode()) {
includeSubdirectories = processDirectory = false;
}
else if (!EditorSettingsExternalizable.getInstance().getOptions().SHOW_OPIMIZE_IMPORTS_DIALOG) {
else if (!EditorSettingsExternalizable.getInstance().getOptions().SHOW_OPIMIZE_IMPORTS_DIALOG && file != null) {
includeSubdirectories = processDirectory = false;
}
else {
@@ -16,6 +16,7 @@
package com.intellij.codeInsight.editorActions.enter;
import com.intellij.lang.Language;
import com.intellij.lang.LanguageFormatting;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.editor.Document;
@@ -122,6 +123,9 @@ public class BaseIndentEnterHandler extends EnterHandlerDelegateAdapter {
return Result.Stop;
}
if (LanguageFormatting.INSTANCE.forLanguage(myLanguage) != null) {
return Result.Continue;
}
EditorModificationUtil.insertStringAtCaret(editor, "\n" + lineIndent);
editor.getCaretModel().moveToLogicalPosition(new LogicalPosition(lineNumber + 1, lineIndent.length()));
return Result.Stop;
@@ -96,7 +96,17 @@ public class ConfigurationSettingsEditorWrapper extends SettingsEditor<RunnerAnd
}
}
myStepsPanel.setLayout(new GridLayout((stepsRows.size() + 1) / 2, 1, UIUtil.DEFAULT_HGAP, UIUtil.DEFAULT_VGAP));
int maxStepRowWidth = 0;
for (StepBeforeLaunchRow stepRow : stepsRows) {
maxStepRowWidth = Math.max(maxStepRowWidth, stepRow.getPreferredSize().width);
}
if (maxStepRowWidth * 3 < getComponent().getPreferredSize().width || maxStepRowWidth < 200) {
myStepsPanel.setLayout(new GridLayout(0, 3, UIUtil.DEFAULT_HGAP, UIUtil.DEFAULT_VGAP));
} else if (maxStepRowWidth * 2 < getComponent().getPreferredSize().width || maxStepRowWidth < 300) {
myStepsPanel.setLayout(new GridLayout(0, 2, UIUtil.DEFAULT_HGAP, UIUtil.DEFAULT_VGAP));
} else {
myStepsPanel.setLayout(new GridLayout(0, 1, UIUtil.DEFAULT_HGAP, UIUtil.DEFAULT_VGAP));
}
for (StepBeforeLaunchRow stepRow : stepsRows) {
myStepsPanel.add(stepRow);
}
@@ -142,6 +142,7 @@ public abstract class DialogWrapper {
};
private List<JBOptionButton> myOptionsButtons = new ArrayList<JBOptionButton>();
private int myCurrentOptionsButtonIndex = -1;
private boolean myResizeInProgress = false;
protected String getDoNotShowMessage() {
return CommonBundle.message("dialog.options.do.not.show");
@@ -167,6 +168,14 @@ public abstract class DialogWrapper {
*/
protected DialogWrapper(Project project, boolean canBeParent) {
myPeer = createPeer(project, canBeParent);
myPeer.getWindow().addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
if (!myResizeInProgress) {
myActualSize = myPeer.getSize();
}
}
});
createDefaultActions();
}
@@ -1611,8 +1620,10 @@ public abstract class DialogWrapper {
private void resizeWithAnimation(final Dimension size) {
//todo[kb]: fix this PITA
myResizeInProgress = true;
if (!Registry.is("enable.animation.on.dialogs")) {
setSize(size.width, size.height);
myResizeInProgress = false;
return;
}
@@ -1637,6 +1648,7 @@ public abstract class DialogWrapper {
if (myErrorText.shouldBeVisible()) {
myErrorText.setVisible(true);
}
myResizeInProgress = false;
}
}.start();
}
@@ -30,6 +30,7 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.newvfs.NewVirtualFile;
import com.intellij.openapi.vfs.newvfs.RefreshQueue;
import com.intellij.openapi.vfs.newvfs.RefreshSession;
import com.intellij.util.Alarm;
import org.jetbrains.annotations.NotNull;
import java.beans.PropertyChangeEvent;
@@ -48,6 +49,7 @@ public class SaveAndSyncHandler implements ApplicationComponent {
private final AtomicInteger myBlockSaveOnFrameDeactivationCount = new AtomicInteger();
private final AtomicInteger myBlockSyncOnFrameActivationCount = new AtomicInteger();
private final Alarm myRefreshDelayAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD);
public static SaveAndSyncHandler getInstance(){
return ApplicationManager.getApplication().getComponent(SaveAndSyncHandler.class);
@@ -157,7 +159,8 @@ public class SaveAndSyncHandler implements ApplicationComponent {
}
ApplicationManager.getApplication().invokeLater(new Runnable() {
myRefreshDelayAlarm.cancelAllRequests();
myRefreshDelayAlarm.addRequest(new Runnable() {
@Override
public void run() {
if (canSyncOrSave()) {
@@ -166,7 +169,7 @@ public class SaveAndSyncHandler implements ApplicationComponent {
maybeRefresh(ModalityState.NON_MODAL);
}
}, ModalityState.NON_MODAL);
}, 300, ModalityState.NON_MODAL);
if (LOG.isDebugEnabled()) {
LOG.debug("exit: synchronize()");
@@ -213,4 +216,4 @@ public class SaveAndSyncHandler implements ApplicationComponent {
public void unblockSyncOnFrameActivation() {
myBlockSyncOnFrameActivationCount.decrementAndGet();
}
}
}
@@ -19,8 +19,7 @@ import com.intellij.ide.presentation.VirtualFilePresentation;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.actionSystem.DataProvider;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.application.AccessToken;
import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.application.ex.ApplicationManagerEx;
import com.intellij.openapi.diff.DiffRequest;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
@@ -275,15 +274,14 @@ public class VirtualFileDiffElement extends DiffElement<VirtualFile> {
}
if (!docsToSave.isEmpty()) {
AccessToken token = WriteAction.start();
try {
for (Document document : docsToSave) {
manager.saveDocument(document);
ApplicationManagerEx.getApplicationEx().runEdtSafeAction(new Runnable() {
@Override
public void run() {
for (Document document : docsToSave) {
manager.saveDocument(document);
}
}
}
finally {
token.finish();
}
});
}
if (!FileWatcher.getInstance().isWatched(virtualFile)) {
((NewVirtualFile)virtualFile).markDirtyRecursively();
@@ -16,6 +16,7 @@
package com.intellij.openapi.diff.impl.highlighting;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.diff.DiffContent;
import com.intellij.openapi.diff.impl.ContentChangeListener;
@@ -24,7 +25,9 @@ import com.intellij.openapi.diff.impl.DiffVersionComponent;
import com.intellij.openapi.diff.impl.util.ContentDocumentListener;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.EditorFactory;
import com.intellij.openapi.editor.ex.DocumentEx;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.editor.impl.EditorImpl;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.FileEditorProvider;
import com.intellij.openapi.fileEditor.TextEditor;
@@ -65,11 +68,11 @@ class EditorPlaceHolder extends DiffMarkup implements DiffVersionComponent {
}
public void setContent(final DiffContent content) {
runRegisteredDisposables();
myContent = content;
if (myContent != null) {
Document document = myContent.getDocument();
final Document document = myContent.getDocument();
if (myContent.isBinary() || document == null || myContent.getContentType() instanceof UIBasedFileType) {
runRegisteredDisposables();
final VirtualFile file = myContent.getFile();
if (file != null) {
final FileEditorProvider[] providers = FileEditorProviderManager.getInstance().getProviders(getProject(), file);
@@ -93,6 +96,22 @@ class EditorPlaceHolder extends DiffMarkup implements DiffVersionComponent {
}
}
else {
if (myEditor != null) {
final DocumentEx doc = ((EditorImpl)myEditor).getDocument();
if (doc != null) {
final boolean wasWriteable = doc.isWritable();
doc.setReadOnly(false);
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
doc.setText(document.getText());
}
});
doc.setReadOnly(! wasWriteable);
return;
}
}
runRegisteredDisposables();
final EditorFactory editorFactory = EditorFactory.getInstance();
myEditor = DiffUtil.createEditor(document, getProject(), false);
addDisposable(new Disposable() {
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
* Copyright 2000-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,6 +15,7 @@
*/
package com.intellij.openapi.fileEditor.impl;
import com.google.common.collect.Lists;
import com.intellij.ide.ui.UISettings;
import com.intellij.openapi.actionSystem.KeyboardShortcut;
import com.intellij.openapi.actionSystem.Shortcut;
@@ -65,8 +66,8 @@ import java.util.List;
*/
public class EditorsSplitters extends JPanel {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.fileEditor.impl.EditorsSplitters");
private EditorWindow myCurrentWindow;
private VirtualFile myCurrentFile;
private final FileEditorManagerImpl myManager;
private Element mySplittersElement; // temporarily used during initialization
int myInsideChange = 0;
@@ -97,7 +98,6 @@ public class EditorsSplitters extends JPanel {
removeAll();
myWindows.clear();
setCurrentWindow(null);
myCurrentFile = null;
repaint (); // revalidate doesn't repaint correctly after "Close All"
}
@@ -151,13 +151,12 @@ public class EditorsSplitters extends JPanel {
}
if (showEmptyText()) {
final boolean darkerColors = UIUtil.isUnderAquaLookAndFeel() || UIUtil.isUnderGTKLookAndFeel();
UIUtil.applyRenderingHints(g);
g.setColor(darkerColors ? Gray._100 : Color.DARK_GRAY);
g.setColor(Gray._100);
g.setFont(UIUtil.getLabelFont().deriveFont(18f));
final UIUtil.TextPainter painter = new UIUtil.TextPainter(1.4f);
painter.appendLine("No files are open").underlined(darkerColors ? Gray._150 : Color.DARK_GRAY);
final UIUtil.TextPainter painter = new UIUtil.TextPainter().withShadow(true).withLineSpacing(1.4f);
painter.appendLine("No files are open").underlined(Gray._150);
if (!isProjectViewVisible()) {
painter.appendLine("Open Project View with " + KeymapUtil.getShortcutText(new KeyboardShortcut(
@@ -325,8 +324,7 @@ public class EditorsSplitters extends JPanel {
window = findWindowWith(panel);
LOG.assertTrue(window != null);
}
//noinspection unchecked
final List<Element> children = new ArrayList<Element>(leaf.getChildren("file"));
@SuppressWarnings("unchecked") final List<Element> children = Lists.newArrayList(leaf.getChildren("file"));
VirtualFile currentFile = null;
if (UISettings.getInstance().ACTIVATE_RIGHT_EDITOR_ON_CLOSE) {
@@ -506,7 +504,7 @@ public class EditorsSplitters extends JPanel {
return myInsideChange > 0;
}
private void setCurrentWindow(final EditorWindow currentWindow) {
private void setCurrentWindow(@Nullable final EditorWindow currentWindow) {
myCurrentWindow = currentWindow;
}
@@ -785,16 +783,11 @@ public class EditorsSplitters extends JPanel {
private final class MyFocusWatcher extends FocusWatcher {
protected void focusedComponentChanged(final Component component, final AWTEvent cause) {
EditorWindow newWindow = null;
VirtualFile newFile = null;
if (component != null) {
newWindow = findWindowWith(component);
if (newWindow != null) {
newFile = newWindow.getSelectedFile();
}
}
myCurrentFile = newFile;
setCurrentWindow(newWindow);
setCurrentWindow(newWindow, false);
}
@@ -0,0 +1,60 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* 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.intellij.openapi.vcs.changes;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Getter;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.search.PsiSearchHelper;
import com.intellij.psi.search.TodoItem;
import com.intellij.util.Consumer;
import java.util.List;
/**
* Created with IntelliJ IDEA.
* User: Irina.Chernushina
* Date: 2/28/12
* Time: 6:06 PM
*/
public class TodoForBaseRevision extends TodoForRanges {
private final Getter<Object> myGetter;
private final Consumer<Object> mySaver;
public TodoForBaseRevision(Project project,
List<TextRange> ranges,
int additionalOffset,
String name,
String text,
boolean revision, FileType type, final Getter<Object> cacheGetter,
final Consumer<Object> cacheSaver) {
super(project, ranges, additionalOffset, name, text, revision, type);
myGetter = cacheGetter;
mySaver = cacheSaver;
}
@Override
protected TodoItem[] getTodoItems() {
final TodoItem[] items = (TodoItem[])myGetter.get();
if (items != null) return items;
final TodoItem[] todoItems = getTodoForText(PsiSearchHelper.SERVICE.getInstance(myProject));
if (todoItems != null) {
mySaver.consume(todoItems);
}
return todoItems;
}
}
@@ -0,0 +1,67 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* 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.intellij.openapi.vcs.changes;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiFileFactory;
import com.intellij.psi.PsiManager;
import com.intellij.psi.search.PsiSearchHelper;
import com.intellij.psi.search.TodoItem;
import java.util.List;
/**
* Created with IntelliJ IDEA.
* User: Irina.Chernushina
* Date: 2/28/12
* Time: 4:48 PM
*/
public class TodoForExistingFile extends TodoForRanges {
private final VirtualFile myFile;
public TodoForExistingFile(Project project,
List<TextRange> ranges,
int additionalOffset,
String name,
String text,
boolean revision, FileType type, VirtualFile file) {
super(project, ranges, additionalOffset, name, text, revision, type);
myFile = file;
}
protected TodoItem[] getTodoItems() {
return ApplicationManager.getApplication().runReadAction(new Computable<TodoItem[]>() {
@Override
public TodoItem[] compute() {
final PsiSearchHelper helper = PsiSearchHelper.SERVICE.getInstance(myProject);
PsiFile psiFile = myFile == null ? null : PsiManager.getInstance(myProject).findFile(myFile);
if (psiFile != null) {
return helper.findTodoItems(psiFile);
}
return getTodoForText(helper);
}
});
}
}
@@ -41,36 +41,32 @@ import java.util.List;
* Date: 9/9/11
* Time: 9:55 AM
*/
public class TodoForRanges {
private final Project myProject;
private final String myFileName;
private final String myText;
private final boolean myOldRevision;
public abstract class TodoForRanges {
protected final Project myProject;
private final List<TextRange> myRanges;
private final FileType myFileType;
private final int myAdditionalOffset;
protected final String myFileName;
protected final String myText;
protected final boolean myOldRevision;
protected final FileType myFileType;
public TodoForRanges(final Project project, final String fileName, final String text, final boolean oldRevision,
final List<TextRange> ranges, FileType fileType, int additionalOffset) {
protected TodoForRanges(final Project project,
final List<TextRange> ranges,
int additionalOffset,
String name,
String text,
boolean revision, FileType type) {
myProject = project;
myFileName = fileName;
myText = text;
myOldRevision = oldRevision;
myRanges = ranges;
myFileType = fileType;
myAdditionalOffset = additionalOffset;
myFileName = name;
myText = text;
myOldRevision = revision;
myFileType = type;
}
public List<Pair<TextRange, TextAttributes>> execute() {
final TodoItem[] todoItems = ApplicationManager.getApplication().runReadAction(new Computable<TodoItem[]>() {
@Override
public TodoItem[] compute() {
final PsiFile psiFile = PsiFileFactory.getInstance(myProject).createFileFromText((myOldRevision ? "old" : "") + myFileName, myFileType, myText);
final PsiSearchHelper helper = PsiSearchHelper.SERVICE.getInstance(myProject);
return helper.findTodoItemsLight(psiFile);
}
});
final TodoItem[] todoItems = getTodoItems();
final StepIntersection<TodoItem, TextRange> stepIntersection =
new StepIntersection<TodoItem, TextRange>(new Convertor<TodoItem, TextRange>() {
@@ -105,4 +101,16 @@ public class TodoForRanges {
}
return result;
}
protected abstract TodoItem[] getTodoItems();
protected TodoItem[] getTodoForText(PsiSearchHelper helper) {
final PsiFile psiFile = ApplicationManager.getApplication().runReadAction(new Computable<PsiFile>() {
@Override
public PsiFile compute() {
return PsiFileFactory.getInstance(myProject).createFileFromText((myOldRevision ? "old" : "") + myFileName, myFileType, myText);
}
});
return helper.findTodoItemsLight(psiFile);
}
}
@@ -145,6 +145,9 @@ compiler.server.use.memory.temp.cache.description=Store temporary data in memory
compiler.server.use.external.javac.process=true
compiler.server.use.external.javac.process.description=Run javac compiler in external process (allows to run compile server with smaller heap size)
compiler.server.ping.interval=5
compiler.server.ping.interval.description=Interval in seconds between ping requests the IDE periodically sends to server. If server does not receive pings for some time, it shuts down. Specify -1 to disable this feature.
compiler.server.debug.port=-1
#compiler.server.javac.debug.port=-1
@@ -38,8 +38,8 @@
<action id="CIDR.CreateXcodeProject">
<keyboard-shortcut first-keystroke="shift meta N" />
</action>
<action id="CloseProject">
<keyboard-shortcut first-keystroke="meta W" />
<action id="CloseContent">
<keyboard-shortcut first-keystroke="meta W"/>
</action>
<action id="CodeCompletion">
<keyboard-shortcut first-keystroke="control SPACE" />
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
* Copyright 2000-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,10 +19,7 @@ import com.intellij.openapi.Disposable;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.ColorUtil;
import com.intellij.ui.Gray;
import com.intellij.ui.PanelWithAnchor;
import com.intellij.ui.SideBorder;
import com.intellij.ui.*;
import com.intellij.util.ArrayUtil;
import com.intellij.util.PairFunction;
import com.intellij.util.Processor;
@@ -2308,25 +2305,48 @@ public class UIUtil {
public static class TextPainter {
private List<Pair<String, LineInfo>> myLines = new ArrayList<Pair<String, LineInfo>>();
private boolean myDrawMacShadow;
private Color myMacShadowColor;
private boolean myDrawShadow;
private Color myShadowColor;
private float myLineSpacing;
public TextPainter() {
this(true, Gray._220, 1.0f);
myDrawShadow = isUnderAquaLookAndFeel();
myShadowColor = Gray._220;
myLineSpacing = 1.0f;
}
/** @deprecated use {@linkplain #withLineSpacing(float)} (to remove in IDEA 12) */
@SuppressWarnings("UnusedDeclaration")
public TextPainter(final float lineSpacing) {
this(true, Gray._220, lineSpacing);
myDrawShadow = isUnderAquaLookAndFeel();
myShadowColor = Gray._220;
myLineSpacing = lineSpacing;
}
public TextPainter(final boolean drawMacShadow, final Color shadowColor, final float lineSpacing) {
myDrawMacShadow = drawMacShadow;
myMacShadowColor = shadowColor;
/** @deprecated use {@linkplain #withShadow(boolean, java.awt.Color)} and {@linkplain #withLineSpacing(float)} (to remove in IDEA 12) */
@SuppressWarnings("UnusedDeclaration")
public TextPainter(final boolean drawShadow, final Color shadowColor, final float lineSpacing) {
myDrawShadow = drawShadow;
myShadowColor = shadowColor;
myLineSpacing = lineSpacing;
}
public TextPainter withShadow(final boolean drawShadow) {
myDrawShadow = drawShadow;
return this;
}
public TextPainter withShadow(final boolean drawShadow, final Color shadowColor) {
myDrawShadow = drawShadow;
myShadowColor = shadowColor;
return this;
}
public TextPainter withLineSpacing(final float lineSpacing) {
myLineSpacing = lineSpacing;
return this;
}
public TextPainter appendLine(final String text) {
if (text == null || text.length() == 0) return this;
myLines.add(Pair.create(text, new LineInfo()));
@@ -2432,9 +2452,9 @@ public class UIUtil {
xOffset = x + (maxWidth[0] - fm.stringWidth(pair.getFirst())) / 2;
}
if (myDrawMacShadow && UIUtil.isUnderAquaLookAndFeel()) {
if (myDrawShadow) {
final Color oldColor = g.getColor();
g.setColor(myMacShadowColor);
g.setColor(myShadowColor);
if (info.withBullet) {
g.drawString(info.bulletChar + " ", x - fm.stringWidth(" " + info.bulletChar), yOffset[0] + 1);
@@ -2462,9 +2482,9 @@ public class UIUtil {
g.setColor(c);
}
if (myDrawMacShadow && UIUtil.isUnderAquaLookAndFeel()) {
if (myDrawShadow) {
c = g.getColor();
g.setColor(myMacShadowColor);
g.setColor(myShadowColor);
g.drawLine(x - maxBulletWidth[0] - 10, yOffset[0] + fm.getDescent() + 1, x + maxWidth[0] + 10, yOffset[0] + fm.getDescent() + 1);
g.setColor(c);
}
@@ -2533,23 +2553,30 @@ public class UIUtil {
return false;
}
public static void mergeComponentsWithAnchor(PanelWithAnchor c1, PanelWithAnchor c2) {
if (c1 == null || c2 == null) return;
if (c1.getAnchor() == null) {
c1.setAnchor(c2.getAnchor());
} else {
if (c2.getAnchor() == null) {
c2.setAnchor(c1.getAnchor());
} else {
JComponent anchor = c1.getAnchor().getPreferredSize().getWidth() > c2.getAnchor().getPreferredSize().getWidth() ?
c1.getAnchor() : c2.getAnchor();
c2.setAnchor(anchor);
c1.setAnchor(anchor);
}
}
@Nullable
public static JComponent mergeComponentsWithAnchor(PanelWithAnchor...panels) {
return mergeComponentsWithAnchor(Arrays.asList(panels));
}
@Nullable
public static JComponent mergeComponentsWithAnchor(Collection<? extends PanelWithAnchor> panels) {
JComponent tempAnchor = null;
int maxWidth = 0;
for (PanelWithAnchor panel : panels) {
if (panel == null) continue;
if (panel.getAnchor() == null) continue;
if (maxWidth < panel.getAnchor().getPreferredSize().width) {
maxWidth = panel.getAnchor().getPreferredSize().width;
tempAnchor = panel.getAnchor();
}
}
for (PanelWithAnchor panel : panels) {
if (panel == null) continue;
panel.setAnchor(tempAnchor);
}
return tempAnchor;
}
public static void setNotOpaqueRecursively(@NotNull Component component) {
if (!isUnderAquaLookAndFeel()) return;
@@ -46,14 +46,14 @@ public abstract class VcsDirtyScopeManager {
*
* @param file the file for which the status update is requested.
*/
public abstract void fileDirty(VirtualFile file);
public abstract void fileDirty(@NotNull VirtualFile file);
/**
* Requests an asynchronous file status update for the specified file path. Must be called from a read action.
*
* @param file the file path for which the status update is requested.
*/
public abstract void fileDirty(FilePath file);
public abstract void fileDirty(@NotNull FilePath file);
/**
* Requests an asynchronous file status update for all files under the specified directory.
@@ -16,6 +16,7 @@
package com.intellij.openapi.vcs.impl;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Getter;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Throwable2Computable;
import com.intellij.openapi.vcs.FilePath;
@@ -48,12 +49,14 @@ public class ContentRevisionCache {
private final Object myLock;
private final SLRUMap<Key, byte[]> myCache;
private final SLRUMap<CurrentKey, VcsRevisionNumber> myCurrentRevisionsCache;
private final SLRUMap<Pair<FilePath, VcsRevisionNumber>, Object> myCustom;
private long myCounter;
public ContentRevisionCache() {
myLock = new Object();
myCache = new SLRUMap<Key, byte[]>(100, 50);
myCurrentRevisionsCache = new SLRUMap<CurrentKey, VcsRevisionNumber>(200, 50);
myCustom = new SLRUMap<Pair<FilePath, VcsRevisionNumber>, Object>(30,30);
myCounter = 0;
}
@@ -73,6 +76,19 @@ public class ContentRevisionCache {
}
}
public void putCustom(FilePath path, VcsRevisionNumber number, final Object o) {
synchronized (myLock) {
myCustom.put(new Pair<FilePath, VcsRevisionNumber>(path, number), o);
}
}
@Nullable
public Object getCustom(FilePath path, VcsRevisionNumber number) {
synchronized (myLock) {
return myCustom.get(new Pair<FilePath, VcsRevisionNumber>(path, number));
}
}
public void clearAllCurrent() {
synchronized (myLock) {
++ myCounter;
@@ -220,6 +236,10 @@ public class ContentRevisionCache {
Pair<VcsRevisionNumber, byte[]> loaded;
while (true) {
currentRevision = putIntoCurrentCache(cache, path, vcsKey, loader);
final byte[] cachedCurrent = cache.getBytes(path, currentRevision, vcsKey, UniqueType.REPOSITORY_CONTENT);
if (cachedCurrent != null) {
return new Pair<VcsRevisionNumber, byte[]>(currentRevision, cachedCurrent);
}
loaded = loader.get();
if (loaded.getFirst().equals(currentRevision)) break;
}
@@ -32,22 +32,15 @@ import java.util.List;
* @author yole
*/
public abstract class VcsBackgroundTask<T> extends Task.ConditionalModal {
@NotNull private final PerformInBackgroundOption myBackgroundOption;
private final Collection<T> myItems;
private final List<VcsException> myExceptions = new ArrayList<VcsException>();
public VcsBackgroundTask(final Project project, @NotNull final String title, @NotNull final PerformInBackgroundOption backgroundOption,
final Collection<T> itemsToProcess) {
super(project, title, false, backgroundOption);
myBackgroundOption = backgroundOption;
myItems = itemsToProcess;
}
@NotNull
public PerformInBackgroundOption getBackgroundOption() {
return myBackgroundOption;
}
public void run(@NotNull ProgressIndicator indicator) {
for(T item: myItems) {
try {
@@ -81,14 +81,15 @@ public class FragmentedDiffRequestFromChange {
final FileStatus fs = change.getFileStatus();
fragmentedContent.setIsAddition(FileStatus.ADDED.equals(fs));
fragmentedContent.setOneSide(FileStatus.ADDED.equals(fs) || FileStatus.DELETED.equals(fs));
final PreparedFragmentedContent preparedFragmentedContent = new PreparedFragmentedContent(myProject, fragmentedContent,
filePath.getName(), filePath.getFileType());
VirtualFile file = filePath.getVirtualFile();
if (file == null) {
filePath.hardRefresh();
file = filePath.getVirtualFile();
}
preparedFragmentedContent.setVirtualFile(file);
final PreparedFragmentedContent preparedFragmentedContent = new PreparedFragmentedContent(myProject, fragmentedContent,
filePath.getName(), filePath.getFileType(),
change.getBeforeRevision() == null ? null : change.getBeforeRevision().getRevisionNumber(),
change.getAfterRevision() == null ? null : change.getAfterRevision().getRevisionNumber(), filePath, file);
return preparedFragmentedContent;
}
@@ -24,11 +24,18 @@ import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.SyntaxHighlighter;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Getter;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
import com.intellij.openapi.vcs.VcsConfiguration;
import com.intellij.openapi.vcs.history.VcsRevisionNumber;
import com.intellij.openapi.vcs.impl.ContentRevisionCache;
import com.intellij.openapi.vcs.impl.ProjectLevelVcsManagerImpl;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.BeforeAfter;
import com.intellij.util.Consumer;
import java.util.ArrayList;
import java.util.Collections;
@@ -60,14 +67,25 @@ public class PreparedFragmentedContent {
private final FragmentedContent myFragmentedContent;
private final String myFileName;
private final FileType myFileType;
private final VcsRevisionNumber myBeforeNumber;
private final VcsRevisionNumber myAfterNumber;
private VirtualFile myFile;
private FilePath myFilePath;
public PreparedFragmentedContent(final Project project, final FragmentedContent fragmentedContent, final String fileName,
final FileType fileType) {
final FileType fileType,
VcsRevisionNumber beforeNumber,
VcsRevisionNumber afterNumber,
FilePath path,
VirtualFile file) {
myFile = file;
myProject = project;
myFragmentedContent = fragmentedContent;
myFileName = fileName;
myFileType = fileType;
myBeforeNumber = beforeNumber;
myAfterNumber = afterNumber;
myFilePath = path;
oldConvertor = new LineNumberConvertor();
newConvertor = new LineNumberConvertor();
sbOld = new StringBuilder();
@@ -301,18 +319,26 @@ public class PreparedFragmentedContent {
}
private void setTodoHighlighting(final Document oldDocument, final Document document) {
final List<Pair<TextRange,TextAttributes>> beforeTodoRanges = new TodoForRanges(myProject, myFileName, oldDocument.getText(), true,
getBeforeFragments(), myFileType, 1).execute();
final List<Pair<TextRange, TextAttributes>> afterTodoRanges = new TodoForRanges(myProject, myFileName, document.getText(), false,
getAfterFragments(), myFileType, 1).execute();
final ContentRevisionCache cache = ProjectLevelVcsManager.getInstance(myProject).getContentRevisionCache();
final List<Pair<TextRange,TextAttributes>> beforeTodoRanges = myBeforeNumber == null ? Collections.<Pair<TextRange,TextAttributes>>emptyList() :
new TodoForBaseRevision(myProject, getBeforeFragments(), 1, myFileName, oldDocument.getText(), true, myFileType, new Getter<Object>() {
@Override
public Object get() {
return cache.getCustom(myFilePath, myBeforeNumber);
}
}, new Consumer<Object>() {
@Override
public void consume(Object items) {
cache.putCustom(myFilePath, myBeforeNumber, items);
}
}).execute();
final List<Pair<TextRange, TextAttributes>> afterTodoRanges = new TodoForExistingFile(myProject, getAfterFragments(), 1,
myFileName, document.getText(), false, myFileType, myFile).execute();
setBeforeTodoRanges(beforeTodoRanges);
setAfterTodoRanges(afterTodoRanges);
}
public void setVirtualFile(VirtualFile file) {
myFile = file;
}
public VirtualFile getFile() {
return myFile;
}
@@ -252,7 +252,7 @@ public class VcsDirtyScopeManagerImpl extends VcsDirtyScopeManager implements Pr
}
}
public void fileDirty(final VirtualFile file) {
public void fileDirty(@NotNull final VirtualFile file) {
try {
final AbstractVcs vcs = myGuess.getVcsForDirty(file);
if (vcs == null) return;
@@ -269,7 +269,7 @@ public class VcsDirtyScopeManagerImpl extends VcsDirtyScopeManager implements Pr
}
}
public void fileDirty(final FilePath file) {
public void fileDirty(@NotNull final FilePath file) {
try {
final AbstractVcs vcs = myGuess.getVcsForDirty(file);
if (vcs == null) return;
@@ -54,11 +54,11 @@ class VcsDirtyScopeManagerProxy extends VcsDirtyScopeManager {
myFiles.clear();
}
public void fileDirty(final VirtualFile file) {
public void fileDirty(@NotNull final VirtualFile file) {
myVFiles.add(file);
}
public void fileDirty(final FilePath file) {
public void fileDirty(@NotNull final FilePath file) {
myFiles.add(file);
}
@@ -27,6 +27,7 @@ import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.impl.ProjectLevelVcsManagerImpl;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class VcsGuess {
@@ -41,7 +42,7 @@ public class VcsGuess {
}
@Nullable
public AbstractVcs getVcsForDirty(final VirtualFile file) {
public AbstractVcs getVcsForDirty(@NotNull final VirtualFile file) {
if (!file.isInLocalFileSystem()) {
return null;
}
@@ -56,7 +57,7 @@ public class VcsGuess {
}
@Nullable
public AbstractVcs getVcsForDirty(final FilePath filePath) {
public AbstractVcs getVcsForDirty(@NotNull final FilePath filePath) {
if (filePath.isNonLocal()) {
return null;
}
@@ -16,13 +16,15 @@
package com.intellij.openapi.vcs.changes.actions;
import com.intellij.idea.ActionsBundle;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.actionSystem.ActionPlaces;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.IconLoader;
import com.intellij.openapi.vcs.FileStatus;
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
import com.intellij.openapi.vcs.VcsBundle;
import com.intellij.openapi.vcs.VcsDataKeys;
import com.intellij.openapi.vcs.changes.*;
import com.intellij.openapi.vcs.changes.ui.ChangeListChooser;
@@ -135,7 +137,7 @@ public class MoveChangesToAnotherListAction extends AnAction implements DumbAwar
final ChangeListManagerImpl listManager = ChangeListManagerImpl.getInstanceImpl(project);
final List<LocalChangeList> lists = listManager.getChangeLists();
ChangeListChooser chooser = new ChangeListChooser(project, getPreferredLists(lists, changes, true), guessPreferredList(lists, changes),
VcsBundle.message("changes.changelist.chooser.title"), null);
ActionsBundle.message("action.ChangesView.Move.text"), null);
chooser.show();
LocalChangeList resultList = chooser.getSelectedList();
if (resultList != null) {
@@ -25,6 +25,7 @@ import com.intellij.openapi.vcs.changes.ChangeListManager;
import com.intellij.openapi.vcs.changes.LocalChangeList;
import com.intellij.openapi.vcs.changes.committed.CommittedChangeListRenderer;
import com.intellij.openapi.vcs.changes.issueLinks.IssueLinkRenderer;
import com.intellij.ui.CollectionComboBoxModel;
import com.intellij.ui.HtmlListCellRenderer;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.util.Consumer;
@@ -36,12 +37,20 @@ import javax.swing.*;
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.Collection;
import java.util.*;
import java.util.List;
/**
* @author yole
*/
public class ChangeListChooserPanel extends JPanel {
private static final Comparator<ChangeList> CHANGE_LIST_COMPARATOR = new Comparator<ChangeList>() {
@Override
public int compare(ChangeList o1, ChangeList o2) {
return o1.getName().compareToIgnoreCase(o2.getName());
}
};
private JPanel myPanel;
private JRadioButton myRbExisting;
private JRadioButton myRbNew;
@@ -94,12 +103,9 @@ public class ChangeListChooserPanel extends JPanel {
}
public void setChangeLists(Collection<? extends ChangeList> changeLists) {
final DefaultComboBoxModel model = new DefaultComboBoxModel();
for (ChangeList list : changeLists) {
model.addElement(list);
}
myExistingListsCombo.setModel(model);
List<ChangeList> list = new ArrayList<ChangeList>(changeLists);
Collections.sort(list, CHANGE_LIST_COMPARATOR);
myExistingListsCombo.setModel(new CollectionComboBoxModel(list, null));
}
public void setDefaultName(String name) {
@@ -166,7 +166,7 @@ public class ChangesViewContentManager extends AbstractProjectComponent implemen
}
public boolean isToolwindowVisible() {
return myToolWindow.isVisible();
return ! myToolWindow.isDisposed() && myToolWindow.isVisible();
}
public void projectClosed() {
@@ -70,6 +70,18 @@ public abstract class XDebuggerEvaluator {
evaluate(expression, callback);
}
/**
* Start evaluating expression.
*
* called from evaluation dialog
* @param expression expression to evaluate
* @param callback used to notify that the expression has been evaluated or an error occurs
* @param mode code fragment or expression
*/
public void evaluate(@NotNull String expression, XEvaluationCallback callback, @Nullable XSourcePosition expressionPosition, @Nullable EvaluationMode mode) {
evaluate(expression, callback, expressionPosition);
}
/**
* @deprecated override {@link #evaluate(String, XEvaluationCallback, com.intellij.xdebugger.XSourcePosition)} instead
*/
@@ -182,7 +182,7 @@ public class XDebuggerEvaluationDialog extends DialogWrapper {
final XDebuggerEditorBase inputEditor = myInputComponent.getInputEditor();
inputEditor.saveTextInHistory();
String expression = inputEditor.getText();
myEvaluator.evaluate(expression, evaluationCallback, null);
myEvaluator.evaluate(expression, evaluationCallback, null, inputEditor.getMode());
}
public JComponent getPreferredFocusedComponent() {
@@ -52,6 +52,10 @@ public abstract class XDebuggerEditorBase {
mySourcePosition = sourcePosition;
}
public EvaluationMode getMode() {
return myMode;
}
public abstract JComponent getComponent();
protected abstract void doSetText(String text);
@@ -13,4 +13,6 @@ android.jps.errors.package.not.specified=Package is not specified in AndroidMani
android.jps.progress.library.packaging=Packaging Android libraries for module {0}
android.jps.errors.main.content.root.not.found=Cannot find main content root for module {0}
android.jps.progress.proguard=Executing ProGuard for module {0}
android.jps.progress.res.caching=Caching resources for module {0}
android.jps.progress.res.caching=Caching resources for module {0}
android.jps.progress.packaging=Packaging {0}
android.jps.progress.packaging.resources=Packaging resources for module {0}
@@ -0,0 +1,42 @@
package org.jetbrains.jps.android;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.incremental.storage.ValidityState;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
/**
* @author Eugene.Kudelevsky
*/
public class AndroidApkBuilderConfigState implements ValidityState {
private final String myOutputApkPath;
private final String myCustomKeystorePath;
public AndroidApkBuilderConfigState(@NotNull String outputApkPath, @NotNull String customKeystorePath) {
myOutputApkPath = outputApkPath;
myCustomKeystorePath = customKeystorePath;
}
public AndroidApkBuilderConfigState(DataInput in) throws IOException {
myOutputApkPath = in.readUTF();
myCustomKeystorePath = in.readUTF();
}
@Override
public boolean equalsTo(ValidityState otherState) {
if (!(otherState instanceof AndroidApkBuilderConfigState)) {
return false;
}
final AndroidApkBuilderConfigState apkBuilderConfigState = (AndroidApkBuilderConfigState)otherState;
return apkBuilderConfigState.myOutputApkPath.equals(myOutputApkPath) &&
apkBuilderConfigState.myCustomKeystorePath.equals(myCustomKeystorePath);
}
@Override
public void save(DataOutput out) throws IOException {
out.writeUTF(myOutputApkPath);
out.writeUTF(myCustomKeystorePath);
}
}
@@ -0,0 +1,38 @@
package org.jetbrains.jps.android;
import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.EnumeratorStringDescriptor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.incremental.storage.AbstractStateStorage;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.File;
import java.io.IOException;
/**
* @author Eugene.Kudelevsky
*/
public class AndroidApkBuilderConfigStateStorage extends AbstractStateStorage<String, AndroidApkBuilderConfigState> {
public AndroidApkBuilderConfigStateStorage(@NotNull File dataStorageRoot, @NotNull String storageName) throws IOException {
super(getStorageFile(dataStorageRoot, storageName), new EnumeratorStringDescriptor(), new MyDataExternalizer());
}
@NotNull
private static File getStorageFile(@NotNull File dataStorageRoot, @NotNull String storageName) {
return new File(new File(new File(dataStorageRoot, AndroidJpsUtil.ANDROID_STORAGE_DIR), storageName), storageName);
}
private static class MyDataExternalizer implements DataExternalizer<AndroidApkBuilderConfigState> {
@Override
public void save(DataOutput out, AndroidApkBuilderConfigState value) throws IOException {
value.save(out);
}
@Override
public AndroidApkBuilderConfigState read(DataInput in) throws IOException {
return new AndroidApkBuilderConfigState(in);
}
}
}
@@ -24,6 +24,6 @@ public class AndroidBuilderService extends BuilderService {
@Override
public List<? extends ProjectLevelBuilder> createProjectLevelBuilders() {
return Arrays.asList(new AndroidDexBuilder(),
new AndroidResourcePackagingBuilder());
new AndroidPackagingBuilder());
}
}
@@ -1,60 +0,0 @@
package org.jetbrains.jps.android;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.util.Processor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.incremental.storage.ValidityState;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
/**
* @author Eugene.Kudelevsky
*/
class AndroidClassesAndJarsState implements ValidityState {
private Map<String, Long> myFiles;
public AndroidClassesAndJarsState(@NotNull Collection<String> roots) {
myFiles = new HashMap<String, Long>();
for (String rootPath : roots) {
AndroidJpsUtil.processClassFilesAndJarsRecursively(rootPath, new Processor<File>() {
@Override
public boolean process(File file) {
myFiles.put(FileUtil.toSystemIndependentName(file.getPath()), file.lastModified());
return true;
}
});
}
}
public AndroidClassesAndJarsState(@NotNull DataInput in) throws IOException {
myFiles = new HashMap<String, Long>();
int size = in.readInt();
while (size-- > 0) {
final String path = in.readUTF();
final long timestamp = in.readLong();
myFiles.put(path, timestamp);
}
}
public boolean equalsTo(ValidityState otherState) {
return otherState instanceof AndroidClassesAndJarsState
&& myFiles.equals(((AndroidClassesAndJarsState)otherState).myFiles);
}
public void save(DataOutput out) throws IOException {
out.writeInt(myFiles.size());
for (String path : myFiles.keySet()) {
out.writeUTF(path);
out.writeLong(myFiles.get(path));
}
}
}
@@ -1,43 +0,0 @@
package org.jetbrains.jps.android;
import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.EnumeratorStringDescriptor;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.incremental.storage.AbstractStateStorage;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.File;
import java.io.IOException;
/**
* @author Eugene.Kudelevsky
*/
public class AndroidClassesAndJarsStateStorage extends AbstractStateStorage<String, AndroidClassesAndJarsState> {
@NonNls private static final String CLASSES_AND_JARS_STORAGE = "classes_and_jars";
public AndroidClassesAndJarsStateStorage(@NotNull File dataStorageRoot, @NotNull String suffix) throws IOException {
super(getStorageFile(dataStorageRoot, suffix), new EnumeratorStringDescriptor(), new MyDataExternalizer());
}
@NotNull
private static File getStorageFile(@NotNull File dataStorageRoot, @NotNull String suffix) {
return new File(new File(new File(dataStorageRoot, AndroidJpsUtil.ANDROID_STORAGE_DIR), CLASSES_AND_JARS_STORAGE + suffix),
CLASSES_AND_JARS_STORAGE);
}
private static class MyDataExternalizer implements DataExternalizer<AndroidClassesAndJarsState> {
@Override
public void save(DataOutput out, AndroidClassesAndJarsState value) throws IOException {
value.save(out);
}
@Override
public AndroidClassesAndJarsState read(DataInput in) throws IOException {
return new AndroidClassesAndJarsState(in);
}
}
}
@@ -55,11 +55,12 @@ public class AndroidDexBuilder extends ProjectLevelBuilder {
private static void doBuild(CompileContext context) throws IOException, ProjectBuildException {
final File root = context.getDataManager().getDataStorageRoot();
AndroidClassesAndJarsStateStorage dexStateStorage = null;
AndroidClassesAndJarsStateStorage proguardStateStorage = null;
AndroidFileSetStorage dexStateStorage = null;
AndroidFileSetStorage proguardStateStorage = null;
try {
dexStateStorage = new AndroidClassesAndJarsStateStorage(root, "_dex");
proguardStateStorage = new AndroidClassesAndJarsStateStorage(root, "_proguard");
dexStateStorage = new AndroidFileSetStorage(root, "dex");
proguardStateStorage = new AndroidFileSetStorage(root, "proguard");
if (!doDexBuild(context, dexStateStorage, proguardStateStorage)) {
throw new ProjectBuildException();
}
@@ -75,8 +76,8 @@ public class AndroidDexBuilder extends ProjectLevelBuilder {
}
private static boolean doDexBuild(@NotNull CompileContext context,
@NotNull AndroidClassesAndJarsStateStorage dexStateStorage,
@NotNull AndroidClassesAndJarsStateStorage proguardStateStorage) {
@NotNull AndroidFileSetStorage dexStateStorage,
@NotNull AndroidFileSetStorage proguardStateStorage) {
boolean success = true;
for (Module module : context.getProject().getModules().values()) {
@@ -145,10 +146,10 @@ public class AndroidDexBuilder extends ProjectLevelBuilder {
}
}
}
final AndroidClassesAndJarsState newState = new AndroidClassesAndJarsState(fileSet);
final AndroidFileSetState newState = new AndroidFileSetState(fileSet, AndroidJpsUtil.CLASSES_AND_JARS_FILTER, true);
if (context.isMake()) {
final AndroidClassesAndJarsState oldState = dexStateStorage.getState(module.getName());
final AndroidFileSetState oldState = dexStateStorage.getState(module.getName());
if (oldState != null && oldState.equalsTo(newState)) {
continue;
}
@@ -192,11 +193,11 @@ public class AndroidDexBuilder extends ProjectLevelBuilder {
return "Android Dex Builder";
}
public static boolean runDex(@NotNull AndroidSdk sdk,
@NotNull IAndroidTarget target,
@NotNull String outputDir,
@NotNull String[] compileTargets,
@NotNull CompileContext context) throws IOException {
private static boolean runDex(@NotNull AndroidSdk sdk,
@NotNull IAndroidTarget target,
@NotNull String outputDir,
@NotNull String[] compileTargets,
@NotNull CompileContext context) throws IOException {
@SuppressWarnings("deprecation")
final String dxJarPath = FileUtil.toSystemDependentName(target.getPath(IAndroidTarget.DX_JAR));
@@ -254,7 +255,7 @@ public class AndroidDexBuilder extends ProjectLevelBuilder {
@NotNull CompileContext context,
@NotNull String outputJarPath,
@NotNull String proguardCfgPath,
@NotNull AndroidClassesAndJarsStateStorage proguardStateStorage) throws IOException {
@NotNull AndroidFileSetStorage proguardStateStorage) throws IOException {
final Module module = facet.getModule();
final File proguardCfgFile = new File(proguardCfgPath);
@@ -294,8 +295,8 @@ public class AndroidDexBuilder extends ProjectLevelBuilder {
allFiles.addAll(libClassFilesDirs);
allFiles.addAll(externalJars);
final AndroidClassesAndJarsState newState = new AndroidClassesAndJarsState(allFiles);
final AndroidClassesAndJarsState oldState = proguardStateStorage.getState(module.getName());
final AndroidFileSetState newState = new AndroidFileSetState(allFiles, AndroidJpsUtil.CLASSES_AND_JARS_FILTER, true);
final AndroidFileSetState oldState = proguardStateStorage.getState(module.getName());
if (context.getTimestampStorage().getStamp(proguardCfgFile) == proguardCfgFile.lastModified() &&
newState.equalsTo(oldState)) {
return true;
@@ -23,6 +23,10 @@ class AndroidFacet extends Facet {
boolean useCustomManifestForCompilation;
boolean packTestCode;
String apkRelativePath;
String customDebugKeyStorePath;
String nativeLibsFolderRelativePath;
AndroidFacet(Module module, String name) {
this.module = module
this.name = name;
@@ -54,6 +58,11 @@ class AndroidFacet extends Facet {
return manifestFile != null ? manifestFile.getCanonicalFile() : null;
}
File getNativeLibsDir() throws IOException {
def nativeLibsFolder = findFileByRelativeModulePath(nativeLibsFolderRelativePath, false);
return nativeLibsFolder != null ? nativeLibsFolder.getCanonicalFile() : null;
}
private File findFileByRelativeModulePath(String relativePath, boolean lookInContentRoot) {
if (module.basePath != null) {
def absPath = FileUtil.toSystemIndependentName(module.basePath + relativePath)
@@ -4,6 +4,7 @@ import org.jetbrains.jps.MacroExpander
import org.jetbrains.jps.Module
import org.jetbrains.jps.idea.Facet
import org.jetbrains.jps.idea.FacetTypeService
import org.jetbrains.jps.idea.IdeaProjectLoadingUtil
/**
* @author Eugene.Kudelevsky
@@ -49,6 +50,16 @@ class AndroidFacetType extends FacetTypeService {
break
case "ASSETS_FOLDER_RELATIVE_PATH":
facet.assetsFolderRelativePath = value
break
case "APK_PATH":
facet.apkRelativePath = value
break
case "CUSTOM_DEBUG_KEYSTORE_PATH":
facet.customDebugKeyStorePath = IdeaProjectLoadingUtil.pathFromUrl(value)
break
case "LIBS_FOLDER_RELATIVE_PATH":
facet.nativeLibsFolderRelativePath = value
break
}
}
return facet;
@@ -1,5 +1,6 @@
package org.jetbrains.jps.android;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.util.Processor;
import com.intellij.util.containers.HashMap;
@@ -19,21 +20,29 @@ import java.util.Map;
public class AndroidFileSetState implements ValidityState {
private final Map<String, Long> myTimestamps;
public AndroidFileSetState(@NotNull Collection<String> roots) {
public AndroidFileSetState(@NotNull Collection<String> roots, @NotNull final Condition<File> filter, boolean recursively) {
myTimestamps = new HashMap<String, Long>();
for (String resourceDir : roots) {
FileUtil.processFilesRecursively(new File(resourceDir), new Processor<File>() {
@Override
public boolean process(File file) {
myTimestamps.put(FileUtil.toSystemIndependentName(file.getPath()), file.lastModified());
return true;
}
});
for (String rootPath : roots) {
final File root = new File(rootPath);
if (recursively) {
FileUtil.processFilesRecursively(root, new Processor<File>() {
@Override
public boolean process(File file) {
if (filter.value(file)) {
myTimestamps.put(FileUtil.toSystemIndependentName(file.getPath()), file.lastModified());
}
return true;
}
});
}
else if (filter.value(root)) {
myTimestamps.put(FileUtil.toSystemIndependentName(root.getPath()), root.lastModified());
}
}
}
public AndroidFileSetState(DataInput in) throws IOException {
final int resourcesCount = in.readInt();
myTimestamps = new HashMap<String, Long>(resourcesCount);
@@ -3,6 +3,7 @@ package org.jetbrains.jps.android;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.util.ArrayUtil;
@@ -34,9 +35,18 @@ import java.util.regex.Matcher;
*/
class AndroidJpsUtil {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.android.AndroidJpsUtil");
@NonNls public static final String ANDROID_STORAGE_DIR = "android";
@NonNls private static final String RESOURCE_CACHE_STORAGE = "res-cache";
public static final Condition<File> CLASSES_AND_JARS_FILTER = new Condition<File>() {
@Override
public boolean value(File file) {
final String ext = FileUtil.getExtension(file.getName());
return "jar".equals(ext) || "class".equals(ext);
}
};
private AndroidJpsUtil() {
}
@@ -106,6 +116,19 @@ class AndroidJpsUtil {
return result;
}
@NotNull
public static List<String> toPaths(@NotNull Collection<File> files) {
if (files.size() == 0) {
return Collections.emptyList();
}
final List<String> result = new ArrayList<String>(files.size());
for (File file : files) {
result.add(file.getPath());
}
return result;
}
@Nullable
public static File getOutputDirectoryForPackagedFiles(@NotNull ProjectPaths paths, @NotNull Module module) {
// todo: return build directory for mavenized modules to place .dex and .apk files into target dir (not target/classes)
@@ -150,6 +173,7 @@ class AndroidJpsUtil {
}
if (libraries != null) {
// todo: do not include provided libs there
for (ClasspathItem item : module.getClasspath(ClasspathKind.PRODUCTION_COMPILE, exportedLibrariesOnly)) {
if (item instanceof Library && !(item instanceof Sdk)) {
for (String filePath : item.getClasspathRoots(ClasspathKind.PRODUCTION_COMPILE)) {
@@ -180,7 +204,7 @@ class AndroidJpsUtil {
if (depLibrary) {
final File packagedClassesJar = new File(depClassDir, AndroidCommonUtils.CLASSES_JAR_FILE_NAME);
if (packagedClassesJar.isDirectory()) {
if (packagedClassesJar.isFile()) {
outputDirs.add(packagedClassesJar.getPath());
}
}
@@ -402,4 +426,72 @@ class AndroidJpsUtil {
final File androidStorage = new File(context.getDataManager().getDataStorageRoot(), ANDROID_STORAGE_DIR);
return new File(new File(androidStorage, RESOURCE_CACHE_STORAGE), module.getName());
}
private static void fillSourceRoots(@NotNull Module module, @NotNull Set<Module> visited, @NotNull Set<File> result)
throws IOException {
visited.add(module);
final AndroidFacet facet = getFacet(module);
File resDir = null;
File resDirForCompilation = null;
if (facet != null) {
resDir = facet.getResourceDir();
resDirForCompilation = facet.getResourceDirForCompilation();
}
for (String sourceRootPath : module.getSourceRoots()) {
final File sourceRoot = new File(sourceRootPath).getCanonicalFile();
if (!sourceRoot.equals(resDir) && !sourceRoot.equals(resDirForCompilation)) {
result.add(sourceRoot);
}
}
if (facet != null && facet.isPackTestCode()) {
for (String testRootPath : module.getTestRoots()) {
final File testRoot = new File(testRootPath).getCanonicalFile();
if (!testRoot.equals(resDir) && !testRoot.equals(resDirForCompilation)) {
result.add(testRoot);
}
}
}
for (ClasspathItem classpathItem : module.getClasspath(ClasspathKind.PRODUCTION_COMPILE)) {
if (classpathItem instanceof Module) {
final Module depModule = (Module)classpathItem;
if (!visited.contains(depModule)) {
fillSourceRoots(depModule, visited, result);
}
}
}
}
@NotNull
public static File[] getSourceRootsForModuleAndDependencies(@NotNull Module module) throws IOException {
Set<File> result = new HashSet<File>();
fillSourceRoots(module, new HashSet<Module>(), result);
return result.toArray(new File[result.size()]);
}
@Nullable
public static String getApkPath(@NotNull AndroidFacet facet, @NotNull File outputDirForPackagedArtifacts) {
final String apkRelativePath = facet.getApkRelativePath();
final Module module = facet.getModule();
if (apkRelativePath.length() == 0) {
return new File(outputDirForPackagedArtifacts, getApkName(module)).getPath();
}
final String moduleDirPath = module.getBasePath();
return moduleDirPath != null
? FileUtil.toSystemDependentName(moduleDirPath + apkRelativePath)
: null;
}
@NotNull
public static String getApkName(@NotNull Module module) {
return module.getName() + ".apk";
}
}
@@ -45,8 +45,7 @@ public class AndroidLibraryPackagingBuilder extends ModuleLevelBuilder {
private static ModuleLevelBuilder.ExitCode doBuild(CompileContext context, ModuleChunk chunk) throws IOException {
boolean success = true;
final AndroidClassesAndJarsStateStorage storage =
new AndroidClassesAndJarsStateStorage(context.getDataManager().getDataStorageRoot(), "_libs");
final AndroidFileSetStorage storage = new AndroidFileSetStorage(context.getDataManager().getDataStorageRoot(), "libs_packaging");
try {
for (Module module : chunk.getModules()) {
@@ -73,8 +72,8 @@ public class AndroidLibraryPackagingBuilder extends ModuleLevelBuilder {
final Set<String> subdirs = new HashSet<String>();
AndroidJpsUtil.addSubdirectories(classesDir, subdirs);
final AndroidClassesAndJarsState newState = new AndroidClassesAndJarsState(subdirs);
final AndroidClassesAndJarsState oldState = storage.getState(module.getName());
final AndroidFileSetState newState = new AndroidFileSetState(subdirs, AndroidJpsUtil.CLASSES_AND_JARS_FILTER, true);
final AndroidFileSetState oldState = storage.getState(module.getName());
if (oldState != null && oldState.equalsTo(newState)) {
continue;
@@ -0,0 +1,496 @@
package org.jetbrains.jps.android;
import com.android.sdklib.IAndroidTarget;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.HashMap;
import org.jetbrains.android.compiler.tools.AndroidApkBuilder;
import org.jetbrains.android.compiler.tools.AndroidApt;
import org.jetbrains.android.util.AndroidCommonUtils;
import org.jetbrains.android.util.AndroidCompilerMessageKind;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.CompilerExcludes;
import org.jetbrains.jps.Module;
import org.jetbrains.jps.Project;
import org.jetbrains.jps.ProjectPaths;
import org.jetbrains.jps.incremental.CompileContext;
import org.jetbrains.jps.incremental.ProjectBuildException;
import org.jetbrains.jps.incremental.ProjectLevelBuilder;
import org.jetbrains.jps.incremental.messages.BuildMessage;
import org.jetbrains.jps.incremental.messages.CompilerMessage;
import org.jetbrains.jps.incremental.messages.ProgressMessage;
import java.io.File;
import java.io.IOException;
import java.util.*;
/**
* @author Eugene.Kudelevsky
*/
public class AndroidPackagingBuilder extends ProjectLevelBuilder {
@NonNls private static final String BUILDER_NAME = "android-packager";
@NonNls private static final String RELEASE_SUFFIX = ".release";
@NonNls private static final String UNSIGNED_SUFFIX = ".unsigned";
@Override
public String getName() {
return BUILDER_NAME;
}
@Override
public String getDescription() {
return "Android Packaging Builder";
}
@Override
public void build(CompileContext context) throws ProjectBuildException {
if (!AndroidJpsUtil.containsAndroidFacet(context.getProject()) || AndroidJpsUtil.isLightBuild(context)) {
return;
}
final Collection<Module> modules = context.getProject().getModules().values();
final Map<Module, AndroidFileSetState> resourcesStates = new HashMap<Module, AndroidFileSetState>();
final Map<Module, AndroidFileSetState> assetsStates = new HashMap<Module, AndroidFileSetState>();
try {
fillStates(modules, resourcesStates, assetsStates);
if (!doCaching(context, modules, resourcesStates)) {
throw new ProjectBuildException();
}
if (!doResourcePackaging(context, modules, resourcesStates, assetsStates)) {
throw new ProjectBuildException();
}
if (!doPackaging(context, modules)) {
throw new ProjectBuildException();
}
}
catch (ProjectBuildException e) {
throw e;
}
catch (Exception e) {
AndroidJpsUtil.handleException(context, e, BUILDER_NAME);
}
}
@SuppressWarnings("unchecked")
private static void fillStates(@NotNull Collection<Module> modules,
@NotNull Map<Module, AndroidFileSetState> resourcesStates,
@NotNull Map<Module, AndroidFileSetState> assetsStates) throws IOException {
for (Module module : modules) {
final AndroidFacet facet = AndroidJpsUtil.getFacet(module);
if (facet != null) {
final File resourceDir = facet.getResourceDir();
final List<String> resourceDirs = resourceDir != null
? Arrays.asList(resourceDir.getPath())
: Collections.<String>emptyList();
resourcesStates.put(module, new AndroidFileSetState(resourceDirs, Condition.TRUE, true));
final File assetsDir = facet.getAssetsDir();
final List<String> assetDirs = assetsDir != null
? Arrays.asList(assetsDir.getPath())
: Collections.<String>emptyList();
assetsStates.put(module, new AndroidFileSetState(assetDirs, Condition.TRUE, true));
}
}
}
private static boolean doCaching(@NotNull CompileContext context,
@NotNull Collection<Module> modules,
@NotNull Map<Module, AndroidFileSetState> module2state) throws IOException {
boolean success = true;
final File dataStorageRoot = context.getDataManager().getDataStorageRoot();
final AndroidFileSetStorage storage = new AndroidFileSetStorage(dataStorageRoot, "resource_caching");
try {
for (Module module : modules) {
final AndroidFileSetState state = module2state.get(module);
try {
if (!runPngCaching(context, module, storage, state)) {
success = false;
}
}
catch (IOException e) {
AndroidJpsUtil.reportExceptionError(context, null, e, BUILDER_NAME);
}
}
}
finally {
storage.close();
}
return success;
}
private static boolean runPngCaching(@NotNull CompileContext context,
@NotNull Module module,
@NotNull AndroidFileSetStorage storage,
@Nullable AndroidFileSetState state)
throws IOException {
final AndroidFileSetState savedState = storage.getState(module.getName());
if (context.isMake() && savedState != null && savedState.equalsTo(state)) {
return true;
}
final AndroidFacet facet = AndroidJpsUtil.getFacet(module);
if (facet == null) {
return true;
}
context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.INFO,
AndroidJpsBundle.message("android.jps.progress.res.caching", module.getName())));
final File resourceDir = AndroidJpsUtil.getResourceDirForCompilationPath(facet);
if (resourceDir == null) {
return true;
}
final Pair<AndroidSdk, IAndroidTarget> pair = AndroidJpsUtil.getAndroidPlatform(module, context, BUILDER_NAME);
if (pair == null) {
return false;
}
final File resCacheDir = AndroidJpsUtil.getResourcesCacheDir(context, module);
if (!resCacheDir.exists()) {
if (!resCacheDir.mkdirs()) {
context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR,
"Cannot create directory " + resCacheDir.getPath()));
return false;
}
}
final IAndroidTarget target = pair.second;
final Map<AndroidCompilerMessageKind, List<String>> messages =
AndroidApt.crunch(target, Collections.singletonList(resourceDir.getPath()), resCacheDir.getPath());
AndroidJpsUtil.addMessages(context, messages, BUILDER_NAME);
final boolean success = messages.get(AndroidCompilerMessageKind.ERROR).isEmpty();
storage.update(module.getName(), success ? state : null);
return success;
}
private static boolean doResourcePackaging(@NotNull CompileContext context,
@NotNull Collection<Module> modules,
@NotNull Map<Module, AndroidFileSetState> resourcesStates,
@NotNull Map<Module, AndroidFileSetState> assetsStates) throws IOException {
boolean success = true;
final File dataStorageRoot = context.getDataManager().getDataStorageRoot();
final boolean releaseBuild = AndroidJpsUtil.isReleaseBuild(context);
AndroidFileSetStorage resourcesStorage = null;
AndroidFileSetStorage assetsStorage = null;
try {
final String resourcesStorageName = releaseBuild ? "resources_packaging_release" : "resources_packaging_dev";
resourcesStorage = new AndroidFileSetStorage(dataStorageRoot, resourcesStorageName);
final String assetsStorageName = releaseBuild ? "assets_packaging_release" : "assets_packaging_dev";
assetsStorage = new AndroidFileSetStorage(dataStorageRoot, assetsStorageName);
for (Module module : modules) {
final AndroidFacet facet = AndroidJpsUtil.getFacet(module);
if (facet == null) {
continue;
}
boolean updateState = true;
if (!facet.isLibrary() &&
!(context.isMake() &&
checkUpToDate(module, resourcesStates, resourcesStorage) &&
checkUpToDate(module, assetsStates, assetsStorage))) {
updateState = packageResources(facet, context);
if (!updateState) {
success = false;
}
}
resourcesStorage.update(module.getName(), updateState ? resourcesStates.get(module) : null);
assetsStorage.update(module.getName(), updateState ? assetsStates.get(module) : null);
}
}
finally {
if (resourcesStorage != null) {
resourcesStorage.close();
}
if (assetsStorage != null) {
assetsStorage.close();
}
}
return success;
}
private static boolean doPackaging(@NotNull CompileContext context, @NotNull Collection<Module> modules) throws IOException {
final boolean release = AndroidJpsUtil.isReleaseBuild(context);
final File dataStorageRoot = context.getDataManager().getDataStorageRoot();
boolean success = true;
AndroidFileSetStorage apkFileSetStorage = null;
AndroidApkBuilderConfigStateStorage apkBuilderConfigStateStorage = null;
try {
final String apkFileSetStorageName = "apk_builder_file_set" + (release ? "_release" : "_dev");
apkFileSetStorage = new AndroidFileSetStorage(dataStorageRoot, apkFileSetStorageName);
final String apkBuilderStateStorageName = "apk_builder_config" + (release ? "_release" : "_dev");
apkBuilderConfigStateStorage = new AndroidApkBuilderConfigStateStorage(dataStorageRoot, apkBuilderStateStorageName);
for (Module module : modules) {
try {
if (!doPackagingForModule(context, module, apkFileSetStorage, apkBuilderConfigStateStorage, release)) {
success = false;
}
}
catch (IOException e) {
AndroidJpsUtil.reportExceptionError(context, null, e, BUILDER_NAME);
success = false;
}
}
}
finally {
if (apkFileSetStorage != null) {
apkFileSetStorage.close();
}
if (apkBuilderConfigStateStorage != null) {
apkBuilderConfigStateStorage.close();
}
}
return success;
}
private static boolean doPackagingForModule(@NotNull CompileContext context,
@NotNull Module module,
@NotNull AndroidFileSetStorage apkFileSetStorage,
@NotNull AndroidApkBuilderConfigStateStorage apkBuilderConfigStateStorage,
boolean release) throws IOException {
final AndroidFacet facet = AndroidJpsUtil.getFacet(module);
if (facet == null || facet.isLibrary()) {
return true;
}
final String[] sourceRoots = AndroidJpsUtil.toPaths(AndroidJpsUtil.getSourceRootsForModuleAndDependencies(module));
final ProjectPaths paths = context.getProjectPaths();
final File outputDir = AndroidJpsUtil.getOutputDirectoryForPackagedFiles(paths, module);
if (outputDir == null) {
context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, AndroidJpsBundle
.message("android.jps.errors.output.dir.not.specified", module.getName())));
return false;
}
final Pair<AndroidSdk, IAndroidTarget> pair = AndroidJpsUtil.getAndroidPlatform(module, context, BUILDER_NAME);
if (pair == null) {
return false;
}
final Set<String> externalJarsSet = AndroidJpsUtil.getExternalLibraries(paths, module);
final File resPackage = getPackagedResourcesFile(module, outputDir);
final File classesDexFile = new File(outputDir.getPath(), AndroidCommonUtils.CLASSES_FILE_NAME);
final String sdkPath = pair.getFirst().getSdkPath();
final String outputPath = AndroidJpsUtil.getApkPath(facet, outputDir);
if (outputPath == null) {
context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR,
"Cannot compute output path for file " + AndroidJpsUtil.getApkName(module)));
return false;
}
final String customKeyStorePath = FileUtil.toSystemDependentName(facet.getCustomDebugKeyStorePath());
final String[] nativeLibDirs = collectNativeLibsFolders(facet);
final String resPackagePath = release ? resPackage.getPath() + RELEASE_SUFFIX : resPackage.getPath();
final String outputApkPath = release ? outputPath + UNSIGNED_SUFFIX : outputPath;
final String classesDexFilePath = classesDexFile.getPath();
final String[] externalJars = ArrayUtil.toStringArray(externalJarsSet);
final AndroidFileSetState currentFileSetState =
buildCurrentApkBuilderState(context.getProject(), resPackagePath, classesDexFilePath, nativeLibDirs,
sourceRoots, externalJars, release);
final AndroidApkBuilderConfigState currentApkBuilderConfigState =
new AndroidApkBuilderConfigState(outputApkPath, customKeyStorePath);
final AndroidFileSetState savedApkFileSetState = apkFileSetStorage.getState(module.getName());
final AndroidApkBuilderConfigState savedApkBuilderConfigState = apkBuilderConfigStateStorage.getState(module.getName());
if (context.isMake() &&
currentFileSetState.equalsTo(savedApkFileSetState) &&
currentApkBuilderConfigState.equalsTo(savedApkBuilderConfigState)) {
return true;
}
context
.processMessage(new ProgressMessage(AndroidJpsBundle.message("android.jps.progress.packaging", AndroidJpsUtil.getApkName(module))));
final Map<AndroidCompilerMessageKind, List<String>> messages = AndroidApkBuilder
.execute(resPackagePath, classesDexFilePath, sourceRoots, externalJars, nativeLibDirs, outputApkPath,
release, sdkPath, customKeyStorePath, new MyExcludedSourcesFilter(context.getProject()));
AndroidJpsUtil.addMessages(context, messages, BUILDER_NAME);
final boolean success = messages.get(AndroidCompilerMessageKind.ERROR).isEmpty();
apkFileSetStorage.update(module.getName(), success ? currentFileSetState : null);
apkBuilderConfigStateStorage.update(module.getName(), success ? currentApkBuilderConfigState : null);
return success;
}
@SuppressWarnings("unchecked")
private static AndroidFileSetState buildCurrentApkBuilderState(@NotNull Project project,
@NotNull String resPackagePath,
@NotNull String classesDexFilePath,
@NotNull String[] nativeLibDirs,
@NotNull String[] sourceRoots,
@NotNull String[] externalJars,
boolean release) {
final List<String> roots = new ArrayList<String>();
roots.add(resPackagePath);
roots.add(classesDexFilePath);
roots.addAll(Arrays.asList(externalJars));
for (String sourceRootPath : sourceRoots) {
final List<File> files = new ArrayList<File>();
AndroidApkBuilder.collectStandardSourceFolderResources(new File(sourceRootPath), files, new MyExcludedSourcesFilter(project));
roots.addAll(AndroidJpsUtil.toPaths(files));
}
for (String nativeLibDir : nativeLibDirs) {
final List<File> files = new ArrayList<File>();
AndroidApkBuilder.collectNativeLibraries(new File(nativeLibDir), files, !release);
roots.addAll(AndroidJpsUtil.toPaths(files));
}
return new AndroidFileSetState(roots, Condition.TRUE, false);
}
@NotNull
private static String[] collectNativeLibsFolders(@NotNull AndroidFacet facet) throws IOException {
final List<String> result = new ArrayList<String>();
final File libsDir = facet.getNativeLibsDir();
if (libsDir != null) {
result.add(libsDir.getPath());
}
for (AndroidFacet depFacet : AndroidJpsUtil.getAllDependentAndroidLibraries(facet.getModule())) {
final File depLibsDir = depFacet.getNativeLibsDir();
if (depLibsDir != null) {
result.add(depLibsDir.getPath());
}
}
return ArrayUtil.toStringArray(result);
}
private static boolean checkUpToDate(@NotNull Module module,
@NotNull Map<Module, AndroidFileSetState> module2state,
@NotNull AndroidFileSetStorage storage) throws IOException {
final AndroidFileSetState moduleState = module2state.get(module);
final AndroidFileSetState savedState = storage.getState(module.getName());
if (savedState == null || !savedState.equalsTo(moduleState)) {
return false;
}
for (AndroidFacet libFacet : AndroidJpsUtil.getAllDependentAndroidLibraries(module)) {
final Module libModule = libFacet.getModule();
final AndroidFileSetState currentLibState = module2state.get(libModule);
final AndroidFileSetState savedLibState = storage.getState(libModule.getName());
if (savedLibState == null || !savedLibState.equalsTo(currentLibState)) {
return false;
}
}
return true;
}
private static boolean packageResources(@NotNull AndroidFacet facet, @NotNull CompileContext context) {
final Module module = facet.getModule();
try {
context.processMessage(new ProgressMessage(AndroidJpsBundle.message("android.jps.progress.packaging.resources", module.getName())));
final File manifestFile = AndroidJpsUtil.getManifestFileForCompilationPath(facet);
if (manifestFile == null) {
context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR,
AndroidJpsBundle.message("android.jps.errors.manifest.not.found", module.getName())));
return false;
}
final File assetsDir = facet.getAssetsDir();
final File outputDir = AndroidJpsUtil.getOutputDirectoryForPackagedFiles(context.getProjectPaths(), module);
if (outputDir == null) {
context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, AndroidJpsBundle
.message("android.jps.errors.output.dir.not.specified", module.getName())));
return false;
}
final Pair<AndroidSdk, IAndroidTarget> pair = AndroidJpsUtil.getAndroidPlatform(module, context, BUILDER_NAME);
if (pair == null) {
return false;
}
final IAndroidTarget target = pair.getSecond();
final String outputFilePath = getPackagedResourcesFile(module, outputDir).getPath();
final String assetsDirPath = assetsDir != null ? assetsDir.getPath() : null;
final String[] resourceDirPaths = AndroidJpsUtil.collectResourceDirsForCompilation(facet, true, context);
return doPackageResources(context, manifestFile, target, resourceDirPaths, assetsDirPath, outputFilePath,
AndroidJpsUtil.isReleaseBuild(context));
}
catch (IOException e) {
AndroidJpsUtil.reportExceptionError(context, null, e, BUILDER_NAME);
return false;
}
}
private static boolean doPackageResources(@NotNull final CompileContext context,
@NotNull File manifestFile,
@NotNull IAndroidTarget target,
@NotNull String[] resourceDirPaths,
@Nullable String assetsDirPath,
@NotNull String outputFilePath,
boolean releasePackage) {
try {
final String outputPath = releasePackage
? outputFilePath + RELEASE_SUFFIX
: outputFilePath;
final Map<AndroidCompilerMessageKind, List<String>> messages = AndroidApt
.packageResources(target, -1, manifestFile.getPath(), resourceDirPaths, assetsDirPath, outputPath, null, !releasePackage, 0);
AndroidJpsUtil.addMessages(context, messages, BUILDER_NAME);
return messages.get(AndroidCompilerMessageKind.ERROR).size() == 0;
}
catch (final IOException e) {
AndroidJpsUtil.reportExceptionError(context, null, e, BUILDER_NAME);
return false;
}
}
@NotNull
private static File getPackagedResourcesFile(@NotNull Module module, @NotNull File outputDir) {
return new File(outputDir.getPath(), module.getName() + ".apk.res");
}
private static class MyExcludedSourcesFilter implements Condition<File> {
private final CompilerExcludes myExcludes;
public MyExcludedSourcesFilter(@NotNull Project project) {
myExcludes = project.getCompilerConfiguration().getExcludes();
}
@Override
public boolean value(File file) {
return !myExcludes.isExcluded(file);
}
}
}
@@ -1,322 +0,0 @@
package org.jetbrains.jps.android;
import com.android.sdklib.IAndroidTarget;
import com.intellij.openapi.util.Pair;
import com.intellij.util.containers.HashMap;
import org.jetbrains.android.compiler.tools.AndroidApt;
import org.jetbrains.android.util.AndroidCompilerMessageKind;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.Module;
import org.jetbrains.jps.incremental.CompileContext;
import org.jetbrains.jps.incremental.ProjectBuildException;
import org.jetbrains.jps.incremental.ProjectLevelBuilder;
import org.jetbrains.jps.incremental.messages.BuildMessage;
import org.jetbrains.jps.incremental.messages.CompilerMessage;
import org.jetbrains.jps.incremental.messages.ProgressMessage;
import java.io.File;
import java.io.IOException;
import java.util.*;
/**
* @author Eugene.Kudelevsky
*/
public class AndroidResourcePackagingBuilder extends ProjectLevelBuilder {
@NonNls private static final String BUILDER_NAME = "android-packager";
@Override
public String getName() {
return BUILDER_NAME;
}
@Override
public String getDescription() {
return "Android Packaging Builder";
}
@Override
public void build(CompileContext context) throws ProjectBuildException {
if (!AndroidJpsUtil.containsAndroidFacet(context.getProject()) || AndroidJpsUtil.isLightBuild(context)) {
return;
}
final Collection<Module> modules = context.getProject().getModules().values();
final Map<Module, AndroidFileSetState> resourcesStates = new HashMap<Module, AndroidFileSetState>();
final Map<Module, AndroidFileSetState> assetsStates = new HashMap<Module, AndroidFileSetState>();
try {
fillStates(modules, resourcesStates, assetsStates);
if (!doCaching(context, modules, resourcesStates)) {
throw new ProjectBuildException();
}
if (!doPackaging(context, modules, resourcesStates, assetsStates)) {
throw new ProjectBuildException();
}
}
catch (ProjectBuildException e) {
throw e;
}
catch (Exception e) {
AndroidJpsUtil.handleException(context, e, BUILDER_NAME);
}
}
private static void fillStates(@NotNull Collection<Module> modules,
@NotNull Map<Module, AndroidFileSetState> resourcesStates,
@NotNull Map<Module, AndroidFileSetState> assetsStates) throws IOException {
for (Module module : modules) {
final AndroidFacet facet = AndroidJpsUtil.getFacet(module);
if (facet != null) {
final File resourceDir = facet.getResourceDir();
final List<String> resourceDirs = resourceDir != null
? Arrays.asList(resourceDir.getPath())
: Collections.<String>emptyList();
resourcesStates.put(module, new AndroidFileSetState(resourceDirs));
final File assetsDir = facet.getAssetsDir();
final List<String> assetDirs = assetsDir != null
? Arrays.asList(assetsDir.getPath())
: Collections.<String>emptyList();
assetsStates.put(module, new AndroidFileSetState(assetDirs));
}
}
}
private static boolean doCaching(@NotNull CompileContext context,
@NotNull Collection<Module> modules,
@NotNull Map<Module, AndroidFileSetState> module2state) throws IOException {
boolean success = true;
final File dataStorageRoot = context.getDataManager().getDataStorageRoot();
final AndroidFileSetStorage storage = new AndroidFileSetStorage(dataStorageRoot, "resource_caching");
try {
for (Module module : modules) {
final AndroidFileSetState state = module2state.get(module);
try {
if (!runPngCaching(context, module, storage, state)) {
success = false;
}
}
catch (IOException e) {
AndroidJpsUtil.reportExceptionError(context, null, e, BUILDER_NAME);
}
}
}
finally {
storage.close();
}
return success;
}
private static boolean runPngCaching(@NotNull CompileContext context,
@NotNull Module module,
@NotNull AndroidFileSetStorage storage,
@Nullable AndroidFileSetState state)
throws IOException {
final AndroidFileSetState savedState = storage.getState(module.getName());
if (savedState != null && savedState.equalsTo(state)) {
return true;
}
context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.INFO,
AndroidJpsBundle.message("android.jps.progress.res.caching", module.getName())));
final AndroidFacet facet = AndroidJpsUtil.getFacet(module);
if (facet == null) {
return true;
}
final File resourceDir = AndroidJpsUtil.getResourceDirForCompilationPath(facet);
if (resourceDir == null) {
return true;
}
final Pair<AndroidSdk, IAndroidTarget> pair = AndroidJpsUtil.getAndroidPlatform(module, context, BUILDER_NAME);
if (pair == null) {
return false;
}
final File resCacheDir = AndroidJpsUtil.getResourcesCacheDir(context, module);
if (!resCacheDir.exists()) {
if (!resCacheDir.mkdirs()) {
context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR,
"Cannot create directory " + resCacheDir.getPath()));
return false;
}
}
final IAndroidTarget target = pair.second;
final Map<AndroidCompilerMessageKind, List<String>> messages =
AndroidApt.crunch(target, Collections.singletonList(resourceDir.getPath()), resCacheDir.getPath());
AndroidJpsUtil.addMessages(context, messages, BUILDER_NAME);
final boolean success = messages.get(AndroidCompilerMessageKind.ERROR).isEmpty();
storage.update(module.getName(), success ? state : null);
return success;
}
private static boolean doPackaging(@NotNull CompileContext context,
@NotNull Collection<Module> modules,
@NotNull Map<Module, AndroidFileSetState> resourcesStates,
@NotNull Map<Module, AndroidFileSetState> assetsStates) throws IOException {
boolean success = true;
final File dataStorageRoot = context.getDataManager().getDataStorageRoot();
AndroidFileSetStorage devResourcesStorage = null;
AndroidFileSetStorage releaseResourcesStorage = null;
AndroidFileSetStorage devAssetsStorage = null;
AndroidFileSetStorage releaseAssetsStorage = null;
try {
devResourcesStorage = new AndroidFileSetStorage(dataStorageRoot, "resources_packaging_dev");
releaseResourcesStorage = new AndroidFileSetStorage(dataStorageRoot, "resources_packaging_release");
devAssetsStorage = new AndroidFileSetStorage(dataStorageRoot, "assets_packaging_dev");
releaseAssetsStorage = new AndroidFileSetStorage(dataStorageRoot, "assets_packaging_release");
for (Module module : modules) {
final AndroidFacet facet = AndroidJpsUtil.getFacet(module);
if (facet == null || facet.isLibrary()) {
continue;
}
if (!packageResources(facet, context, devResourcesStorage, devAssetsStorage, releaseResourcesStorage, releaseAssetsStorage,
resourcesStates, assetsStates)) {
success = false;
}
}
}
finally {
if (devResourcesStorage != null) {
devResourcesStorage.close();
}
if (releaseResourcesStorage != null) {
releaseResourcesStorage.close();
}
if (devAssetsStorage != null) {
devAssetsStorage.close();
}
if (releaseAssetsStorage != null) {
releaseAssetsStorage.close();
}
}
return success;
}
private static boolean checkUpToDate(@NotNull Module module,
@NotNull Map<Module, AndroidFileSetState> module2state,
@NotNull AndroidFileSetStorage storage) throws IOException {
final AndroidFileSetState moduleState = module2state.get(module);
final AndroidFileSetState savedState = storage.getState(module.getName());
if (savedState == null || !savedState.equalsTo(moduleState)) {
return false;
}
for (AndroidFacet libFacet : AndroidJpsUtil.getAllDependentAndroidLibraries(module)) {
final Module libModule = libFacet.getModule();
final AndroidFileSetState currentLibState = module2state.get(libModule);
final AndroidFileSetState savedLibState = storage.getState(libModule.getName());
if (savedLibState == null || !savedLibState.equalsTo(currentLibState)) {
return false;
}
}
return true;
}
private static boolean packageResources(@NotNull AndroidFacet facet,
@NotNull CompileContext context,
@NotNull AndroidFileSetStorage devResourcesStorage,
@NotNull AndroidFileSetStorage devAssetsStorage,
@NotNull AndroidFileSetStorage releaseResourcesStorage,
@NotNull AndroidFileSetStorage releaseAssetsStorage,
@NotNull Map<Module, AndroidFileSetState> resourcesStates,
@NotNull Map<Module, AndroidFileSetState> assetsStates) {
final Module module = facet.getModule();
final boolean releaseBuild = AndroidJpsUtil.isReleaseBuild(context);
final AndroidFileSetStorage resourcesStorage = releaseBuild ? releaseResourcesStorage : devResourcesStorage;
final AndroidFileSetStorage assetsStorage = releaseBuild ? releaseAssetsStorage : devAssetsStorage;
try {
if (checkUpToDate(module, resourcesStates, resourcesStorage) &&
checkUpToDate(module, assetsStates, assetsStorage)) {
return true;
}
context.processMessage(new ProgressMessage("Packaging resources for module " + module.getName()));
final File manifestFile = AndroidJpsUtil.getManifestFileForCompilationPath(facet);
if (manifestFile == null) {
context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR,
AndroidJpsBundle.message("android.jps.errors.manifest.not.found", module.getName())));
return false;
}
final File assetsDir = facet.getAssetsDir();
final File outputDir = AndroidJpsUtil.getOutputDirectoryForPackagedFiles(context.getProjectPaths(), module);
if (outputDir == null) {
context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, AndroidJpsBundle
.message("android.jps.errors.output.dir.not.specified", module.getName())));
return false;
}
final Pair<AndroidSdk, IAndroidTarget> pair = AndroidJpsUtil.getAndroidPlatform(module, context, BUILDER_NAME);
if (pair == null) {
return false;
}
final IAndroidTarget target = pair.getSecond();
final String outputFilePath = getOutputFile(module, outputDir).getPath();
final String assetsDirPath = assetsDir != null ? assetsDir.getPath() : null;
final String[] resourceDirPaths = AndroidJpsUtil.collectResourceDirsForCompilation(facet, true, context);
if (!doPackageResources(context, manifestFile, target, resourceDirPaths, assetsDirPath, outputFilePath, releaseBuild)) {
resourcesStorage.update(module.getName(), null);
assetsStorage.update(module.getName(), null);
return false;
}
resourcesStorage.update(module.getName(), resourcesStates.get(module));
assetsStorage.update(module.getName(), assetsStates.get(module));
return true;
}
catch (IOException e) {
AndroidJpsUtil.reportExceptionError(context, null, e, BUILDER_NAME);
return false;
}
}
private static boolean doPackageResources(@NotNull final CompileContext context,
@NotNull File manifestFile,
@NotNull IAndroidTarget target,
@NotNull String[] resourceDirPaths,
@Nullable String assetsDirPath,
@NotNull String outputFilePath,
boolean releasePackage) {
try {
final String outputPath = releasePackage
? outputFilePath + ".release"
: outputFilePath;
final Map<AndroidCompilerMessageKind, List<String>> messages = AndroidApt
.packageResources(target, -1, manifestFile.getPath(), resourceDirPaths, assetsDirPath, outputPath, null, !releasePackage, 0);
AndroidJpsUtil.addMessages(context, messages, BUILDER_NAME);
return messages.get(AndroidCompilerMessageKind.ERROR).size() == 0;
}
catch (final IOException e) {
AndroidJpsUtil.reportExceptionError(context, null, e, BUILDER_NAME);
return false;
}
}
@NotNull
static File getOutputFile(@NotNull Module module, @NotNull File outputDir) {
return new File(outputDir.getPath(), module.getName() + ".apk.res");
}
}
@@ -18,8 +18,6 @@ import org.jetbrains.android.util.ValueResourcesFileParser;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.ClasspathItem;
import org.jetbrains.jps.ClasspathKind;
import org.jetbrains.jps.Module;
import org.jetbrains.jps.ModuleChunk;
import org.jetbrains.jps.incremental.*;
@@ -153,7 +151,7 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder {
final IAndroidTarget target = moduleData.getAndroidTarget();
try {
final File[] sourceRoots = getSourceRootsForModuleAndDependencies(module);
final File[] sourceRoots = AndroidJpsUtil.getSourceRootsForModuleAndDependencies(module);
final String[] sourceRootPaths = AndroidJpsUtil.toPaths(sourceRoots);
final String packageName = computePackageForFile(context, file);
@@ -569,44 +567,6 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder {
return FileUtil.toSystemIndependentName(relPath).replace('/', '.');
}
private static void fillSourceRoots(@NotNull Module module, @NotNull Set<Module> visited, @NotNull Set<File> result)
throws IOException {
visited.add(module);
final AndroidFacet facet = AndroidJpsUtil.getFacet(module);
File resDir = null;
File resDirForCompilation = null;
if (facet != null) {
resDir = facet.getResourceDir();
resDirForCompilation = facet.getResourceDirForCompilation();
}
for (String sourceRootPath : module.getSourceRoots()) {
final File sourceRoot = new File(sourceRootPath).getCanonicalFile();
if (!sourceRoot.equals(resDir) && !sourceRoot.equals(resDirForCompilation)) {
result.add(sourceRoot);
}
}
for (ClasspathItem classpathItem : module.getClasspath(ClasspathKind.PRODUCTION_COMPILE)) {
if (classpathItem instanceof Module) {
final Module depModule = (Module)classpathItem;
if (!visited.contains(depModule)) {
fillSourceRoots(depModule, visited, result);
}
}
}
}
@NotNull
public static File[] getSourceRootsForModuleAndDependencies(@NotNull Module module) throws IOException {
Set<File> result = new HashSet<File>();
fillSourceRoots(module, new HashSet<Module>(), result);
return result.toArray(new File[result.size()]);
}
@Override
public String getDescription() {
return "Android Source Generating Builder";
@@ -139,7 +139,6 @@ avd.not.valid.error=AVD {0} is not valid
avd.not.compatible.error=Build target of AVD {0} is not compatible with your build target
avd.cannot.be.loaded.error=Cannot load virtual devices
navigate.to.android.resource.action=Navigate to Android resource definition
android.debug.certificate.expired.error=Debug certificate expired on {0}. Cannot regenerate it, please delete file \"{1}\" manually.
android.identifier.expected={0} is not an identifier
android.directory.cannot.be.found.error="{0}" directory cannot be found
create.run.configuration.question=Do you want to create run configuration for module {0}?
+4
View File
@@ -14,11 +14,15 @@
<CLASSES>
<root url="jar://$MODULE_DIR$/../lib/sdklib.jar!/" />
<root url="jar://$MODULE_DIR$/../lib/common.jar!/" />
<root url="jar://$MODULE_DIR$/../lib/jarutils.jar!/" />
<root url="jar://$MODULE_DIR$/../lib/androidprefs.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$MODULE_DIR$/../lib/src/sdklib_src.zip!/src" />
<root url="jar://$MODULE_DIR$/../lib/src/common_src.zip!/src" />
<root url="jar://$MODULE_DIR$/../lib/src/jarutils.zip!/src" />
<root url="jar://$MODULE_DIR$/../lib/src/androidprefs_src.zip!/src" />
</SOURCES>
</library>
</orderEntry>
@@ -20,25 +20,15 @@ import com.android.jarutils.JavaResourceFilter;
import com.android.jarutils.SignedJarBuilder;
import com.android.prefs.AndroidLocation;
import com.android.sdklib.SdkConstants;
import com.intellij.ide.highlighter.ArchiveFileType;
import com.intellij.openapi.compiler.CompilerManager;
import com.intellij.openapi.compiler.CompilerMessageCategory;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.containers.HashMap;
import com.intellij.util.containers.HashSet;
import com.intellij.util.text.DateFormatUtil;
import org.jetbrains.android.compiler.AndroidCompileUtil;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.sdk.AndroidPlatform;
import org.jetbrains.android.sdk.AndroidSdkUtils;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.android.util.AndroidCommonUtils;
import org.jetbrains.android.util.AndroidCompilerMessageKind;
import org.jetbrains.android.util.AndroidExecutionUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -54,7 +44,7 @@ import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import static com.intellij.openapi.compiler.CompilerMessageCategory.*;
import static org.jetbrains.android.util.AndroidCompilerMessageKind.*;
/**
* @author yole
@@ -68,7 +58,7 @@ public class AndroidApkBuilder {
private AndroidApkBuilder() {
}
private static Map<CompilerMessageCategory, List<String>> filterUsingKeystoreMessages(Map<CompilerMessageCategory, List<String>> messages) {
private static Map<AndroidCompilerMessageKind, List<String>> filterUsingKeystoreMessages(Map<AndroidCompilerMessageKind, List<String>> messages) {
List<String> infoMessages = messages.get(INFORMATION);
if (infoMessages == null) {
infoMessages = new ArrayList<String>();
@@ -118,48 +108,49 @@ public class AndroidApkBuilder {
}
}
public static Map<CompilerMessageCategory, List<String>> execute(Project project,
@NotNull String resPackagePath,
@NotNull String dexPath,
@NotNull VirtualFile[] sourceRoots,
@NotNull String[] externalJars,
@NotNull VirtualFile[] nativeLibsFolders,
@NotNull String finalApk,
boolean unsigned,
@NotNull String sdkPath,
@Nullable String customKeystorePath) throws IOException {
public static Map<AndroidCompilerMessageKind, List<String>> execute(@NotNull String resPackagePath,
@NotNull String dexPath,
@NotNull String[] sourceRoots,
@NotNull String[] externalJars,
@NotNull String[] nativeLibsFolders,
@NotNull String finalApk,
boolean unsigned,
@NotNull String sdkPath,
@Nullable String customKeystorePath,
@NotNull Condition<File> resourceFilter) throws IOException {
if (unsigned) {
return filterUsingKeystoreMessages(
finalPackage(project, dexPath, sourceRoots, externalJars, nativeLibsFolders, finalApk, resPackagePath, customKeystorePath, false));
finalPackage(dexPath, sourceRoots, externalJars, nativeLibsFolders, finalApk, resPackagePath, customKeystorePath, false,
resourceFilter));
}
final Map<CompilerMessageCategory, List<String>> map = new HashMap<CompilerMessageCategory, List<String>>();
final String zipAlignPath = sdkPath + File.separator + AndroidSdkUtils.toolPath(SdkConstants.FN_ZIPALIGN);
final Map<AndroidCompilerMessageKind, List<String>> map = new HashMap<AndroidCompilerMessageKind, List<String>>();
final String zipAlignPath = sdkPath + File.separator + AndroidCommonUtils.toolPath(SdkConstants.FN_ZIPALIGN);
boolean withAlignment = new File(zipAlignPath).exists();
String unalignedApk = finalApk + UNALIGNED_SUFFIX;
Map<CompilerMessageCategory, List<String>> map2 = filterUsingKeystoreMessages(
finalPackage(project, dexPath, sourceRoots, externalJars, nativeLibsFolders, withAlignment ? unalignedApk : finalApk, resPackagePath,
customKeystorePath, true));
Map<AndroidCompilerMessageKind, List<String>> map2 = filterUsingKeystoreMessages(
finalPackage(dexPath, sourceRoots, externalJars, nativeLibsFolders, withAlignment ? unalignedApk : finalApk, resPackagePath,
customKeystorePath, true, resourceFilter));
map.putAll(map2);
if (withAlignment && map.get(ERROR).size() == 0) {
map2 = AndroidCompileUtil.execute(zipAlignPath, "-f", "4", unalignedApk, finalApk);
map2 = AndroidExecutionUtil.doExecute(zipAlignPath, "-f", "4", unalignedApk, finalApk);
map.putAll(map2);
}
return map;
}
private static Map<CompilerMessageCategory, List<String>> finalPackage(Project project,
@NotNull String dexPath,
@NotNull VirtualFile[] sourceRoots,
@NotNull String[] externalJars,
@NotNull VirtualFile[] nativeLibsFolders,
@NotNull String outputApk,
@NotNull String apkPath,
@Nullable String customKeystorePath,
boolean signed) {
final Map<CompilerMessageCategory, List<String>> result = new HashMap<CompilerMessageCategory, List<String>>();
private static Map<AndroidCompilerMessageKind, List<String>> finalPackage(@NotNull String dexPath,
@NotNull String[] sourceRoots,
@NotNull String[] externalJars,
@NotNull String[] nativeLibsFolders,
@NotNull String outputApk,
@NotNull String apkPath,
@Nullable String customKeystorePath,
boolean signed,
@NotNull Condition<File> resourceFilter) {
final Map<AndroidCompilerMessageKind, List<String>> result = new HashMap<AndroidCompilerMessageKind, List<String>>();
result.put(ERROR, new ArrayList<String>());
result.put(INFORMATION, new ArrayList<String>());
result.put(WARNING, new ArrayList<String>());
@@ -187,35 +178,36 @@ public class AndroidApkBuilder {
if (certificate != null && certificate.getNotAfter().compareTo(new Date()) < 0) {
String date = DateFormatUtil.formatPrettyDateTime(certificate.getNotAfter());
result.get(ERROR).add(AndroidBundle.message("android.debug.certificate.expired.error", date, keyStoreOsPath));
result.get(ERROR).add(
("Debug certificate expired on " + date + ". Cannot regenerate it, please delete file \"" + keyStoreOsPath + "\" manually."));
return result;
}
PrivateKey key = provider.getDebugKey();
if (key == null) {
result.get(ERROR).add(AndroidBundle.message("android.cannot.create.new.key.error"));
result.get(ERROR).add("Cannot create new key or keystore");
return result;
}
if (!new File(apkPath).exists()) {
result.get(CompilerMessageCategory.ERROR).add("File " + apkPath + " not found. Try to rebuild project");
result.get(ERROR).add("File " + apkPath + " not found. Try to rebuild project");
return result;
}
File dexEntryFile = new File(dexPath);
if (!dexEntryFile.exists()) {
result.get(CompilerMessageCategory.ERROR).add("File " + dexEntryFile.getPath() + " not found. Try to rebuild project");
result.get(ERROR).add("File " + dexEntryFile.getPath() + " not found. Try to rebuild project");
return result;
}
for (String externalJar : externalJars) {
if (new File(externalJar).isDirectory()) {
result.get(CompilerMessageCategory.ERROR).add(externalJar + " is directory. Directory libraries are not supported");
result.get(ERROR).add(externalJar + " is directory. Directory libraries are not supported");
}
}
if (result.get(CompilerMessageCategory.ERROR).size() > 0) {
if (result.get(ERROR).size() > 0) {
return result;
}
@@ -233,9 +225,10 @@ public class AndroidApkBuilder {
builder.writeFile(dexEntryFile, AndroidCommonUtils.CLASSES_FILE_NAME);
final HashSet<String> added = new HashSet<String>();
for (VirtualFile sourceRoot : sourceRoots) {
final HashSet<VirtualFile> sourceFolderResources = new HashSet<VirtualFile>();
collectStandardSourceFolderResources(sourceRoot, new HashSet<VirtualFile>(), sourceFolderResources, project);
for (String sourceRootPath : sourceRoots) {
final HashSet<File> sourceFolderResources = new HashSet<File>();
final File sourceRoot = new File(sourceRootPath);
collectStandardSourceFolderResources(sourceRoot, sourceFolderResources, resourceFilter);
writeStandardSourceFolderResources(sourceFolderResources, sourceRoot, builder, added);
}
@@ -246,7 +239,7 @@ public class AndroidApkBuilder {
}
for (String duplicate : duplicates) {
result.get(CompilerMessageCategory.WARNING).add("Duplicate entry " + duplicate + ". The file won't be added");
result.get(WARNING).add("Duplicate entry " + duplicate + ". The file won't be added");
}
MyResourceFilter filter = new MyResourceFilter(duplicates);
@@ -262,9 +255,14 @@ public class AndroidApkBuilder {
}
final HashSet<String> nativeLibs = new HashSet<String>();
for (VirtualFile nativeLibsFolder : nativeLibsFolders) {
for (VirtualFile child : nativeLibsFolder.getChildren()) {
writeNativeLibraries(builder, nativeLibsFolder, child, signed, nativeLibs);
for (String nativeLibsFolderPath : nativeLibsFolders) {
final File nativeLibsFolder = new File(nativeLibsFolderPath);
final File[] children = nativeLibsFolder.listFiles();
if (children != null) {
for (File child : children) {
writeNativeLibraries(builder, nativeLibsFolder, child, signed, nativeLibs);
}
}
}
builder.close();
@@ -305,7 +303,7 @@ public class AndroidApkBuilder {
return result;
}
private static DebugKeyProvider createDebugKeyProvider(final Map<CompilerMessageCategory, List<String>> result, String path) throws
private static DebugKeyProvider createDebugKeyProvider(final Map<AndroidCompilerMessageKind, List<String>> result, String path) throws
KeyStoreException,
NoSuchAlgorithmException,
CertificateException,
@@ -326,18 +324,20 @@ public class AndroidApkBuilder {
}
private static void writeNativeLibraries(SignedJarBuilder builder,
VirtualFile nativeLibsFolder,
VirtualFile child,
File nativeLibsFolder,
File child,
boolean debugBuild,
Set<String> added)
throws IOException {
ArrayList<VirtualFile> list = new ArrayList<VirtualFile>();
ArrayList<File> list = new ArrayList<File>();
collectNativeLibraries(child, list, debugBuild);
for (VirtualFile file : list) {
String relativePath = VfsUtilCore.getRelativePath(file, nativeLibsFolder, File.separatorChar);
for (File file : list) {
final String relativePath = FileUtil.getRelativePath(nativeLibsFolder, file);
String path = FileUtil.toSystemIndependentName(SdkConstants.FD_APK_NATIVE_LIBS + File.separator + relativePath);
if (added.add(path)) {
builder.writeFile(toIoFile(file), path);
builder.writeFile(file, path);
LOG.info("Native lib file added to APK: " + file.getPath());
}
else {
@@ -346,104 +346,73 @@ public class AndroidApkBuilder {
}
}
private static Map<CompilerMessageCategory, List<String>> addExceptionMessage(Exception e,
Map<CompilerMessageCategory, List<String>> result) {
private static Map<AndroidCompilerMessageKind, List<String>> addExceptionMessage(Exception e,
Map<AndroidCompilerMessageKind, List<String>> result) {
LOG.info(e);
String simpleExceptionName = e.getClass().getCanonicalName();
result.get(ERROR).add(simpleExceptionName + ": " + e.getMessage());
return result;
}
public static void collectNativeLibraries(@NotNull VirtualFile file, @NotNull List<VirtualFile> result, boolean debugBuild) {
public static void collectNativeLibraries(@NotNull File file, @NotNull List<File> result, boolean debugBuild) {
if (!file.isDirectory()) {
String ext = file.getExtension();
String ext = FileUtil.getExtension(file.getName());
// some users store jars and *.so libs in the same directory. Do not pack JARs to APK's "lib" folder!
if (EXT_NATIVE_LIB.equalsIgnoreCase(ext) ||
(debugBuild && !(file.getFileType() instanceof ArchiveFileType))) {
(debugBuild && !("jar".equals(ext)))) {
result.add(file);
}
}
else if (JavaResourceFilter.checkFolderForPackaging(file.getName())) {
for (VirtualFile child : file.getChildren()) {
collectNativeLibraries(child, result, debugBuild);
final File[] children = file.listFiles();
if (children != null) {
for (File child : children) {
collectNativeLibraries(child, result, debugBuild);
}
}
}
}
public static void collectStandardSourceFolderResources(VirtualFile sourceFolder,
Set<VirtualFile> visited,
Set<VirtualFile> result,
@Nullable Project project) {
visited.add(sourceFolder);
for (VirtualFile child : sourceFolder.getChildren()) {
if (child.exists()) {
if (child.isDirectory()) {
if (!visited.contains(child) &&
JavaResourceFilter.checkFolderForPackaging(child.getName()) && !isExcludedFromCompilation(child, project)) {
collectStandardSourceFolderResources(child, visited, result, project);
public static void collectStandardSourceFolderResources(@NotNull File sourceFolder,
@NotNull Collection<File> result,
@NotNull Condition<File> filter) {
final File[] children = sourceFolder.listFiles();
if (children != null) {
for (File child : children) {
if (child.exists()) {
if (child.isDirectory()) {
if (JavaResourceFilter.checkFolderForPackaging(child.getName()) && filter.value(child)) {
collectStandardSourceFolderResources(child, result, filter);
}
}
else if (checkFileForPackaging(child) && filter.value(child)) {
result.add(child);
}
}
else if (checkFileForPackaging(child) && !isExcludedFromCompilation(child, project)) {
result.add(child);
}
}
}
}
private static boolean isExcludedFromCompilation(VirtualFile child, @Nullable Project project) {
final CompilerManager compilerManager = project != null ? CompilerManager.getInstance(project) : null;
if (compilerManager == null) {
return false;
}
if (!compilerManager.isExcludedFromCompilation(child)) {
return false;
}
final Module module = ModuleUtil.findModuleForFile(child, project);
if (module == null) {
return true;
}
final AndroidFacet facet = AndroidFacet.getInstance(module);
if (facet == null || !facet.getConfiguration().LIBRARY_PROJECT) {
return true;
}
final AndroidPlatform platform = facet.getConfiguration().getAndroidPlatform();
if (platform == null) {
return true;
}
// we exclude sources of library modules automatically for tools r7 or previous
return platform.getSdkData().getPlatformToolsRevision() > 7;
}
private static void writeStandardSourceFolderResources(Collection<VirtualFile> resources,
VirtualFile sourceRoot,
private static void writeStandardSourceFolderResources(Collection<File> resources,
File sourceRoot,
SignedJarBuilder jarBuilder,
Set<String> added) throws IOException {
for (VirtualFile child : resources) {
final String relativePath = FileUtil.toSystemIndependentName(VfsUtilCore.getRelativePath(child, sourceRoot, File.separatorChar));
if (!added.contains(relativePath)) {
File file = toIoFile(child);
jarBuilder.writeFile(file, FileUtil.toSystemIndependentName(relativePath));
for (File child : resources) {
final String relativePath = FileUtil.getRelativePath(sourceRoot, child);
if (relativePath != null && !added.contains(relativePath)) {
jarBuilder.writeFile(child, FileUtil.toSystemIndependentName(relativePath));
added.add(relativePath);
}
}
}
private static File toIoFile(VirtualFile child) {
return new File(FileUtil.toSystemDependentName(child.getPath())).getAbsoluteFile();
}
private static boolean checkFileForPackaging(VirtualFile file) {
String fileName = file.getNameWithoutExtension();
private static boolean checkFileForPackaging(File file) {
String fileName = FileUtil.getNameWithoutExtension(file);
if (fileName.length() > 0) {
return JavaResourceFilter.checkFileForPackaging(fileName, file.getExtension());
return JavaResourceFilter.checkFileForPackaging(fileName, FileUtil.getExtension(file.getName()));
}
return false;
}
@@ -343,4 +343,8 @@ public class AndroidCommonUtils {
return FileUtil.toSystemDependentName(inputJar.getPath());
}
public static String toolPath(@NotNull String toolFileName) {
return SdkConstants.OS_SDK_TOOLS_FOLDER + toolFileName;
}
}
+8
View File
@@ -205,6 +205,14 @@
<compileServer.plugin jar-path="common.jar"/>
<compileServer.plugin jar-path="../../../../../community/plugins/android/lib/common.jar"/>
<compileServer.plugin jar-path="../../../../../plugins/android/lib/common.jar"/>
<compileServer.plugin jar-path="jarutils.jar"/>
<compileServer.plugin jar-path="../../../../../community/plugins/android/lib/jarutils.jar"/>
<compileServer.plugin jar-path="../../../../../plugins/android/lib/jarutils.jar"/>
<compileServer.plugin jar-path="androidprefs.jar"/>
<compileServer.plugin jar-path="../../../../../community/plugins/android/lib/androidprefs.jar"/>
<compileServer.plugin jar-path="../../../../../plugins/android/lib/androidprefs.jar"/>
</extensions>
<extensionPoints>
@@ -20,8 +20,8 @@ import com.intellij.execution.ExecutionException;
import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import org.jetbrains.android.sdk.AndroidSdkUtils;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.android.util.AndroidCommonUtils;
import org.jetbrains.android.util.AndroidUtils;
import org.jetbrains.android.util.ExecutionStatus;
import org.jetbrains.annotations.NotNull;
@@ -47,7 +47,7 @@ public class RunAndroidAvdManagerAction extends AndroidRunSdkToolAction {
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
@Override
public void run() {
final String toolPath = sdkPath + File.separator + AndroidSdkUtils.toolPath(SdkConstants.androidCmdName());
final String toolPath = sdkPath + File.separator + AndroidCommonUtils.toolPath(SdkConstants.androidCmdName());
GeneralCommandLine commandLine = new GeneralCommandLine();
commandLine.setExePath(toolPath);
commandLine.addParameter("avd");
@@ -20,8 +20,8 @@ import com.intellij.execution.ExecutionException;
import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import org.jetbrains.android.sdk.AndroidSdkUtils;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.android.util.AndroidCommonUtils;
import org.jetbrains.android.util.AndroidUtils;
import org.jetbrains.android.util.ExecutionStatus;
import org.jetbrains.annotations.NotNull;
@@ -43,7 +43,7 @@ public class RunAndroidSdkManagerAction extends AndroidRunSdkToolAction {
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
@Override
public void run() {
final String toolPath = sdkPath + File.separator + AndroidSdkUtils.toolPath(SdkConstants.androidCmdName());
final String toolPath = sdkPath + File.separator + AndroidCommonUtils.toolPath(SdkConstants.androidCmdName());
GeneralCommandLine commandLine = new GeneralCommandLine();
commandLine.setExePath(toolPath);
commandLine.addParameter("sdk");
@@ -35,6 +35,7 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.*;
@@ -769,4 +770,39 @@ public class AndroidCompileUtil {
if (compilerOutput == null) return null;
return new File(compilerOutput.getPath(), getApkName(module)).getPath();
}
public static boolean isExcludedFromCompilation(@NotNull File file, @Nullable Project project) {
final VirtualFile vFile = LocalFileSystem.getInstance().findFileByIoFile(file);
return vFile != null && isExcludedFromCompilation(vFile, project);
}
public static boolean isExcludedFromCompilation(VirtualFile child, @Nullable Project project) {
final CompilerManager compilerManager = project != null ? CompilerManager.getInstance(project) : null;
if (compilerManager == null) {
return false;
}
if (!compilerManager.isExcludedFromCompilation(child)) {
return false;
}
final Module module = ModuleUtil.findModuleForFile(child, project);
if (module == null) {
return true;
}
final AndroidFacet facet = AndroidFacet.getInstance(module);
if (facet == null || !facet.getConfiguration().LIBRARY_PROJECT) {
return true;
}
final AndroidPlatform platform = facet.getConfiguration().getAndroidPlatform();
if (platform == null) {
return true;
}
// we exclude sources of library modules automatically for tools r7 or previous
return platform.getSdkData().getPlatformToolsRevision() > 7;
}
}
@@ -20,6 +20,7 @@ import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.compiler.*;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.DependencyScope;
import com.intellij.openapi.roots.ModuleOrderEntry;
import com.intellij.openapi.roots.ModuleRootManager;
@@ -233,9 +234,15 @@ public class AndroidPackagingCompiler implements PackagingCompiler {
? item.getFinalPath() + UNSIGNED_SUFFIX
: item.getFinalPath();
final Map<CompilerMessageCategory, List<String>> messages = AndroidApkBuilder
.execute(context.getProject(), resPackagePath, item.getClassesDexPath(), item.getSourceRoots(), externalLibPaths, item.getNativeLibsFolders(),
finalPath, unsigned, item.mySdkPath, item.getCustomKeystorePath());
final String[] sourceRoots = AndroidCompileUtil.toOsPaths(item.getSourceRoots());
final String[] nativeLibsFolders = AndroidCompileUtil.toOsPaths(item.getNativeLibsFolders());
final Project project = context.getProject();
final Map<CompilerMessageCategory, List<String>> messages = AndroidCompileUtil.toCompilerMessageCategoryKeys(
AndroidApkBuilder.execute(resPackagePath, item.getClassesDexPath(), sourceRoots, externalLibPaths,
nativeLibsFolders, finalPath, unsigned, item.mySdkPath, item.getCustomKeystorePath(),
new ExcludedSourcesFilter(project)));
AndroidCompileUtil.addMessages(context, messages);
}
catch (final IOException e) {
@@ -296,7 +303,7 @@ public class AndroidPackagingCompiler implements PackagingCompiler {
private final boolean myGenerateUnsigendApk;
private final Module myModule;
private boolean myReleaseBuild;
private final String myCustomKeystorePath;
private AptPackagingItem(String sdkPath,
@@ -375,8 +382,8 @@ public class AndroidPackagingCompiler implements PackagingCompiler {
@Nullable
public ValidityState getValidityState() {
return new MyValidityState(myResPackagePath, myClassesDexPath, myFinalPath, myGenerateUnsigendApk, myReleaseBuild,
mySourceRoots, myExternalLibraries, myNativeLibsFolders, myCustomKeystorePath);
return new MyValidityState(myModule.getProject(), myResPackagePath, myClassesDexPath, myFinalPath, myGenerateUnsigendApk,
myReleaseBuild, mySourceRoots, myExternalLibraries, myNativeLibsFolders, myCustomKeystorePath);
}
}
@@ -400,7 +407,8 @@ public class AndroidPackagingCompiler implements PackagingCompiler {
myCustomKeystorePath = CompilerIOUtil.readString(is);
}
MyValidityState(String resPackagePath,
MyValidityState(Project project,
String resPackagePath,
String classesDexPath,
String apkPath,
boolean generateUnsignedApk,
@@ -416,25 +424,26 @@ public class AndroidPackagingCompiler implements PackagingCompiler {
myReleaseBuild = releaseBuild;
myCustomKeystorePath = customKeystorePath != null ? customKeystorePath : "";
final HashSet<VirtualFile> resourcesFromSourceRoot = new HashSet<VirtualFile>();
final HashSet<File> resourcesFromSourceRoot = new HashSet<File>();
for (VirtualFile sourceRoot : sourceRoots) {
AndroidApkBuilder.collectStandardSourceFolderResources(sourceRoot, new HashSet<VirtualFile>(), resourcesFromSourceRoot, null);
AndroidApkBuilder.collectStandardSourceFolderResources(new File(sourceRoot.getPath()), resourcesFromSourceRoot,
new ExcludedSourcesFilter(project));
}
for (VirtualFile resource : resourcesFromSourceRoot) {
myResourceTimestamps.put(resource.getPath(), resource.getTimeStamp());
for (File resource : resourcesFromSourceRoot) {
myResourceTimestamps.put(FileUtil.toSystemIndependentName(resource.getPath()), resource.lastModified());
}
for (VirtualFile externalLib : externalLibs) {
myResourceTimestamps.put(externalLib.getPath(), externalLib.getTimeStamp());
}
ArrayList<VirtualFile> nativeLibs = new ArrayList<VirtualFile>();
ArrayList<File> nativeLibs = new ArrayList<File>();
for (VirtualFile nativeLibFolder : nativeLibFolders) {
for (VirtualFile child : nativeLibFolder.getChildren()) {
AndroidApkBuilder.collectNativeLibraries(child, nativeLibs, !releaseBuild);
AndroidApkBuilder.collectNativeLibraries(new File(child.getPath()), nativeLibs, !releaseBuild);
}
}
for (VirtualFile nativeLib : nativeLibs) {
myResourceTimestamps.put(nativeLib.getPath(), nativeLib.getTimeStamp());
for (File nativeLib : nativeLibs) {
myResourceTimestamps.put(FileUtil.toSystemIndependentName(nativeLib.getPath()), nativeLib.lastModified());
}
}
@@ -447,7 +456,7 @@ public class AndroidPackagingCompiler implements PackagingCompiler {
return mvs.myGenerateUnsignedApk == myGenerateUnsignedApk &&
mvs.myReleaseBuild == myReleaseBuild &&
mvs.myResourceTimestamps.equals(myResourceTimestamps) &&
mvs.myApkPath.equals(myApkPath) &&
mvs.myApkPath.equals(myApkPath) &&
mvs.myCustomKeystorePath.equals(myCustomKeystorePath);
}
@@ -0,0 +1,23 @@
package org.jetbrains.android.compiler;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import org.jetbrains.annotations.NotNull;
import java.io.File;
/**
* @author Eugene.Kudelevsky
*/
class ExcludedSourcesFilter implements Condition<File> {
private final Project myProject;
public ExcludedSourcesFilter(@NotNull Project project) {
myProject = project;
}
@Override
public boolean value(File file) {
return !AndroidCompileUtil.isExcludedFromCompilation(file, myProject);
}
}
@@ -51,8 +51,8 @@ import org.jetbrains.android.compiler.AndroidProguardCompiler;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.facet.AndroidRootUtil;
import org.jetbrains.android.sdk.AndroidPlatform;
import org.jetbrains.android.sdk.AndroidSdkUtils;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.android.util.AndroidCommonUtils;
import org.jetbrains.android.util.SaveFileListener;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -196,7 +196,7 @@ class ApkStep extends ExportSignedPackageWizardStep {
AndroidPlatform platform = myWizard.getFacet().getConfiguration().getAndroidPlatform();
assert platform != null;
String sdkPath = platform.getSdkData().getLocation();
String zipAlignPath = sdkPath + File.separatorChar + AndroidSdkUtils.toolPath(SdkConstants.FN_ZIPALIGN);
String zipAlignPath = sdkPath + File.separatorChar + AndroidCommonUtils.toolPath(SdkConstants.FN_ZIPALIGN);
File zipalign = new File(zipAlignPath);
final boolean runZipAlign = zipalign.isFile();
File destFile = null;
@@ -81,6 +81,7 @@ import org.jetbrains.android.resourceManagers.ResourceManager;
import org.jetbrains.android.resourceManagers.SystemResourceManager;
import org.jetbrains.android.sdk.*;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.android.util.AndroidCommonUtils;
import org.jetbrains.android.util.AndroidUtils;
import org.jetbrains.android.util.ResourceEntry;
import org.jetbrains.annotations.NotNull;
@@ -392,7 +393,8 @@ public class AndroidFacet extends Facet<AndroidFacetConfiguration> {
public void launchEmulator(@Nullable final String avdName, @NotNull final String commands, @Nullable ProcessHandler handler) {
AndroidPlatform platform = getConfiguration().getAndroidPlatform();
if (platform != null) {
final String emulatorPath = platform.getSdkData().getLocation() + File.separator + AndroidSdkUtils.toolPath(SdkConstants.FN_EMULATOR);
final String emulatorPath = platform.getSdkData().getLocation() + File.separator + AndroidCommonUtils
.toolPath(SdkConstants.FN_EMULATOR);
final GeneralCommandLine commandLine = new GeneralCommandLine();
commandLine.setExePath(FileUtil.toSystemDependentName(emulatorPath));
if (avdName != null) {
@@ -63,6 +63,7 @@ import org.jetbrains.android.run.testing.AndroidTestRunConfigurationType;
import org.jetbrains.android.sdk.AndroidPlatform;
import org.jetbrains.android.sdk.AndroidSdkUtils;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.android.util.AndroidCommonUtils;
import org.jetbrains.android.util.AndroidUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -218,7 +219,7 @@ public class AndroidModuleBuilder extends JavaModuleBuilder {
IAndroidTarget target = platform.getTarget();
final String androidToolPath =
platform.getSdkData().getLocation() + File.separator + AndroidSdkUtils.toolPath(SdkConstants.androidCmdName());
platform.getSdkData().getLocation() + File.separator + AndroidCommonUtils.toolPath(SdkConstants.androidCmdName());
if (!new File(androidToolPath).exists()) {
return false;
@@ -30,6 +30,7 @@ import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.sdk.AndroidSdkData;
import org.jetbrains.android.sdk.AndroidSdkUtils;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.android.util.AndroidCommonUtils;
import org.jetbrains.android.util.AndroidUtils;
import org.jetbrains.android.util.BooleanCellRenderer;
import org.jetbrains.annotations.NotNull;
@@ -71,7 +72,7 @@ public class AvdChooser extends DialogWrapper {
AndroidSdkData sdkData = facet.getConfiguration().getAndroidSdk();
if (sdkData == null) return null;
String androidCmd = SdkConstants.androidCmdName();
return sdkData.getLocation() + File.separator + AndroidSdkUtils.toolPath(androidCmd);
return sdkData.getLocation() + File.separator + AndroidCommonUtils.toolPath(androidCmd);
}
public AvdChooser(@NotNull final Project project,
@@ -300,7 +300,7 @@ public class AndroidSdkData {
private String getAdbPath() {
String path = getLocation() + File.separator + SdkConstants.OS_SDK_PLATFORM_TOOLS_FOLDER + SdkConstants.FN_ADB;
if (!new File(path).exists()) {
return getLocation() + File.separator + AndroidSdkUtils.toolPath(SdkConstants.FN_ADB);
return getLocation() + File.separator + AndroidCommonUtils.toolPath(SdkConstants.FN_ADB);
}
return path;
}
@@ -423,10 +423,6 @@ public class AndroidSdkUtils {
};
}
public static String toolPath(@NotNull String toolFileName) {
return SdkConstants.OS_SDK_TOOLS_FOLDER + toolFileName;
}
@Nullable
public static Sdk findAppropriateAndroidPlatform(@NotNull IAndroidTarget target, @NotNull AndroidSdkData sdkData) {
for (Sdk library : ProjectJdkTable.getInstance().getAllJdks()) {
-2
View File
@@ -1,2 +0,0 @@
Features:
- Groovy support
@@ -37,6 +37,7 @@ import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.AsynchConsumer;
import com.intellij.util.Consumer;
import gnu.trove.TObjectLongHashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.netbeans.lib.cvsclient.admin.Entry;
@@ -73,8 +74,8 @@ public class CvsCommittedChangesProvider implements CachingCommittedChangesProvi
}
private static class MyZipper extends VcsCommittedListsZipperAdapter {
private long lastNumber = 0;
private final Map<CommittedChangeListKey, Long> numberCache = new HashMap();
private long lastNumber = 1;
private final TObjectLongHashMap<CommittedChangeListKey> numberCache = new TObjectLongHashMap<CommittedChangeListKey>();
private MyZipper() {
super(new GroupCreator() {
@@ -98,12 +99,12 @@ public class CvsCommittedChangesProvider implements CachingCommittedChangesProvi
final long time = list.getCommitDate().getTime();
final Long roundedTime = Long.valueOf(time - (time % CvsChangeList.SUITABLE_DIFF));
final CommittedChangeListKey key = new CommittedChangeListKey(list.getCommitterName(), roundedTime, list.getComment());
final Long number = numberCache.get(key);
if (number == null) {
numberCache.put(key, Long.valueOf(lastNumber));
final long number = numberCache.get(key);
if (number == 0) {
numberCache.put(key, lastNumber);
return lastNumber++;
}
return number.longValue();
return number;
}
}
@@ -384,7 +384,8 @@ public class CompleteReferenceExpression {
}
GroovyResolveResult result = (GroovyResolveResult)o;
if (!result.isStaticsOK() || !result.isAccessible()) return;
if (!result.isStaticsOK()) return;
if (!result.isAccessible() && myParameters.getInvocationCount() < 2) return;
if (mySkipPackages && result.getElement() instanceof PsiPackage) return;
PsiElement element = result.getElement();
@@ -115,6 +115,13 @@ public abstract class MvcFramework {
}
}
public boolean isInteractiveConsoleSupport(@NotNull Module module) {
return false;
}
public void runInteractiveConsole(@NotNull Module module) {
throw new UnsupportedOperationException();
}
public abstract void upgradeFramework(@NotNull Module module);
@@ -58,7 +58,7 @@
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="VM options:"/>
<text value="&amp;VM options:"/>
</properties>
</component>
<component id="b107f" class="javax.swing.JLabel" binding="myModuleLabel">
@@ -66,7 +66,7 @@
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Module:"/>
<text value="&amp;Module:"/>
</properties>
</component>
<component id="cf36f" class="javax.swing.JComboBox" binding="myModuleBox">
@@ -54,15 +54,49 @@ public class MvcRunTargetDialog extends DialogWrapper {
private ComboBox myTargetField;
private Module myModule;
public MvcRunTargetDialog(@NotNull Module module, MvcFramework framework) {
private final MvcFramework myFramework;
private Action myInteractiveRunAction;
public MvcRunTargetDialog(@NotNull Module module, @NotNull MvcFramework framework) {
super(module.getProject(), true);
myModule = module;
myFramework = framework;
setTitle("Run " + framework.getDisplayName() + " target");
setUpDialog();
setModal(true);
init();
}
@Override
protected Action[] createLeftSideActions() {
boolean hasOneSupportedModule = false;
for (Module module : ModuleManager.getInstance(myModule.getProject()).getModules()) {
if (module == myModule || myFramework.hasSupport(module)) {
if (myFramework.isInteractiveConsoleSupport(module)) {
hasOneSupportedModule = true;
break;
}
}
}
if (hasOneSupportedModule) {
myInteractiveRunAction = new DialogWrapperAction("&Run Interactive Console") {
@Override
protected void doAction(ActionEvent e) {
myFramework.runInteractiveConsole(getSelectedModule());
doCancelAction();
}
};
myInteractiveRunAction.setEnabled(myFramework.isInteractiveConsoleSupport(myModule));
return new Action[]{myInteractiveRunAction};
}
return new Action[0];
}
private void setUpDialog() {
myTargetLabel.setLabelFor(myTargetField);
myTargetField.setFocusable(true);
@@ -72,7 +106,7 @@ public class MvcRunTargetDialog extends DialogWrapper {
List<Module> mvcModules = new ArrayList<Module>();
for (Module module : ModuleManager.getInstance(myModule.getProject()).getModules()) {
if (module == myModule || MvcFramework.getInstance(module) != null) {
if (module == myModule || myFramework.hasSupport(module)) {
mvcModules.add(module);
}
}
@@ -85,6 +119,9 @@ public class MvcRunTargetDialog extends DialogWrapper {
@Override
public void actionPerformed(ActionEvent e) {
myModule = (Module)myModuleBox.getSelectedItem();
if (myInteractiveRunAction != null) {
myInteractiveRunAction.setEnabled(myFramework.isInteractiveConsoleSupport(myModule));
}
}
});
@@ -1172,4 +1172,15 @@ class Base {
new Base().@prefixField<caret>''')
}
public void testPrivateFieldOnSecondInvocation() {
myFixture.configureByText('_a.groovy', '''\
class Base {
private int field1
}
new Base().fie<caret>x''')
myFixture.complete(CompletionType.BASIC, 2)
assert myFixture.lookupElementStrings == ['field1']
}
}
@@ -168,7 +168,7 @@ public class SvnDiffProvider implements DiffProvider, DiffMixin {
LOG.info("No SVN status returned for " + file.getPath());
return defaultResult();
}
return createResult(info.getRevision(), true, false);
return createResult(info.getCommittedRevision(), true, false);
}
final boolean exists = ! SVNStatusType.STATUS_DELETED.equals(svnStatus.getRemoteContentsStatus()) &&
! SVNStatusType.STATUS_DELETED.equals(svnStatus.getRemoteNodeStatus());
@@ -83,7 +83,8 @@ public class SvnCommandLineUpdateClient extends SvnSvnkitUpdateClient {
new CommandLineAuthenticator(myProject, new CommandLineAuthenticator.AuthenticationRequiringCommand() {
@Override
public void run(File configDir) throws SVNException {
final File base = myCommonAncestor == null ? paths[0] : new File(myCommonAncestor.getPath());
File base = myCommonAncestor == null ? paths[0] : new File(myCommonAncestor.getPath());
base = base.isDirectory() ? base : base.getParentFile();
final SvnLineCommand command = new SvnLineCommand(myProject, base, SvnCommandName.up);
if (revision != null && ! SVNRevision.UNDEFINED.equals(revision) && ! SVNRevision.WORKING.equals(revision)) {
command.addParameters("-r", revision.toString());
@@ -137,7 +137,7 @@ public class SvnHistoryProvider implements VcsHistoryProvider, VcsCacheableHisto
@NotNull List<VcsFileRevision> revisions,
@NotNull FilePath filePath,
VcsRevisionNumber currentRevision) {
return new MyHistorySession(revisions, filePath, aBoolean, currentRevision);
return new MyHistorySession(revisions, filePath, aBoolean, currentRevision, false);
}
class MyHistorySession extends VcsAbstractHistorySession {
@@ -145,11 +145,13 @@ public class SvnHistoryProvider implements VcsHistoryProvider, VcsCacheableHisto
private final boolean mySupports15;
private MyHistorySession(final List<VcsFileRevision> revisions, final FilePath committedPath, final boolean supports15,
@Nullable final VcsRevisionNumber currentRevision) {
@Nullable final VcsRevisionNumber currentRevision, boolean skipRefreshOnStart) {
super(revisions, currentRevision);
myCommittedPath = committedPath;
mySupports15 = supports15;
shouldBeRefreshed();
if (! skipRefreshOnStart) {
shouldBeRefreshed();
}
}
public HistoryAsTreeProvider getHistoryAsTreeProvider() {
@@ -161,7 +163,23 @@ public class SvnHistoryProvider implements VcsHistoryProvider, VcsCacheableHisto
if (myCommittedPath == null) {
return null;
}
return getCurrentRevision(myCommittedPath);
if (myCommittedPath.isNonLocal()) {
// technically, it does not make sense, since there's no "current" revision for non-local history (if look how it's used)
// but ok, lets keep it for now
return new SvnRevisionNumber(SVNRevision.HEAD);
}
try {
SVNWCClient wcClient = myVcs.createWCClient();
SVNInfo info = wcClient.doInfo(new File(myCommittedPath.getPath()), SVNRevision.UNDEFINED);
if (info != null) {
return new SvnRevisionNumber(info.getCommittedRevision());
} else {
return null;
}
}
catch (SVNException e) {
return null;
}
}
public FilePath getCommittedPath() {
@@ -179,7 +197,7 @@ public class SvnHistoryProvider implements VcsHistoryProvider, VcsCacheableHisto
@Override
public VcsHistorySession copy() {
return new MyHistorySession(getRevisionList(), myCommittedPath, mySupports15, getCurrentRevisionNumber());
return new MyHistorySession(getRevisionList(), myCommittedPath, mySupports15, getCurrentRevisionNumber(), true);
}
}
@@ -212,7 +230,7 @@ public class SvnHistoryProvider implements VcsHistoryProvider, VcsCacheableHisto
logLoader.initSupports15();
final MyHistorySession historySession =
new MyHistorySession(Collections.<VcsFileRevision>emptyList(), committedPath, Boolean.TRUE.equals(logLoader.mySupport15), null);
new MyHistorySession(Collections.<VcsFileRevision>emptyList(), committedPath, Boolean.TRUE.equals(logLoader.mySupport15), null, false);
final Ref<Boolean> sessionReported = new Ref<Boolean>();
final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
@@ -44,4 +44,6 @@ public abstract class LocalTask extends Task {
public boolean isDefault() {
return false;
}
public abstract void setAssociatedChangelistId(String associatedChangelistId);
}
@@ -179,7 +179,7 @@ public class ActivateTaskDialog extends DialogWrapper {
}
}
}
myMarkAsInProgressBox.setSelected(myMarkAsInProgress && isMarkAsInProgressApplicable());
myMarkAsInProgressBox.setSelected(myMarkAsInProgress);
// refresh change lists
ChangeListManager changeListManager = ChangeListManager.getInstance(myProject);
@@ -19,8 +19,8 @@ package com.intellij.tasks.actions;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.ui.Messages;
import com.intellij.tasks.LocalTask;
import com.intellij.tasks.TaskManager;
import com.intellij.tasks.Task;
import com.intellij.tasks.impl.TaskManagerImpl;
import com.intellij.tasks.impl.TaskUtil;
@@ -34,7 +34,7 @@ public class CreateChangelistAction extends BaseTaskAction {
TaskManager manager = getTaskManager(event);
Presentation presentation = event.getPresentation();
if (manager == null || !manager.getOpenChangelists(manager.getActiveTask()).isEmpty()) {
if (manager == null || !manager.isVcsEnabled() || !manager.getOpenChangelists(manager.getActiveTask()).isEmpty()) {
presentation.setText(getTemplatePresentation().getText());
presentation.setEnabled(false);
} else {
@@ -45,8 +45,8 @@ public class CreateChangelistAction extends BaseTaskAction {
@Override
public void actionPerformed(AnActionEvent e) {
Task activeTask = getActiveTask(e);
String name = Messages.showInputDialog(getProject(e), "Changelist name:", "Create changelist", null, TaskUtil.getChangeListName(activeTask), null);
LocalTask activeTask = getActiveTask(e);
String name = Messages.showInputDialog(getProject(e), "Changelist name:", "Create Changelist", null, TaskUtil.getChangeListName(activeTask), null);
if (name != null) {
TaskManagerImpl manager = (TaskManagerImpl)getTaskManager(e);
assert manager != null;
@@ -24,6 +24,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.JDOMUtil;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.tasks.Task;
import com.intellij.util.NullableFunction;
import com.intellij.util.containers.ContainerUtil;
@@ -153,7 +154,7 @@ public class WorkingContextManager {
private boolean loadContext(String zipPostfix, String entryName) {
try {
JBZipFile archive = getTasksArchive(zipPostfix);
JBZipEntry entry = archive.getEntry(entryName.startsWith("/") ? entryName : "/" + entryName);
JBZipEntry entry = archive.getEntry(StringUtil.startsWithChar(entryName, '/') ? entryName : "/" + entryName);
if (entry != null) {
byte[] bytes = entry.getData();
Document document = JDOMUtil.loadDocument(new String(bytes));
@@ -196,6 +196,7 @@ public class LocalTaskImpl extends LocalTask {
myChangeLists = changeLists;
}
@Override
public void setAssociatedChangelistId(String associatedChangelistId) {
myAssociatedChangelistId = associatedChangelistId;
}
@@ -310,7 +310,7 @@ public class TaskManagerImpl extends TaskManager implements ProjectComponent, Pe
}
myContextManager.restoreContext(origin);
final Task task = doActivate(origin, true);
final LocalTask task = doActivate(origin, true);
if (isVcsEnabled()) {
List<ChangeListInfo> changeLists = getOpenChangelists(task);
@@ -340,17 +340,18 @@ public class TaskManagerImpl extends TaskManager implements ProjectComponent, Pe
myActiveTask.setUpdated(new Date());
}
public void createChangeList(Task task, String name) {
public void createChangeList(LocalTask task, String name) {
String comment = TaskUtil.getChangeListComment(this, task);
createChangeList(task, name, comment);
}
public void createChangeList(Task task, String name, String comment) {
public void createChangeList(LocalTask task, String name, String comment) {
LocalChangeList changeList = myChangeListManager.findChangeList(name);
if (changeList == null) {
changeList = myChangeListManager.addChangeList(name, comment);
}
myChangeListManager.setDefaultChangeList(changeList);
task.setAssociatedChangelistId(changeList.getId());
getOpenChangelists(task).add(new ChangeListInfo(changeList));
}