All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.gradle.plugins.ide.idea.IdeaPlugin Maven / Gradle / Ivy

There is a newer version: 8.11.1
Show newest version
/*
 * Copyright 2010 the original author or authors.
 *
 * 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.gradle.plugins.ide.idea;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
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;
import org.gradle.api.artifacts.ConfigurationContainer;
import org.gradle.api.artifacts.PublishArtifact;
import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
import org.gradle.api.file.FileCollection;
import org.gradle.api.internal.ConventionMapping;
import org.gradle.api.internal.IConventionAware;
import org.gradle.api.internal.artifacts.ivyservice.projectmodule.ProjectLocalComponentProvider;
import org.gradle.api.internal.artifacts.publish.DefaultPublishArtifact;
import org.gradle.api.internal.project.ProjectInternal;
import org.gradle.api.invocation.Gradle;
import org.gradle.api.plugins.JavaBasePlugin;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.plugins.WarPlugin;
import org.gradle.api.plugins.scala.ScalaBasePlugin;
import org.gradle.api.tasks.SourceSetContainer;
import org.gradle.internal.component.local.model.LocalComponentArtifactMetadata;
import org.gradle.internal.component.local.model.PublishArtifactLocalArtifactMetadata;
import org.gradle.internal.reflect.Instantiator;
import org.gradle.language.scala.plugins.ScalaLanguagePlugin;
import org.gradle.plugins.ide.api.XmlFileContentMerger;
import org.gradle.plugins.ide.idea.internal.IdeaNameDeduper;
import org.gradle.plugins.ide.idea.internal.IdeaScalaConfigurer;
import org.gradle.plugins.ide.idea.model.IdeaLanguageLevel;
import org.gradle.plugins.ide.idea.model.IdeaModel;
import org.gradle.plugins.ide.idea.model.IdeaModule;
import org.gradle.plugins.ide.idea.model.IdeaModuleIml;
import org.gradle.plugins.ide.idea.model.IdeaProject;
import org.gradle.plugins.ide.idea.model.IdeaWorkspace;
import org.gradle.plugins.ide.idea.model.PathFactory;
import org.gradle.plugins.ide.idea.model.internal.GeneratedIdeaScope;
import org.gradle.plugins.ide.idea.model.internal.IdeaDependenciesProvider;
import org.gradle.plugins.ide.internal.IdePlugin;

import javax.inject.Inject;
import java.io.File;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;

import static org.gradle.internal.component.local.model.DefaultProjectComponentIdentifier.newProjectId;

/**
 * Adds a GenerateIdeaModule task. When applied to a root project, also adds a GenerateIdeaProject task. For projects that have the Java plugin applied, the tasks receive additional Java-specific
 * configuration.
 */
public class IdeaPlugin extends IdePlugin {
    private static final Predicate HAS_IDEA_AND_JAVA_PLUGINS = new Predicate() {
        @Override
        public boolean apply(Project project) {
            return project.getPlugins().hasPlugin(IdeaPlugin.class) && project.getPlugins().hasPlugin(JavaBasePlugin.class);
        }
    };
    public static final Function SOURCE_COMPATIBILITY = new Function() {
        @Override
        public JavaVersion apply(Project p) {
            return p.getConvention().getPlugin(JavaPluginConvention.class).getSourceCompatibility();
        }
    };
    public static final Function TARGET_COMPATIBILITY = new Function() {
        @Override
        public JavaVersion apply(Project p) {
            return p.getConvention().getPlugin(JavaPluginConvention.class).getTargetCompatibility();
        }
    };

    private final Instantiator instantiator;
    private IdeaModel ideaModel;
    private List allJavaProjects;

    @Inject
    public IdeaPlugin(Instantiator instantiator) {
        this.instantiator = instantiator;
    }

    public IdeaModel getModel() {
        return ideaModel;
    }

    @Override
    protected String getLifecycleTaskName() {
        return "idea";
    }

