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