IDEA-120097 Gradle sourceCompatibility is ignored in multi-module builds
authorVladislav.Soroka <Vladislav.Soroka@jetbrains.com>
Tue, 27 Oct 2015 12:57:36 +0000 (15:57 +0300)
committerVladislav.Soroka <Vladislav.Soroka@jetbrains.com>
Tue, 27 Oct 2015 13:00:38 +0000 (16:00 +0300)
platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/ModuleData.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/AbstractModuleDataService.java
plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java
plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleMiscImportingTest.java [new file with mode: 0644]
plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ExternalSourceSet.java
plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultExternalSourceSet.java
plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/ExternalProjectBuilderImpl.groovy

index 75e252e44b0f190a03151e29071583f9599d1c3e..8bb80bdb303712e531af42eb2ca911a18fec0c49 100644 (file)
@@ -26,11 +26,13 @@ public class ModuleData extends AbstractNamedData implements Named, ExternalConf
   @NotNull private final String myModuleTypeId;
   @NotNull private final String myExternalConfigPath;
   @NotNull private String myModuleFileDirectoryPath;
-  @Nullable private String group;
-  @Nullable private String version;
-  @Nullable private String description;
+  @Nullable private String myGroup;
+  @Nullable private String myVersion;
+  @Nullable private String myDescription;
   @NotNull private List<File> myArtifacts;
-  @Nullable private String[] ideModuleGroup;
+  @Nullable private String[] myIdeModuleGroup;
+  @Nullable  private String mySourceCompatibility;
+  @Nullable private String myTargetCompatibility;
 
   private boolean myInheritProjectCompileOutputPath = true;
 
@@ -121,29 +123,29 @@ public class ModuleData extends AbstractNamedData implements Named, ExternalConf
 
   @Nullable
   public String getGroup() {
-    return group;
+    return myGroup;
   }
 
   public void setGroup(@Nullable String group) {
-    this.group = group;
+    this.myGroup = group;
   }
 
   @Nullable
   public String getVersion() {
-    return version;
+    return myVersion;
   }
 
   public void setVersion(@Nullable String version) {
-    this.version = version;
+    this.myVersion = version;
   }
 
   @Nullable
   public String getDescription() {
-    return description;
+    return myDescription;
   }
 
   public void setDescription(@Nullable String description) {
-    this.description = description;
+    this.myDescription = description;
   }
 
   @NotNull
@@ -157,11 +159,29 @@ public class ModuleData extends AbstractNamedData implements Named, ExternalConf
 
   @Nullable
   public String[] getIdeModuleGroup() {
-    return ideModuleGroup;
+    return myIdeModuleGroup;
   }
 
   public void setIdeModuleGroup(@Nullable String[] ideModuleGroup) {
-    this.ideModuleGroup = ideModuleGroup;
+    this.myIdeModuleGroup = ideModuleGroup;
+  }
+
+  @Nullable
+  public String getSourceCompatibility() {
+    return mySourceCompatibility;
+  }
+
+  public void setSourceCompatibility(@Nullable String sourceCompatibility) {
+    mySourceCompatibility = sourceCompatibility;
+  }
+
+  @Nullable
+  public String getTargetCompatibility() {
+    return myTargetCompatibility;
+  }
+
+  public void setTargetCompatibility(@Nullable String targetCompatibility) {
+    myTargetCompatibility = targetCompatibility;
   }
 
   @Override
@@ -171,10 +191,10 @@ public class ModuleData extends AbstractNamedData implements Named, ExternalConf
 
     ModuleData that = (ModuleData)o;
 
-    if (group != null ? !group.equals(that.group) : that.group != null) return false;
+    if (myGroup != null ? !myGroup.equals(that.myGroup) : that.myGroup != null) return false;
     if (!myModuleTypeId.equals(that.myModuleTypeId)) return false;
