blob: 81b0c82988134139be4586f62db9d34e11bc087c [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.model.serialization;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.JDOMUtil;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.util.ArrayUtil;
import com.intellij.util.concurrency.BoundedTaskExecutor;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.TimingLog;
import org.jetbrains.jps.model.JpsDummyElement;
import org.jetbrains.jps.model.JpsElement;
import org.jetbrains.jps.model.JpsElementFactory;
import org.jetbrains.jps.model.JpsProject;
import org.jetbrains.jps.model.java.JpsJavaModuleType;
import org.jetbrains.jps.model.library.sdk.JpsSdkType;
import org.jetbrains.jps.model.module.JpsModule;
import org.jetbrains.jps.model.serialization.artifact.JpsArtifactSerializer;
import org.jetbrains.jps.model.serialization.facet.JpsFacetSerializer;
import org.jetbrains.jps.model.serialization.impl.JpsModuleSerializationDataExtensionImpl;
import org.jetbrains.jps.model.serialization.impl.JpsProjectSerializationDataExtensionImpl;
import org.jetbrains.jps.model.serialization.library.JpsLibraryTableSerializer;
import org.jetbrains.jps.model.serialization.library.JpsSdkTableSerializer;
import org.jetbrains.jps.model.serialization.module.JpsModuleClasspathSerializer;
import org.jetbrains.jps.model.serialization.module.JpsModulePropertiesSerializer;
import org.jetbrains.jps.model.serialization.module.JpsModuleRootModelSerializer;
import org.jetbrains.jps.model.serialization.runConfigurations.JpsRunConfigurationSerializer;
import org.jetbrains.jps.service.SharedThreadPool;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
/**
* @author nik
*/
public class JpsProjectLoader extends JpsLoaderBase {
private static final Logger LOG = Logger.getInstance(JpsProjectLoader.class);
private static final BoundedTaskExecutor ourThreadPool = new BoundedTaskExecutor(SharedThreadPool.getInstance(), Runtime.getRuntime().availableProcessors());
public static final String CLASSPATH_ATTRIBUTE = "classpath";
public static final String CLASSPATH_DIR_ATTRIBUTE = "classpath-dir";
private final JpsProject myProject;
private final Map<String, String> myPathVariables;
private JpsProjectLoader(JpsProject project, Map<String, String> pathVariables, File baseDir) {
super(createProjectMacroExpander(pathVariables, baseDir));
myProject = project;
myPathVariables = pathVariables;
myProject.getContainer().setChild(JpsProjectSerializationDataExtensionImpl.ROLE, new JpsProjectSerializationDataExtensionImpl(baseDir));
}
static JpsMacroExpander createProjectMacroExpander(Map<String, String> pathVariables, File baseDir) {
final JpsMacroExpander expander = new JpsMacroExpander(pathVariables);
expander.addFileHierarchyReplacements(PathMacroUtil.PROJECT_DIR_MACRO_NAME, baseDir);
return expander;
}
public static void loadProject(final JpsProject project, Map<String, String> pathVariables, String projectPath) throws IOException {
File file = new File(FileUtil.toCanonicalPath(projectPath));
if (file.isFile() && projectPath.endsWith(".ipr")) {
new JpsProjectLoader(project, pathVariables, file.getParentFile()).loadFromIpr(file);
}
else {
File dotIdea = new File(file, PathMacroUtil.DIRECTORY_STORE_NAME);
File directory;
if (dotIdea.isDirectory()) {
directory = dotIdea;
}
else if (file.isDirectory() && file.getName().equals(PathMacroUtil.DIRECTORY_STORE_NAME)) {
directory = file;
}
else {
throw new IOException("Cannot find IntelliJ IDEA project files at " + projectPath);
}
new JpsProjectLoader(project, pathVariables, directory.getParentFile()).loadFromDirectory(directory);
}
}
public static String getDirectoryBaseProjectName(File dir) {
File nameFile = new File(dir, ".name");
if (nameFile.isFile()) {
try {
return FileUtilRt.loadFile(nameFile).trim();
}
catch (IOException ignored) {
}
}
return dir.getParentFile().getName();
}
private void loadFromDirectory(File dir) {
myProject.setName(getDirectoryBaseProjectName(dir));
JpsSdkType<?> projectSdkType = loadProjectRoot(loadRootElement(new File(dir, "misc.xml")));
for (JpsModelSerializerExtension extension : JpsModelSerializerExtension.getExtensions()) {
for (JpsProjectExtensionSerializer serializer : extension.getProjectExtensionSerializers()) {
loadComponents(dir, "misc.xml", serializer, myProject);
}
}
loadModules(loadRootElement(new File(dir, "modules.xml")), projectSdkType);
Runnable timingLog = TimingLog.startActivity("loading project libraries");
for (File libraryFile : listXmlFiles(new File(dir, "libraries"))) {
loadProjectLibraries(loadRootElement(libraryFile));
}
timingLog.run();
Runnable artifactsTimingLog = TimingLog.startActivity("loading artifacts");
for (File artifactFile : listXmlFiles(new File(dir, "artifacts"))) {
loadArtifacts(loadRootElement(artifactFile));
}
artifactsTimingLog.run();
if (hasRunConfigurationSerializers()) {
Runnable runConfTimingLog = TimingLog.startActivity("loading run configurations");
for (File configurationFile : listXmlFiles(new File(dir, "runConfigurations"))) {
JpsRunConfigurationSerializer.loadRunConfigurations(myProject, loadRootElement(configurationFile));
}
File workspaceFile = new File(dir, "workspace.xml");
if (workspaceFile.exists()) {
Element runManager = JDomSerializationUtil.findComponent(loadRootElement(workspaceFile), "RunManager");
JpsRunConfigurationSerializer.loadRunConfigurations(myProject, runManager);
}
runConfTimingLog.run();
}
}
private static boolean hasRunConfigurationSerializers() {
for (JpsModelSerializerExtension extension : JpsModelSerializerExtension.getExtensions()) {
if (!extension.getRunConfigurationPropertiesSerializers().isEmpty()) {
return true;
}
}
return false;
}
@NotNull
private static File[] listXmlFiles(final File dir) {
File[] files = dir.listFiles(new FileFilter() {
@Override
public boolean accept(File file) {
return isXmlFile(file);
}
});
return files != null ? files : ArrayUtil.EMPTY_FILE_ARRAY;
}
private void loadFromIpr(File iprFile) {
final Element iprRoot = loadRootElement(iprFile);
String projectName = FileUtil.getNameWithoutExtension(iprFile);
myProject.setName(projectName);
File iwsFile = new File(iprFile.getParent(), projectName + ".iws");
Element iwsRoot = iwsFile.exists() ? loadRootElement(iwsFile) : null;
JpsSdkType<?> projectSdkType = loadProjectRoot(iprRoot);
for (JpsModelSerializerExtension extension : JpsModelSerializerExtension.getExtensions()) {
for (JpsProjectExtensionSerializer serializer : extension.getProjectExtensionSerializers()) {
Element rootTag = JpsProjectExtensionSerializer.WORKSPACE_FILE.equals(serializer.getConfigFileName()) ? iwsRoot : iprRoot;
Element component = JDomSerializationUtil.findComponent(rootTag, serializer.getComponentName());
if (component != null) {
serializer.loadExtension(myProject, component);
}
else {
serializer.loadExtensionWithDefaultSettings(myProject);
}
}
}
loadModules(iprRoot, projectSdkType);
loadProjectLibraries(JDomSerializationUtil.findComponent(iprRoot, "libraryTable"));
loadArtifacts(JDomSerializationUtil.findComponent(iprRoot, "ArtifactManager"));
if (hasRunConfigurationSerializers()) {
JpsRunConfigurationSerializer.loadRunConfigurations(myProject, JDomSerializationUtil.findComponent(iprRoot, "ProjectRunConfigurationManager"));
JpsRunConfigurationSerializer.loadRunConfigurations(myProject, JDomSerializationUtil.findComponent(iwsRoot, "RunManager"));
}
}
private void loadArtifacts(@Nullable Element artifactManagerComponent) {
JpsArtifactSerializer.loadArtifacts(myProject, artifactManagerComponent);
}
@Nullable
private JpsSdkType<?> loadProjectRoot(Element root) {
JpsSdkType<?> sdkType = null;
Element rootManagerElement = JDomSerializationUtil.findComponent(root, "ProjectRootManager");
if (rootManagerElement != null) {
String sdkName = rootManagerElement.getAttributeValue("project-jdk-name");
String sdkTypeId = rootManagerElement.getAttributeValue("project-jdk-type");
if (sdkName != null) {
sdkType = JpsSdkTableSerializer.getSdkType(sdkTypeId);
JpsSdkTableSerializer.setSdkReference(myProject.getSdkReferencesTable(), sdkName, sdkType);
}
}
return sdkType;
}
private void loadProjectLibraries(@Nullable Element libraryTableElement) {
JpsLibraryTableSerializer.loadLibraries(libraryTableElement, myProject.getLibraryCollection());
}
private void loadModules(Element root, final @Nullable JpsSdkType<?> projectSdkType) {
Runnable timingLog = TimingLog.startActivity("loading modules");
Element componentRoot = JDomSerializationUtil.findComponent(root, "ProjectModuleManager");
if (componentRoot == null) return;
final Element modules = componentRoot.getChild("modules");
List<Future<JpsModule>> futures = new ArrayList<Future<JpsModule>>();
List<Future<Pair<File, Element>>> futureModuleFiles = new ArrayList<Future<Pair<File, Element>>>();
for (Element moduleElement : JDOMUtil.getChildren(modules, "module")) {
final String path = moduleElement.getAttributeValue("filepath");
final File file = new File(path);
if (!file.exists()) {
LOG.info("Module '" + FileUtil.getNameWithoutExtension(file) + "' is skipped: " + file.getAbsolutePath() + " doesn't exist");
continue;
}
futureModuleFiles.add(ourThreadPool.submit(new Callable<Pair<File, Element>>() {
@Override
public Pair<File, Element> call() throws Exception {
final JpsMacroExpander expander = createModuleMacroExpander(myPathVariables, file);
final Element moduleRoot = loadRootElement(file, expander);
return Pair.create(file, moduleRoot);
}
}));
}
try {
final List<String> classpathDirs = new ArrayList<String>();
for (Future<Pair<File, Element>> moduleFile : futureModuleFiles) {
final String classpathDir = moduleFile.get().getSecond().getAttributeValue(CLASSPATH_DIR_ATTRIBUTE);
if (classpathDir != null) {
classpathDirs.add(classpathDir);
}
}
for (final Future<Pair<File, Element>> futureModuleFile : futureModuleFiles) {
final Pair<File, Element> moduleFile = futureModuleFile.get();
futures.add(ourThreadPool.submit(new Callable<JpsModule>() {
@Override
public JpsModule call() throws Exception {
return loadModule(moduleFile.getFirst(), moduleFile.getSecond(), classpathDirs, projectSdkType);
}
}));
}
for (Future<JpsModule> future : futures) {
JpsModule module = future.get();
if (module != null) {
myProject.addModule(module);
}
}
}
catch (Exception e) {
throw new RuntimeException(e);
}
timingLog.run();
}
@Nullable
private JpsModule loadModule(@NotNull File file, @NotNull Element moduleRoot, List<String> paths, @Nullable JpsSdkType<?> projectSdkType) {
String name = FileUtil.getNameWithoutExtension(file);
final String typeId = moduleRoot.getAttributeValue("type");
final JpsModulePropertiesSerializer<?> serializer = getModulePropertiesSerializer(typeId);
final JpsModule module = createModule(name, moduleRoot, serializer);
module.getContainer().setChild(JpsModuleSerializationDataExtensionImpl.ROLE,
new JpsModuleSerializationDataExtensionImpl(file.getParentFile()));
for (JpsModelSerializerExtension extension : JpsModelSerializerExtension.getExtensions()) {
extension.loadModuleOptions(module, moduleRoot);
}
String baseModulePath = FileUtil.toSystemIndependentName(file.getParent());
String classpath = moduleRoot.getAttributeValue(CLASSPATH_ATTRIBUTE);
if (classpath == null) {
JpsModuleRootModelSerializer.loadRootModel(module, JDomSerializationUtil.findComponent(moduleRoot, "NewModuleRootManager"),
projectSdkType);
}
else {
for (JpsModelSerializerExtension extension : JpsModelSerializerExtension.getExtensions()) {
JpsModuleClasspathSerializer classpathSerializer = extension.getClasspathSerializer();
if (classpathSerializer != null && classpathSerializer.getClasspathId().equals(classpath)) {
String classpathDir = moduleRoot.getAttributeValue(CLASSPATH_DIR_ATTRIBUTE);
final JpsMacroExpander expander = createModuleMacroExpander(myPathVariables, file);
classpathSerializer.loadClasspath(module, classpathDir, baseModulePath, expander, paths, projectSdkType);
}
}
}
JpsFacetSerializer.loadFacets(module, JDomSerializationUtil.findComponent(moduleRoot, "FacetManager"));
return module;
}
static JpsMacroExpander createModuleMacroExpander(final Map<String, String> pathVariables, File moduleFile) {
final JpsMacroExpander expander = new JpsMacroExpander(pathVariables);
String moduleDirPath = PathMacroUtil.getModuleDir(moduleFile.getAbsolutePath());
if (moduleDirPath != null) {
expander.addFileHierarchyReplacements(PathMacroUtil.MODULE_DIR_MACRO_NAME, new File(FileUtil.toSystemDependentName(moduleDirPath)));
}
return expander;
}
private static <P extends JpsElement> JpsModule createModule(String name, Element moduleRoot, JpsModulePropertiesSerializer<P> loader) {
String componentName = loader.getComponentName();
Element component = componentName != null ? JDomSerializationUtil.findComponent(moduleRoot, componentName) : null;
return JpsElementFactory.getInstance().createModule(name, loader.getType(), loader.loadProperties(component));
}
private static JpsModulePropertiesSerializer<?> getModulePropertiesSerializer(@Nullable String typeId) {
for (JpsModelSerializerExtension extension : JpsModelSerializerExtension.getExtensions()) {
for (JpsModulePropertiesSerializer<?> loader : extension.getModulePropertiesSerializers()) {
if (loader.getTypeId().equals(typeId)) {
return loader;
}
}
}
return new JpsModulePropertiesSerializer<JpsDummyElement>(JpsJavaModuleType.INSTANCE, "JAVA_MODULE", null) {
@Override
public JpsDummyElement loadProperties(@Nullable Element componentElement) {
return JpsElementFactory.getInstance().createDummyElement();
}
@Override
public void saveProperties(@NotNull JpsDummyElement properties, @NotNull Element componentElement) {
}
};
}
}