    @Override
    protected void onApply(Project project) {
        getLifecycleTask().setDescription("Generates IDEA project files (IML, IPR, IWS)");
        getCleanTask().setDescription("Cleans IDEA project files (IML, IPR)");

        ideaModel = project.getExtensions().create("idea", IdeaModel.class);

        configureIdeaWorkspace(project);
        configureIdeaProject(project);
        configureIdeaModule(project);
        configureForJavaPlugin(project);
        configureForWarPlugin(project);
        configureForScalaPlugin();
        postProcess("idea", new Action() {
            @Override
            public void execute(Gradle gradle) {
                performPostEvaluationActions();
            }
        });
    }

    public void performPostEvaluationActions() {
        makeSureModuleNamesAreUnique();
        // This needs to happen after de-duplication
        registerImlArtifacts();
    }

    private void makeSureModuleNamesAreUnique() {
        new IdeaNameDeduper().configureRoot(project.getRootProject());
    }

    private void registerImlArtifacts() {
        Set projectsWithIml = Sets.filter(project.getRootProject().getAllprojects(), new Predicate() {
            @Override
            public boolean apply(Project project) {
                return project.getPlugins().hasPlugin(IdeaPlugin.class);
            }
        });
        for (Project project : projectsWithIml) {
            ProjectLocalComponentProvider projectComponentProvider = ((ProjectInternal) project).getServices().get(ProjectLocalComponentProvider.class);
            ProjectComponentIdentifier projectId = newProjectId(project);
            projectComponentProvider.registerAdditionalArtifact(projectId, createImlArtifact(projectId, project));
        }
    }

    private static LocalComponentArtifactMetadata createImlArtifact(ProjectComponentIdentifier projectId, Project project) {
        String moduleName = project.getExtensions().getByType(IdeaModel.class).getModule().getName();
        File imlFile = new File(project.getProjectDir(), moduleName + ".iml");
        Task byName = project.getTasks().getByName("ideaModule");
        PublishArtifact publishArtifact = new DefaultPublishArtifact(moduleName, "iml", "iml", null, null, imlFile, byName);
        return new PublishArtifactLocalArtifactMetadata(projectId, publishArtifact);
    }

    private void configureIdeaWorkspace(final Project project) {
        if (isRoot(project)) {
            GenerateIdeaWorkspace task = project.getTasks().create("ideaWorkspace", GenerateIdeaWorkspace.class);
            task.setDescription("Generates an IDEA workspace file (IWS)");
            IdeaWorkspace workspace = new IdeaWorkspace();
            workspace.setIws(new XmlFileContentMerger(task.getXmlTransformer()));
            task.setWorkspace(workspace);
            ideaModel.setWorkspace(task.getWorkspace());
            task.setOutputFile(new File(project.getProjectDir(), project.getName() + ".iws"));
            addWorker(task, false);
        }

    }