-    if (version != null ? !version.equals(that.version) : that.version != null) return false;
-    if (description != null ? !description.equals(that.description) : that.description != null) return false;
+    if (myVersion != null ? !myVersion.equals(that.myVersion) : that.myVersion != null) return false;
+    if (myDescription != null ? !myDescription.equals(that.myDescription) : that.myDescription != null) return false;
 
     return true;
   }
@@ -183,17 +203,17 @@ public class ModuleData extends AbstractNamedData implements Named, ExternalConf
   public int hashCode() {
     int result = super.hashCode();
     result = 31 * result + myModuleTypeId.hashCode();
-    result = 31 * result + (group != null ? group.hashCode() : 0);
-    result = 31 * result + (version != null ? version.hashCode() : 0);
-    result = 31 * result + (description != null ? description.hashCode() : 0);
+    result = 31 * result + (myGroup != null ? myGroup.hashCode() : 0);
+    result = 31 * result + (myVersion != null ? myVersion.hashCode() : 0);
+    result = 31 * result + (myDescription != null ? myDescription.hashCode() : 0);
     return result;
   }
 
   @Override
   public String toString() {
     return String.format("module '%s:%s:%s'",
-                         group == null ? "" : group,
+                         myGroup == null ? "" : myGroup,
                          getExternalName(),
-                         version == null ? "" : version);
+                         myVersion == null ? "" : myVersion);
   }
 }
index f36e260dbb1dacd701c81e4cd78162b9c5a113cb..81847f1ed9a2d001e0675d00448fe1b482f75d16 100644 (file)
@@ -15,6 +15,7 @@
  */
 package com.intellij.openapi.externalSystem.service.project.manage;
 
+import com.intellij.compiler.CompilerConfiguration;
 import com.intellij.ide.util.projectWizard.ModuleBuilder;
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.diagnostic.Logger;
@@ -40,13 +41,11 @@ import com.intellij.openapi.util.Key;
 import com.intellij.openapi.util.Pair;
 import com.intellij.openapi.util.io.FileUtil;
 import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.pom.java.LanguageLevel;
 import com.intellij.ui.CheckBoxList;
 import com.intellij.ui.IdeBorderFactory;
 import com.intellij.ui.components.JBScrollPane;
-import com.intellij.util.ArrayUtil;
-import com.intellij.util.Consumer;
-import com.intellij.util.Function;
-import com.intellij.util.SmartList;
+import com.intellij.util.*;
 import com.intellij.util.containers.ContainerUtil;
 import com.intellij.util.containers.ContainerUtilRt;
 import org.jetbrains.annotations.NotNull;
@@ -90,8 +89,9 @@ public abstract class AbstractModuleDataService<E extends ModuleData> extends Ab
 
         final ModifiableModuleModel modifiableModel = modelsProvider.getModifiableModuleModel();
         modifiableModel.setModuleGroupPath(module, node.getData().getIdeModuleGroup());
-
-        syncPaths(module, modelsProvider, node.getData());
+        ModifiableRootModel modifiableRootModel = modelsProvider.getModifiableRootModel(module);
+        syncPaths(module, modifiableRootModel, node.getData());
+        setLanguageLevel(modifiableRootModel, node.getData());
       }
     }
   }
@@ -161,8 +161,7 @@ public abstract class AbstractModuleDataService<E extends ModuleData> extends Ab
     return result;
   }
 
