This spec defines a number of features to improve the developer experience using Gradle from the IDE. It covers changes in Gradle to improve those features that directly affect the IDE user experience. This includes the tooling API and tooling models, the Gradle daemon, and the Gradle IDE plugins (i.e. the plugins that run inside Gradle to provide the IDE models).
Tooling API stories that are not related directly to the IDE experience should go in the tooling-api-improvements.md
spec.
Spec moved to [features/ide-integration/source-and-target-jvm].
- 3.5 days
-
add new
JavaProject
to model IDE agnostic Java Projects -
add details about Java Source level to the
JavaModel
- should be based on projects
sourceCompatibility
level
- should be based on projects
-
add details about dependencies to the project
- project and external dependencies
-
add details about source folders to the
JavaProject
model -
add details of the target JVM:
- should be based on projects
targetCompatibility
level - Java version
- Install directory
- should be based on projects
-
TBD: Classpath
-
For older Gradle versions:
- TBD - reasonable defaults for Java language version
-
Query
JavaProject
for older Gradle providers throws meaningful error message -
Query
JavaProject
for non java projects fails with meaningful error message -
JavaProject
model for multi project build contains information about- source folders
- thirdparty dependencies
- project dependencies
- target sdk
- source compatibility
-
respects customizations of sourcesets
- additional sourcesets
- additional sourcefolders to sourcesets are respected
-
TBD: provide test and runtime classpath
TBD
This feature exposes via the tooling API some task execution and reporting features that are currently available on the command-line.
- Task selection by name, where I can run
gradle test
and this will find and execute all tasks with nametest
in the current project and its subprojects. However, this selection logic does have some special cases. For example, when I rungradle help
orgradle tasks
, then the task from the current project only is executed, and no subproject tasks are executed. - Task reporting, where I can run
gradle tasks
and this will show me the things I can run from the current project. This report, by default, shows only the public interface tasks and hides the implementation tasks.
The first part of this feature involves extracting the logic for task selection and task reporting into some reusable service, that is used for command-line invocation and exposed through the tooling API. This way, both tools and the command-line use the same consistent logic.
The second part of this feature involves some improvements to the task reporting, to simplify it, improve performance and to integrate with the component model introduced by the new language plugins:
- Task reporting treats as public any task with a non-empty
group
attribute, or any task that is declared as a public task of a build element. - All other tasks are treated as private.
- This is a breaking change. Previously, the reporting logic used to analyse the task dependency graph and treated any task which was not a dependency of some other task as a public task. This is very slow, requires every task in every project to be configured, and is not particularly accurate.
- Split
gradle tasks
into 'what can I do with this build?' and a 'what are all the tasks in this project?'.
Change the task visibility logic introduced in the previous stories so that:
- A task is public if its
group
attribute is not empty. - A task selector is public if any of the tasks that it selects are public.
- Tooling API and
gradle tasks
treats as public a task with a non-emptygroup
attribute. - Tooling API and
gradle tasks
treats as public a selector that selects a public task. - Tooling API and
gradle tasks
treats as private a task with nullgroup
attribute. - Tooling API and
gradle tasks
treats as private a selector that selects only private tasks.
Change the task visibility logic so that the lifecycle task of all BuildableModelElement
objects are public, regardless of their group attribute.
- Tooling API and
gradle tasks
treats as public the lifecycle task of aBuildableModelElement
with a nullgroup
attribute.
- Should other tasks for a
BuildableModelElement
be private, regardless of group attribute?
A new model will be available for defining IDEA scopes: these scopes will formed by combining and excluding Gradle configurations and other scopes. The model can statically restrict the available scopes to 'compile', 'runtime', 'provided' and 'test'.
Introduce a model for binaries that run on the JVM and that are built locally:
interface Classpath { // This type already exists
...
libs(Object) // Adds libraries to this classpath
}
interface JvmPlatform {
Classpath compile
Classpath runtime
}
interface JvmBinary {
JvmPlatform platform
Classpath compile
Classpath runtime
}
interface MainJvmBinary extends JvmBinary {
JvmBinary tests
}
The Java base plugin registers an implementation of MainJvmBinary
, and the Java and WAR plugins fill it in so that:
configurations.compile
is added tojvmBinary.compile
configurations.runtime
is added tojvmBinary.runtime
configurations.testCompile
is added tojvmBinary.test.compile
configurations.testRuntime
is added tojvmBinary.test.runtime
configurations.providedCompile
is added tojvmBinary.platform.compile
configurations.providedRuntime
is added tojvmBinary.platform.runtime
Introduce IDEA scope model:
interface IdeaScope {
String name
List<Dependency> libraries // read-only, calculated on demand
}
interface IdeaScopes {
IdeaScope provided
IdeaScope compile
IdeaScope runtime
IdeaScope test
}
class IdeaModule { // this type already exists
...
IdeaScopes ideaScopes
}
The IDEA plugin calculates the contents of the scopes based on the JvmBinary
model defined above:
- scope
provided
should containjvmBinary.platform.compile
- scope
compile
should containjvmBinary.compile
minus scopeprovided
. - scope
runtime
should contain (jvmBinary.runtime
unionjvmBinary.platform.runtime
) minus scopecompile
. - scope
test
should contain (jvmBinary.test.compile
minus scopecompile
) union (jvmBinary.test.runtime
minus scoperuntime
).
An example customisation:
binaries {
jvm {
test.compile configurations.integTestCompile
test.runtime configurations.integTestRuntime
platform.compile configurations.myCustomProvided
}
}
TODO: Example DSL
The new DSL (and model defaults) will be used to configure an IDEA module when the current scopes
map has not been altered by the user.
Once the new DSL is stabilised we will deprecate and remove the scopes
map.
- Add
ResolvableComponents
which represents a set of requirements that can be resolved into a graph or set ofComponents
- Add an implementation of
ResolvableComponents
that wraps aConfiguration
instance - Add an implementation of
ResolvableComponents
that can combine otherResolvableComponents
as required for the Idea scope DSL - Update each method on
IdeDependenciesExtractor
so that it takes aResolvableComponents
parameter in place of the currentCollection<Configuration> plusConfigurations, Collection<Configuration> minusConfigurations
.- For now, these methods can simply unpack the
ResolvableComponents
to a set of added and excluded configurations. - The current scope map DSL should be mapped to the new API
- For now, these methods can simply unpack the
- Add domain classes as required for the new Idea scopes DSL, with appropriate defaults.
- When using the tooling API to fetch the IDEA model or the
idea
task to generate the IDEA project files, verify that:- When a java project has a dependency declared for
testCompile
andruntime
, the dependency appears withtest
andruntime
scopes only. - When a java project has a dependency declared for
testRuntime
andruntime
, the dependency appears withruntime
scope only. - When a java project has a dependency declared for
compile
andtestCompile
, the dependency appears withcompile
scope only. - When a war project has a dependency declared for
providedCompile
andcompile
, the dependency appears withprovided
scope only.
- When a java project has a dependency declared for
- Current defaults are used when the
scopes
maps is configured by user. - User is informed of failure when both
scopes
map and new DSL are configured.
- Similar new DSL for the Eclipse model (except that it’s much simpler).
- ArtifactResolutionQuery API takes a
ResolvableComponents
as input- Include JvmLibrary main artifact in query results
- Replace
IdeDependenciesExtractor.extractRepoFileDependencies
with single ArtifactResolutionQuery
- For the following kinds of failures:
- Missing or broken module version
- Missing or broken jar
- Missing or broken source and javadoc artifact
- Change the IDE plugins to warn for each such problem it ignores, and fail on all others.
- Change the tooling model include the failure details for each such problem.
- Change the existing IDE plugin int tests to verify the warning is produced in each of the above cases.
- Add test coverage for the tooling API to cover the above cases.
This feature exposes some information about how a build script will be compiled. This information can be used by an IDE to provide some basic content assistance for a build script.
Add a groovyVersion
property to GradleScript
to expose the Groovy version that is used.
Add a defaultImports
property to GradleScript
to expose the default imports applied to the script.
- Introduce a new hierarchy to represent a classpath element. Retrofit the IDEA and Eclipse models to use this.
- Should expose a set of files, a set of source archives and a set of API docs.
- Add
compileClasspath
property toGradleScript
to expose the build script classpath. - Script classpath includes the Gradle API and core plugins
- Should include the source and Javadoc
- Script classpath includes the libraries declared in the
buildscript { }
block. - Script classpath includes the plugins declared in the
plugins { }
block. - Script classpath includes the libraries inherited from parent project.
- Add a new
ToolingApiSpecification
integration test class that covers:- Gradle API is included in the classpath.
- buildSrc output is included in the classpath, if present.
- Classpath declared in script is included in the classpath.
- Classpath declared in script of ancestor project is included in the classpath.
- Source and Javadoc artifacts for the above are included in the classpath.
- Verify that a decent error message is received when using a Gradle version that does not expose the build script classpath.
- Need to flesh out the classpath types.
- Will need to use Eclipse and IDEA specific classpath models
Add a way to take a file path and request a BuildScript
model for it.
Add the appropriate properties to the IDEA and Eclipse models.
Expose Scala language level and other details about a Scala component.
Expose the corresponding Eclipse and IDEA model.
Expose Web content, servlet API version, web.xml descriptor, runtime and container classpaths, and other details about a web application.
Expose the corresponding Eclipse and IDEA model.
Expose Ear content, J2EE API versions, deployment descriptor and other details about a J2EE application.
Expose the corresponding Eclipse and IDEA model.
It is useful for IDEs to know which directories are generated by the build. An initial approximation can be to expose
just the build directory and the .gradle
directory. This can be improved later.
Expose details to allow IDEA to build various artifacts: http://www.jetbrains.com/idea/webhelp/configuring-artifacts.html
A couple of versions ago, IntelliJ introduced the directory-based project format. The file-based project format is considered to be deprecated. In the long run, the file-based project will be taken out of action. The Gradle Idea plugin only allows for generating file-based project files. Sooner or later Gradle will need to be able to generate the directory-based project format.
From a user's perspective two different formats are confusing. If you start by importing a Gradle project with IntelliJ's built-in capabilities, the default project format is directory-based (though it is configurable). Any customization of the project settings that are part of the build logic (implemented with the help of the Gradle Idea plugin) will only work properly if the user made sure to select the file-based format during the initial import.
The goal would be to make the directory-based project format the default format when generating project files with the Gradle Idea plugin. Optionally, allow users to pick between file- and directory- based project generation. Some users might never upgrade to a version of IntelliJ that already supports the directory-based format.
From the IntelliJ documentation:
When the directory-based format is used, there is a
.idea
directory in the project directory.
The
.idea
directory contains a set of configuration files (.xml). Each file contains only a portion of configuration data pertaining to a certain functional area which is reflected in the name of a file, for example,compiler.xml
,encodings.xml
,modules.xml
.
Given that the existing logical structure of projects (project, module, workspace) is broken up into different, dedicated files might be a good indicator that the new functionality should live in a new plugin. This fact will help with introducing a different DSL for directory-based configuration. User can dedicated decide whether they prefer the old or new project format by picking a specific plugin.
- Creating a new plugin alongside the existing Idea plugin with the identifier
idea-directory
. We might want to think about moving the file-based plugin into a new plugin namedidea-file
. The existingidea
plugin could act as an aggregation plugin that determines which format to pick based on existing project files in the workspace. A descriptive plugin identifier will be key. - Implement the same functionality we already have in place for file-based project generation for directory-based generation.
- Introduce an new extension for directory-based project file generation. The exposed DSL elements depend on how much we want to abstract the logical structure. The alternative is to expose an element per configuration file. The DSL of the old and new format may not conflict.
- Existing test cases for the file-based format pass.
- Build a similar set of test cases for the directory-based format.
- If no existing project metadata is found in the working directory, generate the directory-based format.
- If existing project metadata is found, regenerating project files uses the existing format.
- Based on user input, a specific format can be picked for project generation.
- Can we keep the existing logical structure of projects (project, module, workspace) and use the same abstraction for the directory-based project file generation? Introducing a new DSL might be confusing to users.
- Is there any feature parity between the file-based and directory-based project formats?
- There's no overarching, publicly-available documentation or specification on directory-based project files. It might be worth to contact JetBrains for a good resource.
Some more features to mix into the above plan:
IdeaSingleEntryLibraryDependency
should expose multiple source or javadoc artifacts.- Honour same environment variables as command-line
gradle
invocation. - Richer events during execution:
- Custom events
- Richer build results:
- Test results
- Custom results
- Compilation and other verification failures
- Task failures
- Build configuration failures
- Expose unresolved dependencies.
- Expose dependency graph.
- Expose component graph.
- Provide some way to search repositories, to offer content assistance with dependency notations.
- Don't configure the projects when
GradleBuild
model is requested. - Configure projects as required when using configure-on-demand.
- Don't configure tasks when they are not requested.
- Deal with non-standard wrapper meta-data location.
- More accurate content assistance.
- User provides input to build execution.
- User edits dependencies via some UI.
- User upgrades Gradle version via some UI.
- User creates a new build via some UI.
- Provide some way to define additional JVM args and system properties (possibly via command-line args)
- Provide some way to locate Gradle builds, eg to allow the user to select which build to import.