    private void configureIdeaProject(final Project project) {
        if (isRoot(project)) {
            final GenerateIdeaProject task = project.getTasks().create("ideaProject", GenerateIdeaProject.class);
            task.setDescription("Generates IDEA project file (IPR)");
            XmlFileContentMerger ipr = new XmlFileContentMerger(task.getXmlTransformer());
            IdeaProject ideaProject = instantiator.newInstance(IdeaProject.class, project, ipr);
            task.setIdeaProject(ideaProject);
            ideaModel.setProject(ideaProject);

            ideaProject.setOutputFile(new File(project.getProjectDir(), project.getName() + ".ipr"));
            ConventionMapping conventionMapping = ((IConventionAware) ideaProject).getConventionMapping();
            conventionMapping.map("jdkName", new Callable() {
                @Override
                public String call() throws Exception {
                    return JavaVersion.current().toString();
                }
            });
            conventionMapping.map("languageLevel", new Callable() {
                @Override
                public IdeaLanguageLevel call() throws Exception {
                    JavaVersion maxSourceCompatibility = getMaxJavaModuleCompatibilityVersionFor(SOURCE_COMPATIBILITY);
                    return new IdeaLanguageLevel(maxSourceCompatibility);
                }

            });
            conventionMapping.map("targetBytecodeVersion", new Callable() {
                @Override
                public JavaVersion call() throws Exception {
                    return getMaxJavaModuleCompatibilityVersionFor(TARGET_COMPATIBILITY);
                }

            });

            ideaProject.setWildcards(Sets.newHashSet("!?*.class", "!?*.scala", "!?*.groovy", "!?*.java"));
            conventionMapping.map("modules", new Callable>() {
                @Override
                public List call() throws Exception {
                    return Lists.newArrayList(Iterables.transform(Sets.filter(project.getRootProject().getAllprojects(), new Predicate() {
                        @Override
                        public boolean apply(Project p) {
                            return p.getPlugins().hasPlugin(IdeaPlugin.class);
                        }

                    }), new Function() {
                        @Override
                        public IdeaModule apply(Project p) {
                            return ideaModelFor(p).getModule();
                        }
                    }));
                }
            });

            conventionMapping.map("pathFactory", new Callable() {
                @Override
                public PathFactory call() throws Exception {
                    return new PathFactory().addPathVariable("PROJECT_DIR", task.getOutputFile().getParentFile());
                }
            });

            addWorker(task);
        }

    }

    private static IdeaModel ideaModelFor(Project project) {
        return project.getExtensions().getByType(IdeaModel.class);
    }

    private JavaVersion getMaxJavaModuleCompatibilityVersionFor(Function toJavaVersion) {
        List allJavaProjects = getAllJavaProjects();
        if (allJavaProjects.isEmpty()) {
            return JavaVersion.VERSION_1_6;
        } else {
            return Collections.max(Lists.transform(allJavaProjects, toJavaVersion));
        }
    }

    private List getAllJavaProjects() {
        if (allJavaProjects != null) {
            // cache result because it is pretty expensive to compute
            return allJavaProjects;
        }
        allJavaProjects = Lists.newArrayList(Iterables.filter(project.getRootProject().getAllprojects(), HAS_IDEA_AND_JAVA_PLUGINS));
        return allJavaProjects;
    }

    private void configureIdeaModule(final Project project) {
        final GenerateIdeaModule task = project.getTasks().create("ideaModule", GenerateIdeaModule.class);
        task.setDescription("Generates IDEA module files (IML)");
        IdeaModuleIml iml = new IdeaModuleIml(task.getXmlTransformer(), project.getProjectDir());
        final IdeaModule module = instantiator.newInstance(IdeaModule.class, project, iml);
        task.setModule(module);

        ideaModel.setModule(module);
        ConventionMapping conventionMapping = ((IConventionAware) module).getConventionMapping();
        conventionMapping.map("sourceDirs", new Callable>() {
            @Override
            public Set call() throws Exception {
                return Sets.newHashSet();
            }
        });
        conventionMapping.map("name", new Callable() {
            @Override
            public String call() throws Exception {
                return project.getName();
            }
        });
        conventionMapping.map("contentRoot", new Callable() {
            @Override
            public File call() throws Exception {
                return project.getProjectDir();
            }
        });
        conventionMapping.map("testSourceDirs", new Callable>() {
            @Override
            public Set call() throws Exception {
                return Sets.newHashSet();
            }
        });
        conventionMapping.map("excludeDirs", new Callable>() {
            @Override
            public Set call() throws Exception {
                return Sets.newHashSet(project.getBuildDir(), project.file(".gradle"));
            }
        });

        conventionMapping.map("pathFactory", new Callable() {
            @Override
            public PathFactory call() throws Exception {
                final PathFactory factory = new PathFactory();
                factory.addPathVariable("MODULE_DIR", task.getOutputFile().getParentFile());
                for (Map.Entry entry : module.getPathVariables().entrySet()) {
                    factory.addPathVariable(entry.getKey(), entry.getValue());
                }
                return factory;
            }

        });

        addWorker(task);
    }