-  private static void syncPaths(@NotNull Module module, @NotNull IdeModifiableModelsProvider modelsProvider, @NotNull ModuleData data) {
-    ModifiableRootModel modifiableModel = modelsProvider.getModifiableRootModel(module);
+  private static void syncPaths(@NotNull Module module, @NotNull ModifiableRootModel modifiableModel, @NotNull ModuleData data) {
     CompilerModuleExtension extension = modifiableModel.getModuleExtension(CompilerModuleExtension.class);
     if (extension == null) {
       //modifiableModel.dispose();
@@ -331,10 +330,12 @@ public abstract class AbstractModuleDataService<E extends ModuleData> extends Ab
                           @NotNull IdeModifiableModelsProvider modelsProvider) {
     for (DataNode<E> moduleDataNode : toImport) {
       final Module module = moduleDataNode.getUserData(MODULE_KEY);
+      if (module == null) continue;
       final Map<OrderEntry, OrderAware> orderAwareMap = moduleDataNode.getUserData(ORDERED_DATA_MAP_KEY);
-      if (module != null && orderAwareMap != null) {
+      if (orderAwareMap != null) {
         rearrangeOrderEntries(orderAwareMap, modelsProvider.getModifiableRootModel(module));
       }
+      setBytecodeTargetLevel(project, module, moduleDataNode.getData());
     }
   }
 
@@ -417,4 +418,24 @@ public abstract class AbstractModuleDataService<E extends ModuleData> extends Ab
     }
     return idx == -1 ? -1 : idx;
   }
+
+  private void setLanguageLevel(@NotNull ModifiableRootModel modifiableRootModel, E data) {
+    LanguageLevel level = LanguageLevel.parse(data.getSourceCompatibility());
+    if (level != null) {
+      try {
+        modifiableRootModel.getModuleExtension(LanguageLevelModuleExtension.class).setLanguageLevel(level);
+      }
+      catch (IllegalArgumentException e) {
+        LOG.debug(e);
+      }
+    }
+  }
+
+  private void setBytecodeTargetLevel(@NotNull Project project, @NotNull Module module, @NotNull E data) {
+    String targetLevel = data.getTargetCompatibility();
+    if (targetLevel != null) {
+      CompilerConfiguration configuration = CompilerConfiguration.getInstance(project);
+      configuration.setBytecodeTargetLevel(module, targetLevel);
+    }
+  }
 }
index 40efbaee6111647fa1c8ccd68b7ca3a5ab465db7..17df607d39876746505510cba4f7546c9724c46d 100644 (file)
@@ -215,6 +215,9 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver
         sourceSetData.setVersion(externalProject.getVersion());
         sourceSetData.setIdeModuleGroup(moduleGroup);
 
