blob: c4928a05dfe6b2c72070f540efb33bfbe70983c0 [file] [log] [blame]
/*
* 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 org.jetbrains.jps.cmdline;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.io.FileUtil;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.api.BuildType;
import org.jetbrains.jps.api.CanceledStatus;
import org.jetbrains.jps.api.GlobalOptions;
import org.jetbrains.jps.builders.*;
import org.jetbrains.jps.builders.impl.BuildDataPathsImpl;
import org.jetbrains.jps.builders.impl.BuildRootIndexImpl;
import org.jetbrains.jps.builders.impl.BuildTargetIndexImpl;
import org.jetbrains.jps.builders.java.dependencyView.Callbacks;
import org.jetbrains.jps.builders.logging.BuildLoggingManager;
import org.jetbrains.jps.builders.storage.BuildDataPaths;
import org.jetbrains.jps.incremental.*;
import org.jetbrains.jps.incremental.fs.BuildFSState;
import org.jetbrains.jps.incremental.messages.BuildMessage;
import org.jetbrains.jps.incremental.messages.CompilerMessage;
import org.jetbrains.jps.incremental.storage.BuildDataManager;
import org.jetbrains.jps.incremental.storage.BuildTargetsState;
import org.jetbrains.jps.incremental.storage.ProjectTimestamps;
import org.jetbrains.jps.incremental.storage.Timestamps;
import org.jetbrains.jps.indices.ModuleExcludeIndex;
import org.jetbrains.jps.indices.impl.IgnoredFileIndexImpl;
import org.jetbrains.jps.indices.impl.ModuleExcludeIndexImpl;
import org.jetbrains.jps.model.JpsModel;
import java.io.File;
import java.io.IOException;
import java.util.*;
import static org.jetbrains.jps.api.CmdlineRemoteProto.Message.ControllerMessage.ParametersMessage.TargetTypeBuildScope;
/**
* @author nik
*/
public class BuildRunner {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.cmdline.BuildRunner");
public static final boolean PARALLEL_BUILD_ENABLED = Boolean.parseBoolean(System.getProperty(GlobalOptions.COMPILE_PARALLEL_OPTION, "false"));
private static final boolean STORE_TEMP_CACHES_IN_MEMORY = PARALLEL_BUILD_ENABLED || Boolean.valueOf(System.getProperty(GlobalOptions.USE_MEMORY_TEMP_CACHE_OPTION, "true"));
private final JpsModelLoader myModelLoader;
private final List<String> myFilePaths;
private final Map<String, String> myBuilderParams;
private boolean myForceCleanCaches;
public BuildRunner(JpsModelLoader modelLoader, List<String> filePaths, Map<String, String> builderParams) {
myModelLoader = modelLoader;
myFilePaths = filePaths;
myBuilderParams = builderParams;
}
public ProjectDescriptor load(MessageHandler msgHandler, File dataStorageRoot, BuildFSState fsState) throws IOException {
final JpsModel jpsModel = myModelLoader.loadModel();
BuildDataPaths dataPaths = new BuildDataPathsImpl(dataStorageRoot);
BuildTargetIndexImpl targetIndex = new BuildTargetIndexImpl(jpsModel);
ModuleExcludeIndex index = new ModuleExcludeIndexImpl(jpsModel);
IgnoredFileIndexImpl ignoredFileIndex = new IgnoredFileIndexImpl(jpsModel);
BuildRootIndexImpl buildRootIndex = new BuildRootIndexImpl(targetIndex, jpsModel, index, dataPaths, ignoredFileIndex);
BuildTargetsState targetsState = new BuildTargetsState(dataPaths, jpsModel, buildRootIndex);
ProjectTimestamps projectTimestamps = null;
BuildDataManager dataManager = null;
try {
projectTimestamps = new ProjectTimestamps(dataStorageRoot, targetsState);
dataManager = new BuildDataManager(dataPaths, targetsState, STORE_TEMP_CACHES_IN_MEMORY);
if (dataManager.versionDiffers()) {
myForceCleanCaches = true;
msgHandler.processMessage(new CompilerMessage("build", BuildMessage.Kind.INFO, "Dependency data format has changed, project rebuild required"));
}
}
catch (Exception e) {
// second try
LOG.info(e);
if (projectTimestamps != null) {
projectTimestamps.close();
}
if (dataManager != null) {
dataManager.close();
}
myForceCleanCaches = true;
FileUtil.delete(dataStorageRoot);
targetsState = new BuildTargetsState(dataPaths, jpsModel, buildRootIndex);
projectTimestamps = new ProjectTimestamps(dataStorageRoot, targetsState);
dataManager = new BuildDataManager(dataPaths, targetsState, STORE_TEMP_CACHES_IN_MEMORY);
// second attempt succeeded
msgHandler.processMessage(new CompilerMessage("build", BuildMessage.Kind.INFO, "Project rebuild forced: " + e.getMessage()));
}
return new ProjectDescriptor(jpsModel, fsState, projectTimestamps, dataManager, BuildLoggingManager.DEFAULT, index, targetsState,
targetIndex, buildRootIndex, ignoredFileIndex);
}
public void setForceCleanCaches(boolean forceCleanCaches) {
myForceCleanCaches = forceCleanCaches;
}
public void runBuild(ProjectDescriptor pd,
CanceledStatus cs,
@Nullable Callbacks.ConstantAffectionResolver constantSearch,
MessageHandler msgHandler,
BuildType buildType,
List<TargetTypeBuildScope> scopes, final boolean includeDependenciesToScope) throws Exception {
for (int attempt = 0; attempt < 2; attempt++) {
final boolean forceClean = myForceCleanCaches && myFilePaths.isEmpty();
final CompileScope compileScope = createCompilationScope(pd, scopes, myFilePaths, forceClean, includeDependenciesToScope);
final IncProjectBuilder builder = new IncProjectBuilder(pd, BuilderRegistry.getInstance(), myBuilderParams, cs, constantSearch, Utils.IS_TEST_MODE);
builder.addMessageHandler(msgHandler);
try {
switch (buildType) {
case BUILD:
builder.build(compileScope, forceClean);
break;
case CLEAN:
//todo[nik]
// new ProjectBuilder(new GantBinding(), project).clean();
break;
case UP_TO_DATE_CHECK:
builder.checkUpToDate(compileScope);
break;
}
break; // break attempts loop
}
catch (RebuildRequestedException e) {
if (attempt == 0) {
LOG.info(e);
myForceCleanCaches = true;
}
else {
throw e;
}
}
}
}
private static CompileScope createCompilationScope(ProjectDescriptor pd, List<TargetTypeBuildScope> scopes, Collection<String> paths,
final boolean forceClean, final boolean includeDependenciesToScope) throws Exception {
Set<BuildTargetType<?>> targetTypes = new HashSet<BuildTargetType<?>>();
Set<BuildTargetType<?>> targetTypesToForceBuild = new HashSet<BuildTargetType<?>>();
Set<BuildTarget<?>> targets = new HashSet<BuildTarget<?>>();
Map<BuildTarget<?>, Set<File>> files;
final TargetTypeRegistry typeRegistry = TargetTypeRegistry.getInstance();
for (TargetTypeBuildScope scope : scopes) {
final BuildTargetType<?> targetType = typeRegistry.getTargetType(scope.getTypeId());
if (targetType == null) {
LOG.info("Unknown target type: " + scope.getTypeId());
continue;
}
if (scope.getForceBuild() || forceClean) {
targetTypesToForceBuild.add(targetType);
}
if (scope.getAllTargets()) {
targetTypes.add(targetType);
}
else {
BuildTargetLoader<?> loader = targetType.createLoader(pd.getModel());
for (String targetId : scope.getTargetIdList()) {
BuildTarget<?> target = loader.createTarget(targetId);
if (target != null) {
targets.add(target);
}
else {
LOG.info("Unknown " + targetType + " target id: " + targetId);
}
}
}
}
if (includeDependenciesToScope) {
includeDependenciesToScope(targetTypes, targets, targetTypesToForceBuild, pd);
}
final Timestamps timestamps = pd.timestamps.getStorage();
if (!paths.isEmpty()) {
files = new HashMap<BuildTarget<?>, Set<File>>();
for (String path : paths) {
final File file = new File(path);
final Collection<BuildRootDescriptor> descriptors = pd.getBuildRootIndex().findAllParentDescriptors(file, null);
for (BuildRootDescriptor descriptor : descriptors) {
Set<File> fileSet = files.get(descriptor.getTarget());
if (fileSet == null) {
fileSet = new THashSet<File>(FileUtil.FILE_HASHING_STRATEGY);
files.put(descriptor.getTarget(), fileSet);
}
fileSet.add(file);
if (targetTypesToForceBuild.contains(descriptor.getTarget().getTargetType())) {
pd.fsState.markDirty(null, file, descriptor, timestamps, false);
}
}
}
}
else {
files = Collections.emptyMap();
}
return new CompileScopeImpl(targetTypes, targetTypesToForceBuild, targets, files);
}
private static void includeDependenciesToScope(Set<BuildTargetType<?>> targetTypes, Set<BuildTarget<?>> targets,
Set<BuildTargetType<?>> targetTypesToForceBuild, ProjectDescriptor descriptor) {
//todo[nik] get rid of CompileContext parameter for BuildTargetIndex.getDependencies() and use it here
TargetOutputIndex dummyIndex = new TargetOutputIndex() {
@Override
public Collection<BuildTarget<?>> getTargetsByOutputFile(@NotNull File file) {
return Collections.emptyList();
}
};
List<BuildTarget<?>> current = new ArrayList<BuildTarget<?>>(targets);
while (!current.isEmpty()) {
List<BuildTarget<?>> next = new ArrayList<BuildTarget<?>>();
for (BuildTarget<?> target : current) {
for (BuildTarget<?> depTarget : target.computeDependencies(descriptor.getBuildTargetIndex(), dummyIndex)) {
if (!targets.contains(depTarget) && !targetTypes.contains(depTarget.getTargetType())) {
next.add(depTarget);
if (targetTypesToForceBuild.contains(target.getTargetType())) {
targetTypesToForceBuild.add(depTarget.getTargetType());
}
}
}
}
targets.addAll(next);
current = next;
}
}
}