    private void configureForJavaPlugin(final Project project) {
        project.getPlugins().withType(JavaPlugin.class, new Action() {
            @Override
            public void execute(JavaPlugin javaPlugin) {
                configureIdeaModuleForJava(project);
            }
        });
    }
    private void configureForWarPlugin(final Project project) {
        project.getPlugins().withType(WarPlugin.class, new Action() {
            @Override
            public void execute(WarPlugin warPlugin) {
                configureIdeaModuleForWar(project);
            }
        });
    }

    private void configureIdeaModuleForJava(final Project project) {
        project.getTasks().withType(GenerateIdeaModule.class, new Action() {
            @Override
            public void execute(GenerateIdeaModule ideaModule) {
                // Defaults
                setupScopes(ideaModule);

                // Convention
                ConventionMapping convention = ((IConventionAware) ideaModule.getModule()).getConventionMapping();
                convention.map("sourceDirs", new Callable>() {
                    @Override
                    public Set call() throws Exception {
                        SourceSetContainer sourceSets = project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets();
                        return sourceSets.getByName("main").getAllSource().getSrcDirs();
                    }
                });
                convention.map("testSourceDirs", new Callable>() {
                    @Override
                    public Set call() throws Exception {
                        SourceSetContainer sourceSets = project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets();
                        return sourceSets.getByName("test").getAllSource().getSrcDirs();
                    }
                });
                convention.map("singleEntryLibraries", new Callable>() {
                    @Override
                    public Map call() throws Exception {
                        SourceSetContainer sourceSets = project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets();
                        LinkedHashMap map = new LinkedHashMap(2);
                        map.put("RUNTIME", sourceSets.getByName("main").getOutput().getDirs());
                        map.put("TEST", sourceSets.getByName("test").getOutput().getDirs());
                        return map;
                    }

                });
                convention.map("targetBytecodeVersion", new Callable() {
                    @Override
                    public JavaVersion call() throws Exception {
                        JavaVersion moduleTargetBytecodeLevel = project.getConvention().getPlugin(JavaPluginConvention.class).getTargetCompatibility();
                        return includeModuleBytecodeLevelOverride(project.getRootProject(), moduleTargetBytecodeLevel) ? moduleTargetBytecodeLevel : null;
                    }

                });
                convention.map("languageLevel", new Callable() {
                    @Override
                    public IdeaLanguageLevel call() throws Exception {
                        IdeaLanguageLevel moduleLanguageLevel = new IdeaLanguageLevel(project.getConvention().getPlugin(JavaPluginConvention.class).getSourceCompatibility());
                        return includeModuleLanguageLevelOverride(project.getRootProject(), moduleLanguageLevel) ? moduleLanguageLevel : null;
                    }

                });
                // Dependencies
                ideaModule.dependsOn(new Callable() {
                    @Override
                    public FileCollection call() throws Exception {
                        SourceSetContainer sourceSets = project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets();
                        return sourceSets.getByName("main").getOutput().getDirs().plus(sourceSets.getByName("test").getOutput().getDirs());
                    }

                });
            }

        });
    }

    private void setupScopes(GenerateIdeaModule ideaModule) {
        Map>> scopes = Maps.newLinkedHashMap();
        for (GeneratedIdeaScope scope : GeneratedIdeaScope.values()) {
            Map> plusMinus = Maps.newLinkedHashMap();
            plusMinus.put(IdeaDependenciesProvider.SCOPE_PLUS, Lists.newArrayList());
            plusMinus.put(IdeaDependenciesProvider.SCOPE_MINUS, Lists.newArrayList());
            scopes.put(scope.name(), plusMinus);
        }

        Project project = ideaModule.getProject();
        ConfigurationContainer configurations = project.getConfigurations();

        Collection provided = scopes.get(GeneratedIdeaScope.PROVIDED.name()).get(IdeaDependenciesProvider.SCOPE_PLUS);
        provided.add(configurations.getByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME));