+        sourceSetData.setSourceCompatibility(sourceSet.getSourceCompatibility());
+        sourceSetData.setTargetCompatibility(sourceSet.getTargetCompatibility());
+
         if ("main".equals(sourceSet.getName())) {
           final List<File> artifacts = ContainerUtil.newArrayList();
           final Set<File> defaultArtifacts = externalProject.getArtifactsByConfiguration().get("default");
diff --git a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleMiscImportingTest.java b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleMiscImportingTest.java
new file mode 100644 (file)
index 0000000..605c34f
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2000-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 org.jetbrains.plugins.gradle.importing;
+
+import com.intellij.compiler.CompilerConfiguration;
+import com.intellij.openapi.roots.*;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.pom.java.LanguageLevel;
+import com.intellij.util.PathUtil;
+import org.gradle.util.GradleVersion;
+import org.junit.Test;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 10/27/2015
+ */
+@SuppressWarnings("JUnit4AnnotatedMethodInJUnit3TestCase")
+public class GradleMiscImportingTest extends GradleImportingTestCase {
+
+  /**
+   * It's sufficient to run the test against one gradle version
+   */
+  @SuppressWarnings("MethodOverridesStaticMethodOfSuperclass")
+  @Parameterized.Parameters(name = "with Gradle-{0}")
+  public static Collection<Object[]> data() throws Throwable {
+    return Arrays.asList(new Object[][]{{BASE_GRADLE_VERSION}});
+  }
+
+
+  @Test
+  public void testInheritProjectJdkForModules() throws Exception {
+    importProject(
+      "apply plugin: 'java'"
+    );
+
+    assertModules("project", "project_main", "project_test");
+    assertTrue(ModuleRootManager.getInstance(getModule("project")).isSdkInherited());
+    assertTrue(ModuleRootManager.getInstance(getModule("project_main")).isSdkInherited());
+    assertTrue(ModuleRootManager.getInstance(getModule("project_test")).isSdkInherited());
+  }
+
+  @Test
+  public void testLanguageLevel() throws Exception {
+    importProject(
+      "apply plugin: 'java'\n" +
+      "sourceCompatibility = 1.5\n" +
+      "apply plugin: 'java'\n" +
+      "compileTestJava {\n" +
+      "  sourceCompatibility = 1.8\n" +
+      "}\n"
+    );
+
+    assertModules("project", "project_main", "project_test");
+    assertEquals(LanguageLevel.JDK_1_5, getLanguageLevelForModule("project_main"));
+    assertEquals(LanguageLevel.JDK_1_8, getLanguageLevelForModule("project_test"));
+  }
+
+  @Test
+  public void testTargetLevel() throws Exception {
+    importProject(
+      "apply plugin: 'java'\n" +
+      "targetCompatibility = 1.8\n" +
+      "compileJava {\n" +
+      "  targetCompatibility = 1.5\n" +
+      "}\n"
+    );
+
+    assertModules("project", "project_main", "project_test");
+    assertEquals("1.5", getBytecodeTargetLevel("project_main"));
+    assertEquals("1.8", getBytecodeTargetLevel("project_test"));
+
+  }
+
+  private LanguageLevel getLanguageLevelForModule(final String moduleName) {
+    return LanguageLevelModuleExtensionImpl.getInstance(getModule(moduleName)).getLanguageLevel();
+  }
+
+  private String getBytecodeTargetLevel(String moduleName) {
+    return CompilerConfiguration.getInstance(myProject).getBytecodeTargetLevel(getModule(moduleName));
+  }
+}
index 449f9f5f2fcb02d6631760a55fd991604a659531..9d13b61de1162ac6f21353db6da15cec900ef143 100644 (file)
@@ -17,6 +17,8 @@ package org.jetbrains.plugins.gradle.model;
 
 import com.intellij.openapi.externalSystem.model.project.IExternalSystemSourceType;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
 import java.io.Serializable;
 import java.util.Collection;
 import java.util.Map;
@@ -29,6 +31,12 @@ public interface ExternalSourceSet extends Serializable {
   @NotNull
   String getName();
 
+  @Nullable
+  String getSourceCompatibility();
+
+  @Nullable
+  String getTargetCompatibility();
+
   Collection<ExternalDependency> getDependencies();
 
   @NotNull
index c05cd0e296ba7280398cc7aaa48939facbe913e6..8cc09ad64301260ee2c3328f490a6044e3b98b77 100644 (file)
@@ -18,6 +18,7 @@ package org.jetbrains.plugins.gradle.model;
 import com.intellij.openapi.externalSystem.model.project.ExternalSystemSourceType;
 import com.intellij.openapi.externalSystem.model.project.IExternalSystemSourceType;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 import java.util.*;
 
@@ -31,6 +32,8 @@ public class DefaultExternalSourceSet implements ExternalSourceSet {
   private String myName;
   private Map<IExternalSystemSourceType, ExternalSourceDirectorySet> mySources;
   private Collection<ExternalDependency> myDependencies;
+  private String mySourceCompatibility;
+  private String myTargetCompatibility;
 
   public DefaultExternalSourceSet() {
     mySources = new HashMap<IExternalSystemSourceType, ExternalSourceDirectorySet>();
@@ -40,6 +43,8 @@ public class DefaultExternalSourceSet implements ExternalSourceSet {
   public DefaultExternalSourceSet(ExternalSourceSet sourceSet) {
     this();
     myName = sourceSet.getName();
+    mySourceCompatibility = sourceSet.getSourceCompatibility();
+    myTargetCompatibility = sourceSet.getTargetCompatibility();
     for (Map.Entry<IExternalSystemSourceType, ExternalSourceDirectorySet> entry : sourceSet.getSources().entrySet()) {
       mySources.put(ExternalSystemSourceType.from(entry.getKey()), new DefaultExternalSourceDirectorySet(entry.getValue()));
     }
@@ -55,6 +60,26 @@ public class DefaultExternalSourceSet implements ExternalSourceSet {
     return myName;
   }
 
+  @Nullable
+  @Override
+  public String getSourceCompatibility() {
+    return mySourceCompatibility;
+  }
+
+  public void setSourceCompatibility(@Nullable String sourceCompatibility) {
+    mySourceCompatibility = sourceCompatibility;
+  }
+
+  @Nullable
+  @Override
+  public String getTargetCompatibility() {
+    return myTargetCompatibility;
+  }
+
+  public void setTargetCompatibility(@Nullable String targetCompatibility) {
+    myTargetCompatibility = targetCompatibility;
+  }
+
   @Override
   public Collection<ExternalDependency> getDependencies() {
     return myDependencies;
index c79797675db5ab067110bea1ec950584b09996f4..6b2743feab4601c9e70b29c015d2d7ed2e993e88 100644 (file)
@@ -18,6 +18,7 @@ package org.jetbrains.plugins.gradle.tooling.builder
 import com.google.gson.GsonBuilder
 import com.intellij.openapi.externalSystem.model.project.ExternalSystemSourceType
 import org.gradle.api.Action
+import org.gradle.api.JavaVersion
 import org.gradle.api.Project
 import org.gradle.api.Task
 import org.gradle.api.artifacts.Configuration
@@ -26,6 +27,7 @@ import org.gradle.api.file.FileCopyDetails
 import org.gradle.api.tasks.SourceSet
 import org.gradle.api.tasks.SourceSetContainer
 import org.gradle.api.tasks.bundling.Jar
+import org.gradle.api.tasks.compile.JavaCompile
 import org.gradle.api.tasks.util.PatternFilterable
 import org.gradle.plugins.ide.idea.IdeaPlugin
 import org.jetbrains.annotations.NotNull
@@ -155,6 +157,16 @@ class ExternalProjectBuilderImpl implements ModelBuilderService {
     def ideaSourceDirs = ideaPluginModule.sourceDirs;
     def ideaTestSourceDirs = ideaPluginModule.testSourceDirs;
 
+    def projectSourceCompatibility
+    def projectTargetCompatibility
+
+    if(project.hasProperty('sourceCompatibility') && project.sourceCompatibility instanceof JavaVersion) {
+      projectSourceCompatibility = project.sourceCompatibility.name;
+    }
+    if(project.hasProperty('targetCompatibility') && project.targetCompatibility instanceof JavaVersion) {
+      projectTargetCompatibility = project.targetCompatibility.name;
+    }
+
     def result = [:] as Map<String, ExternalSourceSet>
     //noinspection GrUnresolvedAccess
     if (!project.hasProperty("sourceSets") || !(project.sourceSets instanceof SourceSetContainer)) {
@@ -171,6 +183,15 @@ class ExternalProjectBuilderImpl implements ModelBuilderService {
       ExternalSourceSet externalSourceSet = new DefaultExternalSourceSet()
       externalSourceSet.name = sourceSet.name
 
+      def javaCompileTask = project.tasks.findByName(sourceSet.compileJavaTaskName)
+      if(javaCompileTask instanceof JavaCompile) {
+        externalSourceSet.sourceCompatibility = javaCompileTask.sourceCompatibility ?: projectSourceCompatibility
+        externalSourceSet.targetCompatibility = javaCompileTask.targetCompatibility ?: projectTargetCompatibility
+      } else {
+        externalSourceSet.sourceCompatibility = projectSourceCompatibility
+        externalSourceSet.targetCompatibility = projectTargetCompatibility
+      }
+
       def sources = [:] as Map<ExternalSystemSourceType, ExternalSourceDirectorySet>
       ExternalSourceDirectorySet resourcesDirectorySet = new DefaultExternalSourceDirectorySet()
       resourcesDirectorySet.name = sourceSet.resources.name