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

org.gradle.plugins.ide.idea.model.IdeaModule Maven / Gradle / Ivy

There is a newer version: 8.11.1
Show newest version
/*
 * Copyright 2011 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.model;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import groovy.lang.Closure;
import org.gradle.api.Action;
import org.gradle.api.Incubating;
import org.gradle.api.JavaVersion;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.internal.project.ProjectInternal;
import org.gradle.language.scala.ScalaPlatform;
import org.gradle.plugins.ide.idea.model.internal.IdeaDependenciesProvider;
import org.gradle.plugins.ide.internal.resolver.UnresolvedDependenciesLogger;

import java.io.File;
import java.util.Collection;
import java.util.Map;
import java.util.Set;

import static org.gradle.util.ConfigureUtil.configure;

/**
 * Enables fine-tuning module details (*.iml file) of the IDEA plugin.
 * 

* Example of use with a blend of most possible properties. * Typically you don't have to configure this model directly because Gradle configures it for you. * *

 * apply plugin: 'java'
 * apply plugin: 'idea'
 *
 * //for the sake of this example, let's introduce a 'performanceTestCompile' configuration
 * configurations {
 *   performanceTestCompile
 *   performanceTestCompile.extendsFrom(testCompile)
 * }
 *
 * dependencies {
 *   //performanceTestCompile "some.interesting:dependency:1.0"
 * }
 *
 * idea {
 *
 *   //if you want parts of paths in resulting files (*.iml, etc.) to be replaced by variables (Files)
 *   pathVariables GRADLE_HOME: file('~/cool-software/gradle')
 *
 *   module {
 *     //if for some reason you want to add an extra sourceDirs
 *     sourceDirs += file('some-extra-source-folder')
 *
 *     //and some extra test source dirs
 *     testSourceDirs += file('some-extra-test-dir')
 *
 *     //and hint to mark some of existing source dirs as generated sources
 *     generatedSourceDirs += file('some-extra-source-folder')
 *
 *     //and some extra dirs that should be excluded by IDEA
 *     excludeDirs += file('some-extra-exclude-dir')
 *
 *     //if you don't like the name Gradle has chosen
 *     name = 'some-better-name'
 *
 *     //if you prefer different output folders
 *     inheritOutputDirs = false
 *     outputDir = file('muchBetterOutputDir')
 *     testOutputDir = file('muchBetterTestOutputDir')
 *
 *     //if you prefer different SDK than the one inherited from IDEA project
 *     jdkName = '1.6'
 *
 *     //put our custom test dependencies onto IDEA's TEST scope
 *     scopes.TEST.plus += [ configurations.performanceTestCompile ]
 *
 *     //if 'content root' (as IDEA calls it) of the module is different
 *     contentRoot = file('my-module-content-root')
 *
 *     //if you love browsing Javadoc
 *     downloadJavadoc = true
 *
 *     //and hate reading sources :)
 *     downloadSources = false
 *   }
 * }
 * 
* * For tackling edge cases, users can perform advanced configuration on the resulting XML file. * It is also possible to affect the way the IDEA plugin merges the existing configuration * via beforeMerged and whenMerged closures. *

* beforeMerged and whenMerged closures receive a {@link Module} parameter *

* Examples of advanced configuration: * *

 * apply plugin: 'java'
 * apply plugin: 'idea'
 *
 * idea {
 *   module {
 *     iml {
 *       //if you like to keep *.iml in a secret folder
 *       generateTo = file('secret-modules-folder')
 *
 *       //if you want to mess with the resulting XML in whatever way you fancy
 *       withXml {
 *         def node = it.asNode()
 *         node.appendNode('iLoveGradle', 'true')
 *         node.appendNode('butAlso', 'I find increasing pleasure tinkering with output *.iml contents. Yeah!!!')
 *       }
 *
 *       //closure executed after *.iml content is loaded from existing file
 *       //but before gradle build information is merged
 *       beforeMerged { module ->
 *         //if you want skip merging exclude dirs
 *         module.excludeFolders.clear()
 *       }
 *
 *       //closure executed after *.iml content is loaded from existing file
 *       //and after gradle build information is merged
 *       whenMerged { module ->
 *         //you can tinker with {@link Module}
 *       }
 *     }
 *   }
 * }
 *
 * 
*/ public class IdeaModule { private String name; private Set sourceDirs; private Set generatedSourceDirs = Sets.newLinkedHashSet(); private Map>> scopes = Maps.newLinkedHashMap(); private boolean downloadSources = true; private boolean downloadJavadoc; private File contentRoot; private Set testSourceDirs; private Set excludeDirs; private Boolean inheritOutputDirs; private File outputDir; private File testOutputDir; private Map pathVariables = Maps.newLinkedHashMap(); private String jdkName; private IdeaLanguageLevel languageLevel; private JavaVersion targetBytecodeVersion; private ScalaPlatform scalaPlatform; private final IdeaModuleIml iml; private final Project project; private PathFactory pathFactory; private boolean offline; private Map> singleEntryLibraries; public IdeaModule(Project project, IdeaModuleIml iml) { this.project = project; this.iml = iml; } /** * Configures module name, that is the name of the *.iml file. *

* It's optional because the task should configure it correctly for you. * By default it will try to use the project.name or prefix it with a part of a project.path to make * sure the module name is unique in the scope of a multi-module build. * The 'uniqueness' of a module name is required for correct import into IDEA and the task will make sure the name * is unique. *

* since 1.0-milestone-2 *

* If your project has problems with unique names it is recommended to always run gradle idea from the * root, i.e. for all subprojects. * If you run the generation of the IDEA module only for a single subproject then you may have different results * because the unique names are calculated based on IDEA modules that are involved in the specific build run. *

* If you update the module names then make sure you run gradle idea from the root, e.g. for all * subprojects, including generation of IDEA project. * The reason is that there may be subprojects that depend on the subproject with amended module name. * So you want them to be generated as well because the module dependencies need to refer to the amended project * name. * Basically, for non-trivial projects it is recommended to always run gradle idea from the root. *

* For example see docs for {@link IdeaModule} */ public String getName() { return name; } public void setName(String name) { this.name = name; } /** * The directories containing the production sources. *

* For example see docs for {@link IdeaModule} */ public Set getSourceDirs() { return sourceDirs; } public void setSourceDirs(Set sourceDirs) { this.sourceDirs = sourceDirs; } /** * The directories containing the generated sources (both production and test sources). *

* For example see docs for {@link IdeaModule} */ @Incubating public Set getGeneratedSourceDirs() { return generatedSourceDirs; } @Incubating public void setGeneratedSourceDirs(Set generatedSourceDirs) { this.generatedSourceDirs = generatedSourceDirs; } /** * The keys of this map are the IDEA scopes. Each key points to another map that has two keys, plus and minus. * The values of those keys are collections of {@link org.gradle.api.artifacts.Configuration} objects. The files of the * plus configurations are added minus the files from the minus configurations. See example below... *

* Example how to use scopes property to enable 'performanceTestCompile' dependencies in the output *.iml file: *

     * apply plugin: 'java'
     * apply plugin: 'idea'
     *
     * configurations {
     *   performanceTestCompile
     *   performanceTestCompile.extendsFrom(testCompile)
     * }
     *
     * dependencies {
     *   //performanceTestCompile "some.interesting:dependency:1.0"
     * }
     *
     * idea {
     *   module {
     *     scopes.TEST.plus += [ configurations.performanceTestCompile ]
     *   }
     * }
     * 
*/ public Map>> getScopes() { return scopes; } public void setScopes(Map>> scopes) { this.scopes = scopes; } /** * Whether to download and add sources associated with the dependency jars.

For example see docs for {@link IdeaModule} */ public boolean isDownloadSources() { return downloadSources; } public void setDownloadSources(boolean downloadSources) { this.downloadSources = downloadSources; } /** * Whether to download and add javadoc associated with the dependency jars.

For example see docs for {@link IdeaModule} */ public boolean isDownloadJavadoc() { return downloadJavadoc; } public void setDownloadJavadoc(boolean downloadJavadoc) { this.downloadJavadoc = downloadJavadoc; } /** * The content root directory of the module.

For example see docs for {@link IdeaModule} */ public File getContentRoot() { return contentRoot; } public void setContentRoot(File contentRoot) { this.contentRoot = contentRoot; } /** * The directories containing the test sources.

For example see docs for {@link IdeaModule} */ public Set getTestSourceDirs() { return testSourceDirs; } public void setTestSourceDirs(Set testSourceDirs) { this.testSourceDirs = testSourceDirs; } /** * {@link org.gradle.api.dsl.ConventionProperty} for the directories to be excluded.

For example see docs for {@link IdeaModule} */ public Set getExcludeDirs() { return excludeDirs; } public void setExcludeDirs(Set excludeDirs) { this.excludeDirs = excludeDirs; } /** * If true, output directories for this module will be located below the output directory for the project; * otherwise, they will be set to the directories specified by {@link #getOutputDir()} and {@link #getTestOutputDir()}. *

* For example see docs for {@link IdeaModule} */ public Boolean getInheritOutputDirs() { return inheritOutputDirs; } public void setInheritOutputDirs(Boolean inheritOutputDirs) { this.inheritOutputDirs = inheritOutputDirs; } /** * The output directory for production classes. * If {@code null}, no entry will be created. *

* For example see docs for {@link IdeaModule} */ public File getOutputDir() { return outputDir; } public void setOutputDir(File outputDir) { this.outputDir = outputDir; } /** * The output directory for test classes. * If {@code null}, no entry will be created. *

* For example see docs for {@link IdeaModule} */ public File getTestOutputDir() { return testOutputDir; } public void setTestOutputDir(File testOutputDir) { this.testOutputDir = testOutputDir; } /** * The variables to be used for replacing absolute paths in the iml entries. * For example, you might add a {@code GRADLE_USER_HOME} variable to point to the Gradle user home dir. *

* For example see docs for {@link IdeaModule} */ public Map getPathVariables() { return pathVariables; } public void setPathVariables(Map pathVariables) { this.pathVariables = pathVariables; } /** * The JDK to use for this module. * If {@code null}, the value of the existing or default ipr XML (inherited) is used. * If it is set to inherited, the project SDK is used. * Otherwise the SDK for the corresponding value of java version is used for this module. *

* For example see docs for {@link IdeaModule} */ public String getJdkName() { return jdkName; } public void setJdkName(String jdkName) { this.jdkName = jdkName; } /** * The module specific language Level to use for this module. * When {@code null}, the module will inherit the language level from the idea project. *

* The Idea module language level is based on the {@code sourceCompatibility} settings for the associated Gradle project. */ @Incubating public IdeaLanguageLevel getLanguageLevel() { return languageLevel; } @Incubating public void setLanguageLevel(IdeaLanguageLevel languageLevel) { this.languageLevel = languageLevel; } /** * The module specific bytecode version to use for this module. * When {@code null}, the module will inherit the bytecode version from the idea project. *

* The Idea module bytecode version is based on the {@code targetCompatibility} settings for the associated Gradle project. */ @Incubating public JavaVersion getTargetBytecodeVersion() { return targetBytecodeVersion; } @Incubating public void setTargetBytecodeVersion(JavaVersion targetBytecodeVersion) { this.targetBytecodeVersion = targetBytecodeVersion; } /** * The Scala version used by this module. */ @Incubating public ScalaPlatform getScalaPlatform() { return scalaPlatform; } @Incubating public void setScalaPlatform(ScalaPlatform scalaPlatform) { this.scalaPlatform = scalaPlatform; } /** * See {@link #iml(Action)} */ public IdeaModuleIml getIml() { return iml; } /** * An owner of this IDEA module. *

* If IdeaModule requires some information from gradle this field should not be used for this purpose. * IdeaModule instances should be configured with all necessary information by the plugin or user. */ public Project getProject() { return project; } public PathFactory getPathFactory() { return pathFactory; } public void setPathFactory(PathFactory pathFactory) { this.pathFactory = pathFactory; } /** * If true then external artifacts (e.g. those found in repositories) will not be included in the resulting classpath (only project and local file dependencies will be included). */ public boolean isOffline() { return offline; } public void setOffline(boolean offline) { this.offline = offline; } public Map> getSingleEntryLibraries() { return singleEntryLibraries; } public void setSingleEntryLibraries(Map> singleEntryLibraries) { this.singleEntryLibraries = singleEntryLibraries; } /** * Enables advanced configuration like tinkering with the output XML or affecting the way existing *.iml content is merged with gradle build information. *

* For example see docs for {@link IdeaModule}. */ public void iml(Closure closure) { configure(closure, getIml()); } /** * Enables advanced configuration like tinkering with the output XML or affecting the way existing *.iml content is merged with gradle build information. *

* For example see docs for {@link IdeaModule}. * * @since 3.5 */ public void iml(Action action) { action.execute(getIml()); } /** * Configures output *.iml file. * It's optional because the task should configure it correctly for you (including making sure it is unique in the multi-module build). * If you really need to change the output file name (or the module name) it is much easier to do it via the moduleName property! *

* Please refer to documentation on moduleName property. * In IntelliJ IDEA the module name is the same as the name of the *.iml file. */ public File getOutputFile() { return new File(iml.getGenerateTo(), getName() + ".iml"); } public void setOutputFile(File newOutputFile) { setName(newOutputFile.getName().replaceFirst("\\.iml$", "")); iml.setGenerateTo(newOutputFile.getParentFile()); } /** * Resolves and returns the module's dependencies. * * @return dependencies */ public Set resolveDependencies() { ProjectInternal projectInternal = (ProjectInternal) project; IdeaDependenciesProvider ideaDependenciesProvider = new IdeaDependenciesProvider(projectInternal.getServices()); new UnresolvedDependenciesLogger().log(ideaDependenciesProvider.getUnresolvedDependencies(this)); return ideaDependenciesProvider.provide(this); } public void mergeXmlModule(Module xmlModule) { iml.getBeforeMerged().execute(xmlModule); Path contentRoot = getPathFactory().path(getContentRoot()); Set sourceFolders = pathsOf(existing(getSourceDirs())); Set generatedSourceFolders = pathsOf(existing(getGeneratedSourceDirs())); Set testSourceFolders = pathsOf(existing(getTestSourceDirs())); Set excludeFolders = pathsOf(getExcludeDirs()); Path outputDir = getOutputDir() != null ? getPathFactory().path(getOutputDir()) : null; Path testOutputDir = getTestOutputDir() != null ? getPathFactory().path(getTestOutputDir()) : null; Set dependencies = resolveDependencies(); String level = getLanguageLevel() != null ? getLanguageLevel().getLevel() : null; xmlModule.configure( contentRoot, sourceFolders, testSourceFolders, generatedSourceFolders, excludeFolders, getInheritOutputDirs(), outputDir, testOutputDir, dependencies, getJdkName(), level ); iml.getWhenMerged().execute(xmlModule); } private Set existing(Set files) { return Sets.filter(files, new Predicate() { @Override public boolean apply(File file) { return file.exists(); } }); } private Set pathsOf(Set files) { return Sets.newLinkedHashSet(Iterables.transform(files, new Function() { @Override public Path apply(File file) { return getPathFactory().path(file); } })); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy