blob: eef0495d44762296feb44f80c8f3f3150ac8d3ff [file]
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.plugins.gradle.nativeplatform.project;
import com.intellij.openapi.externalSystem.model.DataNode;
import com.intellij.openapi.externalSystem.model.Key;
import com.intellij.openapi.externalSystem.model.ProjectKeys;
import com.intellij.openapi.externalSystem.model.project.ContentRootData;
import com.intellij.openapi.externalSystem.model.project.ExternalSystemSourceType;
import com.intellij.openapi.externalSystem.model.project.ModuleData;
import com.intellij.openapi.externalSystem.util.ExternalSystemConstants;
import com.intellij.openapi.externalSystem.util.Order;
import com.intellij.util.containers.ContainerUtil;
import org.gradle.tooling.model.DomainObjectSet;
import org.gradle.tooling.model.Task;
import org.gradle.tooling.model.cpp.CppExecutable;
import org.gradle.tooling.model.cpp.CppSharedLibrary;
import org.gradle.tooling.model.cpp.CppStaticLibrary;
import org.gradle.tooling.model.cpp.LinkageDetails;
import org.gradle.tooling.model.idea.IdeaModule;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.gradle.model.DefaultExternalTask;
import org.jetbrains.plugins.gradle.model.ExternalTask;
import org.jetbrains.plugins.gradle.nativeplatform.tooling.builder.CppModelBuilder;
import org.jetbrains.plugins.gradle.nativeplatform.tooling.model.*;
import org.jetbrains.plugins.gradle.nativeplatform.tooling.model.impl.*;
import org.jetbrains.plugins.gradle.service.project.AbstractProjectResolverExtension;
import org.jetbrains.plugins.gradle.util.GradleConstants;
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* @author Vladislav.Soroka
*/
@Order(ExternalSystemConstants.UNORDERED)
public class GradleNativeProjectResolver extends AbstractProjectResolverExtension {
@NotNull public static final Key<CppProject> CPP_PROJECT = Key.create(CppProject.class, ProjectKeys.MODULE.getProcessingWeight() + 1);
@Override
public void populateModuleContentRoots(@NotNull IdeaModule gradleModule, @NotNull DataNode<ModuleData> ideModule) {
CppProjectImpl cppProject = appendCppProject(gradleModule, ideModule);
if (cppProject != null) {
CppComponent mainComponent = cppProject.getMainComponent();
Set<File> sourceFolders = mainComponent == null ? new HashSet<>() : getSourceFolders(mainComponent);
CppTestSuite testComponent = cppProject.getTestComponent();
Set<File> testSourceFolders = testComponent == null ? new HashSet<>() : getSourceFolders(testComponent);
testSourceFolders.removeAll(sourceFolders);
for (File folder : sourceFolders) {
ContentRootData ideContentRoot = new ContentRootData(GradleConstants.SYSTEM_ID, folder.getAbsolutePath());
ideContentRoot.storePath(ExternalSystemSourceType.SOURCE, folder.getAbsolutePath());
ideModule.createChild(ProjectKeys.CONTENT_ROOT, ideContentRoot);
}
for (File folder : testSourceFolders) {
ContentRootData ideContentRoot = new ContentRootData(GradleConstants.SYSTEM_ID, folder.getAbsolutePath());
ideContentRoot.storePath(ExternalSystemSourceType.TEST, folder.getAbsolutePath());
ideModule.createChild(ProjectKeys.CONTENT_ROOT, ideContentRoot);
}
}
nextResolver.populateModuleContentRoots(gradleModule, ideModule);
}
@NotNull
@Override
public Set<Class<?>> getExtraProjectModelClasses() {
return ContainerUtil.set(org.gradle.tooling.model.cpp.CppProject.class, CppProject.class);
}
@NotNull
@Override
public Set<Class<?>> getToolingExtensionsClasses() {
return ContainerUtil.set(
// native-gradle-tooling jar
CppModelBuilder.class
);
}
@Override
public Set<Class<?>> getTargetTypes() {
return ContainerUtil.set(
org.jetbrains.plugins.gradle.nativeplatform.tooling.model.CppExecutable.class,
org.jetbrains.plugins.gradle.nativeplatform.tooling.model.CppSharedLibrary.class,
org.jetbrains.plugins.gradle.nativeplatform.tooling.model.CppStaticLibrary.class
);
}
@Nullable
private CppProjectImpl appendCppProject(@NotNull IdeaModule gradleModule,
@NotNull DataNode<ModuleData> ideModule) {
CppProjectImpl cppProject = null;
org.gradle.tooling.model.cpp.CppProject gradleCppProject =
resolverCtx.getExtraProject(gradleModule, org.gradle.tooling.model.cpp.CppProject.class);
if (gradleCppProject != null) {
cppProject = convert(gradleCppProject);
}
else {
// todo[Vlad] old experimental support for Gradle 4.1 - 4.9, to be removed in 2019.1
CppProject customGradleCppProject = resolverCtx.getExtraProject(gradleModule, CppProject.class);
if (customGradleCppProject != null) {
// store a local process copy of the object to get rid of proxy types for further serialization
cppProject = new CppProjectImpl(customGradleCppProject);
}
}
if (cppProject != null) {
ideModule.createChild(CPP_PROJECT, cppProject);
}
return cppProject;
}
private static CppProjectImpl convert(@NotNull org.gradle.tooling.model.cpp.CppProject gradleCppProject) {
CppProjectImpl cppProject = new CppProjectImpl();
cppProject.setMainComponent(convert(gradleCppProject.getMainComponent()));
cppProject.setTestComponent((CppTestSuite)convert(gradleCppProject.getTestComponent()));
return cppProject;
}
private static CppComponent convert(@Nullable org.gradle.tooling.model.cpp.CppComponent component) {
if (component == null) return null;
CppComponentImpl cppComponent;
if (component instanceof org.gradle.tooling.model.cpp.CppTestSuite) {
cppComponent = new CppTestSuiteImpl(component.getName(), component.getBaseName());
}
else {
cppComponent = new CppComponentImpl(component.getName(), component.getBaseName());
}
for (org.gradle.tooling.model.cpp.CppBinary binary : component.getBinaries()) {
cppComponent.addBinary(convert(binary));
}
return cppComponent;
}
@Nullable
private static CppBinaryImpl convert(@Nullable org.gradle.tooling.model.cpp.CppBinary binary) {
CppBinaryImpl cppBinary;
if (binary instanceof CppExecutable) {
cppBinary = new CppExecutableImpl(binary.getName(), binary.getBaseName(), binary.getVariantName());
}
else if (binary instanceof CppSharedLibrary) {
cppBinary = new CppSharedLibraryImpl(binary.getName(), binary.getBaseName(), binary.getVariantName());
}
else if (binary instanceof CppStaticLibrary) {
cppBinary = new CppStaticLibraryImpl(binary.getName(), binary.getBaseName(), binary.getVariantName());
}
else {
return null;
}
cppBinary.setCompilationDetails(convert(binary.getCompilationDetails()));
cppBinary.setLinkageDetails(convert(binary.getLinkageDetails()));
return cppBinary;
}
private static LinkageDetailsImpl convert(LinkageDetails details) {
LinkageDetailsImpl linkageDetails = new LinkageDetailsImpl();
linkageDetails.setAdditionalArgs(new ArrayList<>(details.getAdditionalArgs()));
linkageDetails.setLinkTask(convert(details.getLinkTask()));
linkageDetails.setOutputLocation(details.getOutputLocation());
return linkageDetails;
}
private static ExternalTask convert(Task task) {
if (task == null) return null;
DefaultExternalTask externalTask = new DefaultExternalTask();
externalTask.setDescription(task.getDescription());
externalTask.setGroup(task.getGroup());
externalTask.setName(task.getName());
externalTask.setQName(task.getPath());
//externalTask.setType(task.);
return externalTask;
}
private static CompilationDetailsImpl convert(org.gradle.tooling.model.cpp.CompilationDetails details) {
CompilationDetailsImpl compilationDetails = new CompilationDetailsImpl();
compilationDetails.setCompileTask(convert(details.getCompileTask()));
compilationDetails.setAdditionalArgs(new ArrayList<>(details.getAdditionalArgs()));
compilationDetails.setCompilerExecutable(details.getCompilerExecutable());
compilationDetails.setCompileWorkingDir(details.getCompileWorkingDir());
compilationDetails.setFrameworkSearchPaths(new ArrayList<>(details.getFrameworkSearchPaths()));
compilationDetails.setHeaderDirs(new LinkedHashSet<>(details.getHeaderDirs()));
compilationDetails.setMacroDefines(convert(details.getMacroDefines()));
compilationDetails.setMacroUndefines(new LinkedHashSet<>(details.getMacroUndefines()));
compilationDetails.setSources(convertSources(details.getSources()));
compilationDetails.setSystemHeaderSearchPaths(new ArrayList<>(details.getSystemHeaderSearchPaths()));
compilationDetails.setUserHeaderSearchPaths(new ArrayList<>(details.getUserHeaderSearchPaths()));
return compilationDetails;
}
private static Set<SourceFile> convertSources(DomainObjectSet<? extends org.gradle.tooling.model.cpp.SourceFile> sources) {
Set<SourceFile> sourceFiles = new LinkedHashSet<>(sources.size());
for (org.gradle.tooling.model.cpp.SourceFile sourceFile : sources) {
sourceFiles.add(convert(sourceFile));
}
return sourceFiles;
}
private static SourceFile convert(org.gradle.tooling.model.cpp.SourceFile sourceFile) {
return new SourceFileImpl(sourceFile.getSourceFile(), sourceFile.getObjectFile());
}
private static Set<MacroDirective> convert(DomainObjectSet<? extends org.gradle.tooling.model.cpp.MacroDirective> directives) {
Set<MacroDirective> macroDirectives = new LinkedHashSet<>(directives.size());
for (org.gradle.tooling.model.cpp.MacroDirective directive : directives) {
macroDirectives.add(convert(directive));
}
return macroDirectives;
}
private static MacroDirective convert(org.gradle.tooling.model.cpp.MacroDirective directive) {
return new MacroDirectiveImpl(directive.getName(), directive.getValue());
}
private static Set<File> getSourceFolders(CppComponent component) {
Set<File> result = new LinkedHashSet<>();
for (CppBinary binary : component.getBinaries()) {
CompilationDetails compilationDetails = binary.getCompilationDetails();
result.addAll(compilationDetails.getHeaderDirs());
compilationDetails.getSources().forEach(sourceFile -> result.add(sourceFile.getSourceFile().getParentFile()));
}
return result;
}
}