blob: ac858e56f63412ddc34d7a940c5bacfb7588aeec [file] [log] [blame]
/*
* Copyright 2000-2013 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.plugins.gradle.service.project;
import com.google.gson.GsonBuilder;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.configurations.SimpleJavaParameters;
import com.intellij.externalSystem.JavaProjectData;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.externalSystem.model.*;
import com.intellij.openapi.externalSystem.model.project.*;
import com.intellij.openapi.externalSystem.model.task.TaskData;
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
import com.intellij.openapi.externalSystem.util.ExternalSystemDebugEnvironment;
import com.intellij.openapi.externalSystem.util.Order;
import com.intellij.openapi.module.EmptyModuleType;
import com.intellij.openapi.module.JavaModuleType;
import com.intellij.openapi.module.ModuleType;
import com.intellij.openapi.module.StdModuleTypes;
import com.intellij.openapi.roots.DependencyScope;
import com.intellij.openapi.util.KeyValue;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.util.*;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.ContainerUtilRt;
import com.intellij.util.net.HttpConfigurable;
import com.intellij.util.text.CharArrayUtil;
import groovy.lang.GroovyObject;
import org.gradle.tooling.ProjectConnection;
import org.gradle.tooling.model.DomainObjectSet;
import org.gradle.tooling.model.GradleModuleVersion;
import org.gradle.tooling.model.GradleTask;
import org.gradle.tooling.model.gradle.BasicGradleProject;
import org.gradle.tooling.model.gradle.GradleBuild;
import org.gradle.tooling.model.idea.*;
import org.gradle.util.GradleVersion;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.gradle.model.*;
import org.jetbrains.plugins.gradle.model.data.BuildScriptClasspathData;
import org.jetbrains.plugins.gradle.service.project.data.ExternalProjectDataService;
import org.jetbrains.plugins.gradle.tooling.builder.ModelBuildScriptClasspathBuilderImpl;
import org.jetbrains.plugins.gradle.tooling.internal.init.Init;
import org.jetbrains.plugins.gradle.util.GradleBundle;
import org.jetbrains.plugins.gradle.util.GradleConstants;
import org.jetbrains.plugins.gradle.util.GradleUtil;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.net.URL;
import java.util.*;
/**
* {@link BaseGradleProjectResolverExtension} provides base implementation of Gradle project resolver.
*
* @author Vladislav.Soroka
* @since 10/14/13
*/
@Order(Integer.MAX_VALUE)
public class BaseGradleProjectResolverExtension implements GradleProjectResolverExtension {
private static final Logger LOG = Logger.getInstance("#" + BaseGradleProjectResolverExtension.class.getName());
@NotNull @NonNls private static final String UNRESOLVED_DEPENDENCY_PREFIX = "unresolved dependency - ";
private static final String MAIN_SOURCE_SET = "main";
private static final String TEST_SOURCE_SET = "test";
@NotNull private ProjectResolverContext resolverCtx;
@NotNull private final BaseProjectImportErrorHandler myErrorHandler = new BaseProjectImportErrorHandler();
@Override
public void setProjectResolverContext(@NotNull ProjectResolverContext projectResolverContext) {
resolverCtx = projectResolverContext;
}
@Override
public void setNext(@NotNull GradleProjectResolverExtension next) {
// should be the last extension in the chain
}
@Nullable
@Override
public GradleProjectResolverExtension getNext() {
return null;
}
@NotNull
@Override
public ProjectData createProject() {
final String projectDirPath = resolverCtx.getProjectPath();
final IdeaProject ideaProject = resolverCtx.getModels().getIdeaProject();
return new ProjectData(GradleConstants.SYSTEM_ID, ideaProject.getName(), projectDirPath, projectDirPath);
}
@NotNull
@Override
public JavaProjectData createJavaProjectData() {
final String projectDirPath = resolverCtx.getProjectPath();
final IdeaProject ideaProject = resolverCtx.getModels().getIdeaProject();
// Gradle API doesn't expose gradleProject compile output path yet.
JavaProjectData javaProjectData = new JavaProjectData(GradleConstants.SYSTEM_ID, projectDirPath + "/build/classes");
javaProjectData.setJdkVersion(ideaProject.getJdkName());
javaProjectData.setLanguageLevel(ideaProject.getLanguageLevel().getLevel());
return javaProjectData;
}
@Override
public void populateProjectExtraModels(@NotNull IdeaProject gradleProject, @NotNull DataNode<ProjectData> ideProject) {
final ExternalProject externalProject = resolverCtx.getExtraProject(ExternalProject.class);
if (externalProject != null) {
ideProject.createChild(ExternalProjectDataService.KEY, externalProject);
}
}
@NotNull
@Override
public ModuleData createModule(@NotNull IdeaModule gradleModule, @NotNull ProjectData projectData) {
final String moduleName = gradleModule.getName();
if (moduleName == null) {
throw new IllegalStateException("Module with undefined name detected: " + gradleModule);
}
final String moduleConfigPath = getModuleConfigPath(gradleModule, projectData.getLinkedExternalProjectPath());
if (ExternalSystemDebugEnvironment.DEBUG_ORPHAN_MODULES_PROCESSING) {
LOG.info(String.format(
"Creating module data ('%s') with the external config path: '%s'", gradleModule.getGradleProject().getPath(), moduleConfigPath
));
}
String gradlePath = gradleModule.getGradleProject().getPath();
String moduleId = StringUtil.isEmpty(gradlePath) || ":".equals(gradlePath) ? moduleName : gradlePath;
ModuleData moduleData =
new ModuleData(moduleId, GradleConstants.SYSTEM_ID, StdModuleTypes.JAVA.getId(), moduleName, moduleConfigPath, moduleConfigPath);
final ModuleExtendedModel moduleExtendedModel = resolverCtx.getExtraProject(gradleModule, ModuleExtendedModel.class);
if (moduleExtendedModel != null) {
moduleData.setGroup(moduleExtendedModel.getGroup());
moduleData.setVersion(moduleExtendedModel.getVersion());
moduleData.setArtifacts(moduleExtendedModel.getArtifacts());
}
return moduleData;
}
@Override
public void populateModuleExtraModels(@NotNull IdeaModule gradleModule, @NotNull DataNode<ModuleData> ideModule) {
final BuildScriptClasspathModel buildScriptClasspathModel = resolverCtx.getExtraProject(gradleModule, BuildScriptClasspathModel.class);
final List<BuildScriptClasspathData.ClasspathEntry> classpathEntries;
if (buildScriptClasspathModel != null) {
classpathEntries = ContainerUtil
.map(buildScriptClasspathModel.getClasspath(), new Function<ClasspathEntryModel, BuildScriptClasspathData.ClasspathEntry>() {
@Override
public BuildScriptClasspathData.ClasspathEntry fun(ClasspathEntryModel model) {
return new BuildScriptClasspathData.ClasspathEntry(model.getClasses(), model.getSources(), model.getJavadoc());
}
});
}
else {
classpathEntries = ContainerUtil.emptyList();
}
BuildScriptClasspathData buildScriptClasspathData = new BuildScriptClasspathData(GradleConstants.SYSTEM_ID, classpathEntries);
ideModule.createChild(BuildScriptClasspathData.KEY, buildScriptClasspathData);
}
@Override
public void populateModuleContentRoots(@NotNull IdeaModule gradleModule,
@NotNull DataNode<ModuleData> ideModule) {
DomainObjectSet<? extends IdeaContentRoot> contentRoots;
ModuleExtendedModel moduleExtendedModel = resolverCtx.getExtraProject(gradleModule, ModuleExtendedModel.class);
if (moduleExtendedModel != null) {
contentRoots = moduleExtendedModel.getContentRoots();
}
else {
contentRoots = gradleModule.getContentRoots();
}
if (contentRoots == null) {
return;
}
for (IdeaContentRoot gradleContentRoot : contentRoots) {
if (gradleContentRoot == null) continue;
File rootDirectory = gradleContentRoot.getRootDirectory();
if (rootDirectory == null) continue;
ContentRootData ideContentRoot = new ContentRootData(GradleConstants.SYSTEM_ID, rootDirectory.getAbsolutePath());
ideModule.getData().setModuleFileDirectoryPath(ideContentRoot.getRootPath());
populateContentRoot(ideContentRoot, ExternalSystemSourceType.SOURCE, gradleContentRoot.getSourceDirectories());
populateContentRoot(ideContentRoot, ExternalSystemSourceType.TEST, gradleContentRoot.getTestDirectories());
if (gradleContentRoot instanceof ExtIdeaContentRoot) {
ExtIdeaContentRoot extIdeaContentRoot = (ExtIdeaContentRoot)gradleContentRoot;
populateContentRoot(ideContentRoot, ExternalSystemSourceType.RESOURCE, extIdeaContentRoot.getResourceDirectories());
populateContentRoot(ideContentRoot, ExternalSystemSourceType.TEST_RESOURCE, extIdeaContentRoot.getTestResourceDirectories());
}
Set<File> excluded = gradleContentRoot.getExcludeDirectories();
if (excluded != null) {
for (File file : excluded) {
ideContentRoot.storePath(ExternalSystemSourceType.EXCLUDED, file.getAbsolutePath());
}
}
ideModule.createChild(ProjectKeys.CONTENT_ROOT, ideContentRoot);
}
}
@Override
public void populateModuleCompileOutputSettings(@NotNull IdeaModule gradleModule,
@NotNull DataNode<ModuleData> ideModule) {
IdeaCompilerOutput moduleCompilerOutput = gradleModule.getCompilerOutput();
File sourceCompileOutputPath = null;
File testCompileOutputPath = null;
File resourceCompileOutputPath = null;
File testResourceCompileOutputPath = null;
boolean inheritOutputDirs = false;
ModuleData moduleData = ideModule.getData();
if (moduleCompilerOutput != null) {
sourceCompileOutputPath = moduleCompilerOutput.getOutputDir();
testCompileOutputPath = moduleCompilerOutput.getTestOutputDir();
inheritOutputDirs = moduleCompilerOutput.getInheritOutputDirs();
}
ExternalProject externalProject = resolverCtx.getExtraProject(gradleModule, ExternalProject.class);
if (externalProject != null) {
externalProject = new DefaultExternalProject(externalProject);
if (!inheritOutputDirs && (sourceCompileOutputPath == null || testCompileOutputPath == null)) {
sourceCompileOutputPath = getCompileOutputPath(externalProject, MAIN_SOURCE_SET, ExternalSystemSourceType.SOURCE);
resourceCompileOutputPath = getCompileOutputPath(externalProject, MAIN_SOURCE_SET, ExternalSystemSourceType.RESOURCE);
testCompileOutputPath = getCompileOutputPath(externalProject, TEST_SOURCE_SET, ExternalSystemSourceType.TEST);
testResourceCompileOutputPath = getCompileOutputPath(externalProject, TEST_SOURCE_SET, ExternalSystemSourceType.TEST_RESOURCE);
}
else if (!inheritOutputDirs) {
resourceCompileOutputPath = sourceCompileOutputPath;
testResourceCompileOutputPath = testCompileOutputPath;
final ExternalSourceSet mainSourceSet = externalProject.getSourceSets().get(MAIN_SOURCE_SET);
if (mainSourceSet != null) {
final ExternalSourceDirectorySet sourceDirectories = mainSourceSet.getSources().get(ExternalSystemSourceType.SOURCE);
if (sourceDirectories instanceof DefaultExternalSourceDirectorySet) {
((DefaultExternalSourceDirectorySet)sourceDirectories).setOutputDir(sourceCompileOutputPath);
}
final ExternalSourceDirectorySet resourceDirectories = mainSourceSet.getSources().get(ExternalSystemSourceType.RESOURCE);
if (resourceDirectories instanceof DefaultExternalSourceDirectorySet) {
((DefaultExternalSourceDirectorySet)resourceDirectories).setOutputDir(sourceCompileOutputPath);
}
}
final ExternalSourceSet testSourceSet = externalProject.getSourceSets().get(TEST_SOURCE_SET);
if (testSourceSet != null) {
final ExternalSourceDirectorySet testDirectories = testSourceSet.getSources().get(ExternalSystemSourceType.TEST);
if (testDirectories instanceof DefaultExternalSourceDirectorySet) {
((DefaultExternalSourceDirectorySet)testDirectories).setOutputDir(testCompileOutputPath);
}
final ExternalSourceDirectorySet testResourceDirectories = testSourceSet.getSources().get(ExternalSystemSourceType.TEST_RESOURCE);
if (testResourceDirectories instanceof DefaultExternalSourceDirectorySet) {
((DefaultExternalSourceDirectorySet)testResourceDirectories).setOutputDir(testCompileOutputPath);
}
}
final DataNode<ProjectData> projectDataNode = ExternalSystemApiUtil.findParent(ideModule, ProjectKeys.PROJECT);
assert projectDataNode != null;
projectDataNode.createOrReplaceChild(ExternalProjectDataService.KEY, externalProject);
}
}
else {
LOG.warn(String.format("Unable to get ExternalProject model for '%s'", gradleModule.getName()));
}
if (sourceCompileOutputPath != null) {
moduleData.setCompileOutputPath(ExternalSystemSourceType.SOURCE, sourceCompileOutputPath.getAbsolutePath());
}
if (resourceCompileOutputPath != null) {
moduleData.setCompileOutputPath(ExternalSystemSourceType.RESOURCE, resourceCompileOutputPath.getAbsolutePath());
}
if (testCompileOutputPath != null) {
moduleData.setCompileOutputPath(ExternalSystemSourceType.TEST, testCompileOutputPath.getAbsolutePath());
}
if (testResourceCompileOutputPath != null) {
moduleData.setCompileOutputPath(ExternalSystemSourceType.TEST_RESOURCE, testResourceCompileOutputPath.getAbsolutePath());
}
moduleData.setInheritProjectCompileOutputPath(inheritOutputDirs || sourceCompileOutputPath == null);
}
@Nullable
private static File getCompileOutputPath(@Nullable ExternalProject externalProject,
@NotNull String sourceSetName,
@NotNull ExternalSystemSourceType sourceType) {
if (externalProject == null) return null;
final ExternalSourceSet sourceSet = externalProject.getSourceSets().get(sourceSetName);
if(sourceSet == null) return null;
final ExternalSourceDirectorySet directorySet = sourceSet.getSources().get(sourceType);
return directorySet != null ? directorySet.getOutputDir() : null;
}
@Override
public void populateModuleDependencies(@NotNull IdeaModule gradleModule,
@NotNull DataNode<ModuleData> ideModule,
@NotNull DataNode<ProjectData> ideProject) {
final List<? extends IdeaDependency> dependencies = gradleModule.getDependencies().getAll();
if (dependencies == null) return;
for (IdeaDependency dependency : dependencies) {
if (dependency == null) {
continue;
}
DependencyScope scope = parseScope(dependency.getScope());
if (dependency instanceof IdeaModuleDependency) {
ModuleDependencyData d = buildDependency(ideModule, (IdeaModuleDependency)dependency, ideProject);
d.setExported(dependency.getExported());
if (scope != null) {
d.setScope(scope);
}
ideModule.createChild(ProjectKeys.MODULE_DEPENDENCY, d);
}
else if (dependency instanceof IdeaSingleEntryLibraryDependency) {
LibraryDependencyData d = buildDependency(gradleModule, ideModule, (IdeaSingleEntryLibraryDependency)dependency, ideProject);
d.setExported(dependency.getExported());
if (scope != null) {
d.setScope(scope);
}
ideModule.createChild(ProjectKeys.LIBRARY_DEPENDENCY, d);
}
}
}
@NotNull
@Override
public Collection<TaskData> populateModuleTasks(@NotNull IdeaModule gradleModule,
@NotNull DataNode<ModuleData> ideModule,
@NotNull DataNode<ProjectData> ideProject)
throws IllegalArgumentException, IllegalStateException {
final Collection<TaskData> tasks = ContainerUtil.newArrayList();
final String moduleConfigPath = ideModule.getData().getLinkedExternalProjectPath();
for (GradleTask task : gradleModule.getGradleProject().getTasks()) {
String taskName = task.getName();
if (taskName == null || taskName.trim().isEmpty() || isIdeaTask(taskName)) {
continue;
}
TaskData taskData = new TaskData(GradleConstants.SYSTEM_ID, taskName, moduleConfigPath, task.getDescription());
ideModule.createChild(ProjectKeys.TASK, taskData);
tasks.add(taskData);
}
return tasks;
}
@NotNull
@Override
public Collection<TaskData> filterRootProjectTasks(@NotNull List<TaskData> allTasks) {
return allTasks;
}
@NotNull
@Override
public Set<Class> getExtraProjectModelClasses() {
return ContainerUtil.<Class>set(
GradleBuild.class, ExternalProject.class, ModuleExtendedModel.class, BuildScriptClasspathModel.class);
}
@NotNull
@Override
public Set<Class> getToolingExtensionsClasses() {
return ContainerUtil.<Class>set(
ExternalProject.class,
// gradle-tooling-extension-api jar
ProjectImportAction.class,
// gradle-tooling-extension-impl jar
ModelBuildScriptClasspathBuilderImpl.class,
GsonBuilder.class
);
}
@NotNull
@Override
public List<KeyValue<String, String>> getExtraJvmArgs() {
if (ExternalSystemApiUtil.isInProcessMode(GradleConstants.SYSTEM_ID)) {
final List<KeyValue<String, String>> extraJvmArgs = ContainerUtil.newArrayList();
final HttpConfigurable httpConfigurable = HttpConfigurable.getInstance();
if (!StringUtil.isEmpty(httpConfigurable.PROXY_EXCEPTIONS)) {
List<String> hosts = StringUtil.split(httpConfigurable.PROXY_EXCEPTIONS, ",");
if (!hosts.isEmpty()) {
extraJvmArgs.add(KeyValue.create("http.nonProxyHosts", StringUtil.join(hosts, StringUtil.TRIMMER, "|")));
}
}
extraJvmArgs.addAll(HttpConfigurable.getJvmPropertiesList(false, null));
return extraJvmArgs;
}
return Collections.emptyList();
}
@NotNull
@Override
public List<String> getExtraCommandLineArgs() {
return Collections.emptyList();
}
@NotNull
@Override
public ExternalSystemException getUserFriendlyError(@NotNull Throwable error,
@NotNull String projectPath,
@Nullable String buildFilePath) {
return myErrorHandler.getUserFriendlyError(error, projectPath, buildFilePath);
}
@Override
public void preImportCheck() {
}
@Override
public void enhanceTaskProcessing(@NotNull List<String> taskNames,
@Nullable String debuggerSetup,
@NotNull Consumer<String> initScriptConsumer) {
if (!StringUtil.isEmpty(debuggerSetup)) {
final String[] lines = {
"gradle.taskGraph.beforeTask { Task task ->",
" if (task instanceof JavaForkOptions) {",
" task.jvmArgs '" + debuggerSetup.trim() + '\'',
" }" +
"}",
};
final String script = StringUtil.join(lines, SystemProperties.getLineSeparator());
initScriptConsumer.consume(script);
}
}
@Override
public void enhanceRemoteProcessing(@NotNull SimpleJavaParameters parameters) throws ExecutionException {
PathsList classPath = parameters.getClassPath();
// Gradle i18n bundle.
ExternalSystemApiUtil.addBundle(classPath, GradleBundle.PATH_TO_BUNDLE, GradleBundle.class);
// Gradle tool jars.
String toolingApiPath = PathManager.getJarPathForClass(ProjectConnection.class);
if (toolingApiPath == null) {
LOG.warn(GradleBundle.message("gradle.generic.text.error.jar.not.found"));
throw new ExecutionException("Can't find gradle libraries");
}
File gradleJarsDir = new File(toolingApiPath).getParentFile();
String[] gradleJars = gradleJarsDir.list(new FilenameFilter() {
@Override
public boolean accept(@NotNull File dir, @NotNull String name) {
return name.endsWith(".jar");
}
});
if (gradleJars == null) {
LOG.warn(GradleBundle.message("gradle.generic.text.error.jar.not.found"));
throw new ExecutionException("Can't find gradle libraries at " + gradleJarsDir.getAbsolutePath());
}
for (String jar : gradleJars) {
classPath.add(new File(gradleJarsDir, jar).getAbsolutePath());
}
List<String> additionalEntries = ContainerUtilRt.newArrayList();
ContainerUtilRt.addIfNotNull(additionalEntries, PathUtil.getJarPathForClass(GroovyObject.class));
ContainerUtilRt.addIfNotNull(additionalEntries, PathUtil.getJarPathForClass(GsonBuilder.class));
ContainerUtilRt.addIfNotNull(additionalEntries, PathUtil.getJarPathForClass(ExternalProject.class));
ContainerUtilRt.addIfNotNull(additionalEntries, PathUtil.getJarPathForClass(JavaProjectData.class));
ContainerUtilRt.addIfNotNull(additionalEntries, PathUtil.getJarPathForClass(LanguageLevel.class));
ContainerUtilRt.addIfNotNull(additionalEntries, PathUtil.getJarPathForClass(StdModuleTypes.class));
ContainerUtilRt.addIfNotNull(additionalEntries, PathUtil.getJarPathForClass(JavaModuleType.class));
ContainerUtilRt.addIfNotNull(additionalEntries, PathUtil.getJarPathForClass(ModuleType.class));
ContainerUtilRt.addIfNotNull(additionalEntries, PathUtil.getJarPathForClass(EmptyModuleType.class));
ContainerUtilRt.addIfNotNull(additionalEntries, PathUtil.getJarPathForClass(ProjectImportAction.class));
ContainerUtilRt.addIfNotNull(additionalEntries, PathUtil.getJarPathForClass(Init.class));
for (String entry : additionalEntries) {
classPath.add(entry);
}
}
@Override
public void enhanceLocalProcessing(@NotNull List<URL> urls) {
}
@NotNull
private String getModuleConfigPath(@NotNull IdeaModule gradleModule, @NotNull String rootProjectPath) {
GradleBuild build = resolverCtx.getExtraProject(gradleModule, GradleBuild.class);
if (build != null) {
String gradlePath = gradleModule.getGradleProject().getPath();
File moduleDirPath = getModuleDirPath(build, gradlePath);
if (moduleDirPath == null) {
throw new IllegalStateException(String.format("Unable to find root directory for module '%s'", gradleModule.getName()));
}
try {
return ExternalSystemApiUtil.toCanonicalPath(moduleDirPath.getCanonicalPath());
}
catch (IOException e) {
LOG.warn("construction of the canonical path for the module fails", e);
}
}
return GradleUtil.getConfigPath(gradleModule.getGradleProject(), rootProjectPath);
}
/**
* Returns the physical path of the module's root directory (the path in the file system.)
* <p>
* It is important to note that Gradle has its own "logical" path that may or may not be equal to the physical path of a Gradle project.
* For example, the sub-project at ${projectRootDir}/apps/app will have the Gradle path :apps:app. Gradle also allows mapping physical
* paths to a different logical path. For example, in settings.gradle:
* <pre>
* include ':app'
* project(':app').projectDir = new File(rootDir, 'apps/app')
* </pre>
* In this example, sub-project at ${projectRootDir}/apps/app will have the Gradle path :app.
* </p>
*
* @param build contains information about the root Gradle project and its sub-projects. Such information includes the physical path of
* the root Gradle project and its sub-projects.
* @param path the Gradle "logical" path. This path uses colon as separator, and may or may not be equal to the physical path of a
* Gradle project.
* @return the physical path of the module's root directory.
*/
@Nullable
static File getModuleDirPath(@NotNull GradleBuild build, @NotNull String path) {
for (BasicGradleProject project : build.getProjects()) {
if (project.getPath().equals(path)) {
return project.getProjectDirectory();
}
}
return null;
}
/**
* Stores information about given directories at the given content root
*
* @param contentRoot target paths info holder
* @param type type of data located at the given directories
* @param dirs directories which paths should be stored at the given content root
* @throws IllegalArgumentException if specified by {@link ContentRootData#storePath(ExternalSystemSourceType, String)}
*/
private static void populateContentRoot(@NotNull ContentRootData contentRoot,
@NotNull ExternalSystemSourceType type,
@Nullable Iterable<? extends IdeaSourceDirectory> dirs)
throws IllegalArgumentException {
if (dirs == null) {
return;
}
for (IdeaSourceDirectory dir : dirs) {
contentRoot.storePath(type, dir.getDirectory().getAbsolutePath());
}
}
@Nullable
private static DependencyScope parseScope(@Nullable IdeaDependencyScope scope) {
if (scope == null) {
return null;
}
String scopeAsString = scope.getScope();
if (scopeAsString == null) {
return null;
}
for (DependencyScope dependencyScope : DependencyScope.values()) {
if (scopeAsString.equalsIgnoreCase(dependencyScope.toString())) {
return dependencyScope;
}
}
return null;
}
@NotNull
private static ModuleDependencyData buildDependency(@NotNull DataNode<ModuleData> ownerModule,
@NotNull IdeaModuleDependency dependency,
@NotNull DataNode<ProjectData> ideProject)
throws IllegalStateException {
IdeaModule module = dependency.getDependencyModule();
if (module == null) {
throw new IllegalStateException(
String.format("Can't parse gradle module dependency '%s'. Reason: referenced module is null", dependency)
);
}
String moduleName = module.getName();
if (moduleName == null) {
throw new IllegalStateException(String.format(
"Can't parse gradle module dependency '%s'. Reason: referenced module name is undefined (module: '%s') ", dependency, module
));
}
Set<String> registeredModuleNames = ContainerUtilRt.newHashSet();
Collection<DataNode<ModuleData>> modulesDataNode = ExternalSystemApiUtil.getChildren(ideProject, ProjectKeys.MODULE);
for (DataNode<ModuleData> moduleDataNode : modulesDataNode) {
String name = moduleDataNode.getData().getExternalName();
registeredModuleNames.add(name);
if (name.equals(moduleName)) {
return new ModuleDependencyData(ownerModule.getData(), moduleDataNode.getData());
}
}
throw new IllegalStateException(String.format(
"Can't parse gradle module dependency '%s'. Reason: no module with such name (%s) is found. Registered modules: %s",
dependency, moduleName, registeredModuleNames
));
}
@NotNull
private LibraryDependencyData buildDependency(@NotNull IdeaModule gradleModule,
@NotNull DataNode<ModuleData> ownerModule,
@NotNull IdeaSingleEntryLibraryDependency dependency,
@NotNull DataNode<ProjectData> ideProject)
throws IllegalStateException {
File binaryPath = dependency.getFile();
if (binaryPath == null) {
throw new IllegalStateException(String.format(
"Can't parse external library dependency '%s'. Reason: it doesn't specify path to the binaries", dependency
));
}
String libraryName;
final GradleModuleVersion moduleVersion = dependency.getGradleModuleVersion();
final LibraryLevel level;
// Gradle API doesn't explicitly provide information about unresolved libraries (http://issues.gradle.org/browse/GRADLE-1995).
// That's why we use this dirty hack here.
boolean unresolved = binaryPath.getPath().startsWith(UNRESOLVED_DEPENDENCY_PREFIX);
if (moduleVersion == null) {
// use module library level if the dependency does not originate from a remote repository.
level = LibraryLevel.MODULE;
if (binaryPath.isFile()) {
libraryName = FileUtil.getNameWithoutExtension(binaryPath);
}
else {
libraryName = FileUtil.sanitizeFileName(binaryPath.getPath());
}
if (unresolved) {
// Gradle uses names like 'unresolved dependency - commons-collections commons-collections 3.2' for unresolved dependencies.
libraryName = binaryPath.getName().substring(UNRESOLVED_DEPENDENCY_PREFIX.length());
int i = libraryName.indexOf(' ');
if (i >= 0) {
i = CharArrayUtil.shiftForward(libraryName, i + 1, " ");
}
if (i >= 0 && i < libraryName.length()) {
int dependencyNameIndex = i;
i = libraryName.indexOf(' ', dependencyNameIndex);
if (i > 0) {
libraryName = String.format("%s-%s", libraryName.substring(dependencyNameIndex, i), libraryName.substring(i + 1));
}
}
}
}
else {
level = LibraryLevel.PROJECT;
libraryName = String.format("%s:%s:%s", moduleVersion.getGroup(), moduleVersion.getName(), moduleVersion.getVersion());
}
final LibraryData library = new LibraryData(GradleConstants.SYSTEM_ID, libraryName, unresolved);
if (!unresolved) {
library.addPath(LibraryPathType.BINARY, binaryPath.getAbsolutePath());
}
File sourcePath = dependency.getSource();
if (!unresolved && sourcePath != null) {
library.addPath(LibraryPathType.SOURCE, sourcePath.getAbsolutePath());
}
if (!unresolved && sourcePath == null) {
attachGradleSdkSources(gradleModule, libraryName, binaryPath, library);
}
File javadocPath = dependency.getJavadoc();
if (!unresolved && javadocPath != null) {
library.addPath(LibraryPathType.DOC, javadocPath.getAbsolutePath());
}
if(level == LibraryLevel.PROJECT) {
DataNode<LibraryData> libraryData =
ExternalSystemApiUtil.find(ideProject, ProjectKeys.LIBRARY, new BooleanFunction<DataNode<LibraryData>>() {
@Override
public boolean fun(DataNode<LibraryData> node) {
return library.equals(node.getData());
}
});
if (libraryData == null) {
ideProject.createChild(ProjectKeys.LIBRARY, library);
}
}
return new LibraryDependencyData(ownerModule.getData(), library, level);
}
private void attachGradleSdkSources(@NotNull IdeaModule gradleModule,
@NotNull final String libName,
@Nullable final File libFile,
LibraryData library) {
if (libFile == null || !libName.startsWith("gradle-")) return;
final BuildScriptClasspathModel buildScriptClasspathModel =
resolverCtx.getExtraProject(gradleModule, BuildScriptClasspathModel.class);
if (buildScriptClasspathModel == null) return;
final File gradleHomeDir = buildScriptClasspathModel.getGradleHomeDir();
if (gradleHomeDir == null) return;
if (!FileUtil.isAncestor(gradleHomeDir, libFile, true)) return;
File libOrPluginsFile = libFile.getParentFile();
if (libOrPluginsFile != null && ("plugins".equals(libOrPluginsFile.getName()))) {
libOrPluginsFile = libOrPluginsFile.getParentFile();
}
if (libOrPluginsFile != null && "lib".equals(libOrPluginsFile.getName()) && libOrPluginsFile.getParentFile() != null) {
File srcDir = new File(libOrPluginsFile.getParentFile(), "src");
GradleVersion current = GradleVersion.version(buildScriptClasspathModel.getGradleVersion());
if (current.compareTo(GradleVersion.version("1.9")) >= 0) {
int endIndex = libName.indexOf(current.getVersion());
if (endIndex != -1) {
String srcDirChild = libName.substring("gradle-".length(), endIndex - 1);
srcDir = new File(srcDir, srcDirChild);
}
}
if (srcDir.isDirectory()) {
library.addPath(LibraryPathType.SOURCE, srcDir.getAbsolutePath());
}
}
}
private static boolean isIdeaTask(final String taskName) {
return taskName.toLowerCase(Locale.ENGLISH).contains("idea");
}
}