/*
* Copyright 2003-2015 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 jetbrains.mps.jps.build;
import jetbrains.mps.generator.impl.dependencies.GenerationDependenciesCache;
import jetbrains.mps.generator.info.ForeignPathsProvider;
import jetbrains.mps.generator.info.GeneratorPathsComponent;
// hm... using collections runtime in a non-mps-generated, hand-written java file...?
import jetbrains.mps.internal.collections.runtime.ISelector;
import jetbrains.mps.internal.collections.runtime.ISequence;
import jetbrains.mps.internal.collections.runtime.Sequence;
import jetbrains.mps.internal.make.runtime.util.DirUtil;
import jetbrains.mps.jps.model.JpsMPSExtensionService;
import jetbrains.mps.jps.model.JpsMPSModuleExtension;
import jetbrains.mps.jps.project.JpsMPSProject;
import jetbrains.mps.smodel.resources.MResource;
import jetbrains.mps.tool.builder.paths.ModuleOutputPaths;
import jetbrains.mps.vfs.IFile;
import org.jetbrains.jps.incremental.CompileContext;
import org.jetbrains.jps.incremental.ModuleBuildTarget;
import org.jetbrains.jps.incremental.storage.BuildDataManager;
import org.jetbrains.mps.openapi.module.SModule;
import java.io.File;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class GenerationPathsController {
private final JpsMPSProject myProject;
private final CompileContext myContext;
private final Iterable<MResource> myModelResources;
private final JpsRedirects myRedirects = new JpsRedirects();
private final Map<ModuleBuildTarget, File> myOutputRootsPerTarget = new HashMap<ModuleBuildTarget, File>();
private ModuleOutputPaths myOutputPaths;
private MyForeignRootPaths myForeignRootPaths;
public GenerationPathsController(JpsMPSProject project, CompileContext context, Iterable<MResource> modelResources) {
myProject = project;
myContext = context;
myModelResources = modelResources;
}
public JpsRedirects getRedirects() {
return myRedirects;
}
public File getOutputRoot(ModuleBuildTarget target) {
return myOutputRootsPerTarget.get(target);
}
private ISequence<SModule> getModulesInvolved(Iterable<MResource> resources) {
return Sequence.fromIterable(resources).select(new ISelector<MResource, SModule>() {
@Override
public SModule select(MResource r) {
return r.module();
}
});
}
public void registerRedirects() {
GenerationDependenciesCache.getInstance().registerCachePathRedirect(new GenerationDependenciesCache.CachePathRedirect() {
@Override
public IFile redirectTo(IFile outputFile) {
return myRedirects.getRedirect(outputFile.getPath());
}
});
}
public void registerForeignPathsProvider() {
GeneratorPathsComponent.getInstance().registerForeignPathsProvider(new ForeignPathsProvider() {
@Override
public String belongsToForeignPath(IFile path) {
return myForeignRootPaths != null ? myForeignRootPaths.findForeignPrefix(path.getPath()) : null;
}
});
}
public void initWithTargets(Collection<ModuleBuildTarget> targets) {
Set<ModuleBuildTarget> processed = new HashSet<ModuleBuildTarget>();
for (ModuleBuildTarget target : targets) {
if (processed.contains(target)) continue;
processed.add(target);
JpsMPSModuleExtension mpsModule = JpsMPSExtensionService.getInstance().getExtension(target.getModule());
if (mpsModule != null) {
final BuildDataManager dataManager = myContext.getProjectDescriptor().dataManager;
OutputPathsCalculator pathsCalculator = new OutputPathsCalculator(mpsModule, dataManager.getDataPaths());
final File tmpOutputRoot = pathsCalculator.getTmpOutputRoot();
final File cachesOutputRoot = pathsCalculator.getCachesOutputRoot();
final boolean transientOutputFolder = pathsCalculator.isTransientOutputFolder();
myRedirects.addRedirects(myOutputPaths, tmpOutputRoot.getPath(), cachesOutputRoot.getPath(), transientOutputFolder);
myOutputRootsPerTarget.put(target, pathsCalculator.getOutputPath());
}
}
}
public void init(Collection<ModuleBuildTarget> targets) {
final ISequence<SModule> modulesInvolvedInMake = getModulesInvolved(myModelResources);
myProject.getModelAccess().runReadAction(new Runnable() {
@Override
public void run() {
myOutputPaths = new ModuleOutputPaths(modulesInvolvedInMake);
}
});
myForeignRootPaths = new MyForeignRootPaths(myOutputPaths.getOutputPaths());
initWithTargets(targets);
registerForeignPathsProvider();
registerRedirects();
}
private static class MyForeignRootPaths {
private final String[] myRootPaths;
public MyForeignRootPaths(Iterable<String> foreignRoots) {
myRootPaths = Sequence.fromIterable(foreignRoots).select(new ISelector<String, String>() {
@Override
public String select(String dir) {
return DirUtil.normalizeAsDir(dir);
}
}).sort(new ISelector<String, String>() {
@Override
public String select(String dir) {
return dir;
}
}, true).toGenericArray(String.class);
}
public String findForeignPrefix(String path) {
int idx = DirUtil.findPrefixAsDir(path, myRootPaths);
return idx >= 0 ? myRootPaths[idx] : null;
}
}
}