        Collection runtime = scopes.get(GeneratedIdeaScope.RUNTIME.name()).get(IdeaDependenciesProvider.SCOPE_PLUS);
        runtime.add(configurations.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME));

        Collection test = scopes.get(GeneratedIdeaScope.TEST.name()).get(IdeaDependenciesProvider.SCOPE_PLUS);
        test.add(configurations.getByName(JavaPlugin.TEST_COMPILE_CLASSPATH_CONFIGURATION_NAME));
        test.add(configurations.getByName(JavaPlugin.TEST_RUNTIME_CLASSPATH_CONFIGURATION_NAME));

        ideaModule.getModule().setScopes(scopes);
    }

    private void configureIdeaModuleForWar(final Project project) {
        project.getTasks().withType(GenerateIdeaModule.class, new Action() {
            @Override
            public void execute(GenerateIdeaModule ideaModule) {
                ConfigurationContainer configurations = project.getConfigurations();
                Configuration providedRuntime = configurations.getByName(WarPlugin.PROVIDED_RUNTIME_CONFIGURATION_NAME);
                Collection providedPlus = ideaModule.getModule().getScopes().get(GeneratedIdeaScope.PROVIDED.name()).get(IdeaDependenciesProvider.SCOPE_PLUS);
                providedPlus.add(providedRuntime);
                Collection runtimeMinus = ideaModule.getModule().getScopes().get(GeneratedIdeaScope.RUNTIME.name()).get(IdeaDependenciesProvider.SCOPE_MINUS);
                runtimeMinus.add(providedRuntime);
                Collection testMinus = ideaModule.getModule().getScopes().get(GeneratedIdeaScope.TEST.name()).get(IdeaDependenciesProvider.SCOPE_MINUS);
                testMinus.add(providedRuntime);
            }
        });
    }

    private static boolean includeModuleBytecodeLevelOverride(Project rootProject, JavaVersion moduleTargetBytecodeLevel) {
        if (!rootProject.getPlugins().hasPlugin(IdeaPlugin.class)) {
            return true;
        }

        IdeaProject ideaProject = ideaModelFor(rootProject).getProject();
        return !moduleTargetBytecodeLevel.equals(ideaProject.getTargetBytecodeVersion());
    }

    private static boolean includeModuleLanguageLevelOverride(Project rootProject, IdeaLanguageLevel moduleLanguageLevel) {
        if (!rootProject.getPlugins().hasPlugin(IdeaPlugin.class)) {
            return true;
        }

        IdeaProject ideaProject = ideaModelFor(rootProject).getProject();
        return !moduleLanguageLevel.equals(ideaProject.getLanguageLevel());
    }

    private void configureForScalaPlugin() {
        project.getPlugins().withType(ScalaBasePlugin.class, new Action() {
            @Override
            public void execute(ScalaBasePlugin scalaBasePlugin) {
                ideaModuleDependsOnRoot();
            }
        });
        project.getPlugins().withType(ScalaLanguagePlugin.class, new Action() {
            @Override
            public void execute(ScalaLanguagePlugin scalaLanguagePlugin) {
                ideaModuleDependsOnRoot();
            }

        });
        if (isRoot(project)) {
            new IdeaScalaConfigurer(project).configure();
        }
    }

    private void ideaModuleDependsOnRoot() {
        // see IdeaScalaConfigurer which requires the ipr to be generated first
        project.getTasks().findByName("ideaModule").dependsOn(project.getRootProject().getTasks().findByName("ideaProject"));
    }

    private static boolean isRoot(Project project) {
        return project.getParent() == null;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy