Skip to content

Commit

Permalink
Improve IntelliJ IDE integration (#53747)
Browse files Browse the repository at this point in the history
This commit makes a number of improvements when importing the
Elasticsearch project into IntelliJ IDEA. Specifically:

 - Contributing documentation has been updated to reflect that the
   'idea' task should no long be used and Gradle project import is
   instead the officially supported way of setting up the project.
 - Attempts to run the 'idea' task will result in a failure with a
   message directing folks to our CONTRIBUTING.md document.
 - The project JDK is explicit set rather that using whatever JAVA_HOME
   is.
 - Gradle build operation delegation is disabled, and test execution is
   configured to 'choose per test'.
 - Gradle is configured to inherit the project JDK.
 - Some code style conventions are automatically configured.
 - File encoding is explicitly set to UTF-8.
 - Parallel module compilation is enabled and deprecated feature
   warnings are disabled.
 - A remote debug run configuration using listen mode is created.
 - JUnit runner is configured with required system properties.
 - License headers are configured such that Apache 2 is the default
   notice added to all source files with exception of source in /x-pack
   which will use the Elastic license.

(cherry picked from commit 2e617c3)
  • Loading branch information
mark-vieira committed Mar 19, 2020
1 parent 3ba9b6a commit 0d66ab2
Show file tree
Hide file tree
Showing 9 changed files with 184 additions and 110 deletions.
10 changes: 9 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@
*.ipr
*.iws
build-idea/
out/

# include shared intellij config
!.idea/scopes/x_pack.xml
!.idea/inspectionProfiles/Project_Default.xml

# These files are generated in the main tree by IntelliJ
benchmarks/src/main/generated/*

# eclipse files
.project
Expand Down Expand Up @@ -42,7 +50,7 @@ html_docs
/tmp/
eclipse-build

# projects using testfixtures
# projects using testfixtures
testfixtures_shared/

# These are generated from .ci/jobs.t
Expand Down
9 changes: 9 additions & 0 deletions .idea/inspectionProfiles/Project_Default.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions .idea/scopes/x_pack.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

72 changes: 17 additions & 55 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,45 +109,24 @@ and `JAVA11_HOME` available so that the tests can pass.
Elasticsearch uses the Gradle wrapper for its build. You can execute Gradle
using the wrapper via the `gradlew` script in the root of the repository.

We support development in the Eclipse and IntelliJ IDEs.
For Eclipse, the minimum version that we support is [4.13][eclipse].
For IntelliJ, the minimum version that we support is [IntelliJ 2017.2][intellij].

Eclipse users can automatically configure their IDE: `./gradlew eclipse`
then `File: Import: Gradle : Existing Gradle Project`.
Additionally you will want to ensure that Eclipse is using 2048m of heap by modifying
`eclipse.ini` accordingly to avoid GC overhead and OOM errors.

IntelliJ users can automatically configure their IDE: `./gradlew idea`
then `File->New Project From Existing Sources`. Point to the root of
the source directory, select
`Import project from external model->Gradle`, enable
`Use auto-import`. In order to run tests directly from
IDEA 2017.2 and above, it is required to disable the IDEA run launcher in order to avoid
`idea_rt.jar` causing "jar hell". This can be achieved by adding the
`-Didea.no.launcher=true` [JVM
option](https://intellij-support.jetbrains.com/hc/en-us/articles/206544869-Configuring-JVM-options-and-platform-properties).
Alternatively, `idea.no.launcher=true` can be set in the
[`idea.properties`](https://www.jetbrains.com/help/idea/file-idea-properties.html)
file which can be accessed under Help > Edit Custom Properties (this will require a
restart of IDEA). For IDEA 2017.3 and above, in addition to the JVM option, you will need to go to
`Run->Edit Configurations->...->Defaults->JUnit` and verify that the `Shorten command line` setting is set to
`user-local default: none`. You may also need to [remove `ant-javafx.jar` from your
classpath](https://github.com/elastic/elasticsearch/issues/14348) if that is
reported as a source of jar hell.
We support development in IntelliJ versions IntelliJ 2019.2 and
onwards. We would like to support Eclipse, but few of us use it and has fallen
into [disrepair][eclipse].

To run an instance of elasticsearch from the source code run `./gradlew run`
### Importing the project into IntelliJ IDEA

Elasticsearch builds using Java 12. Before importing into IntelliJ you will need
to define an appropriate SDK. The convention is that **this SDK should be named
"12"** so that the project import will detect it automatically. For more details
on defining an SDK in IntelliJ please refer to [their documentation](https://www.jetbrains.com/help/idea/sdk.html#define-sdk).

The Elasticsearch codebase makes heavy use of Java `assert`s and the
test runner requires that assertions be enabled within the JVM. This
can be accomplished by passing the flag `-ea` to the JVM on startup.
You can import the Elasticsearch project into IntelliJ IDEA via:

For IntelliJ, go to
`Run->Edit Configurations...->Defaults->JUnit->VM options` and input
`-ea`.
- Select **File > Open**
- In the subsequent dialog navigate to the root `build.gradle` file
- In the subsequent dialog select **Open as Project**

For Eclipse, go to `Preferences->Java->Installed JREs` and add `-ea` to
`VM Arguments`.
To run an instance of elasticsearch from the source code run `./gradlew run`

Please follow these formatting guidelines:

Expand Down Expand Up @@ -203,26 +182,9 @@ It is important that the only code covered by the Elastic licence is contained
within the top-level `x-pack` directory. The build will fail its pre-commit
checks if contributed code does not have the appropriate license headers.

You may find it helpful to configure your IDE to automatically insert the
appropriate license header depending on the part of the project to which you are
contributing.

#### IntelliJ: Copyright & Scope Profiles

To have IntelliJ insert the correct license, it is necessary to create to copyright profiles.
These may potentially be called `apache2` and `commercial`. These can be created in
`Preferences/Settings->Editor->Copyright->Copyright Profiles`. To associate these profiles to
their respective directories, two "Scopes" will need to be created. These can be created in
`Preferences/Settings->Appearances & Behavior->Scopes`. When creating scopes, be sure to choose
the `shared` scope type. Create a scope, `apache2`, with
the associated pattern of `!file[group:x-pack]:*/`. This pattern will exclude all the files contained in
the `x-pack` directory. The other scope, `commercial`, will have the inverse pattern of `file[group:x-pack]:*/`.
The two scopes, together, should account for all the files in the project. To associate the scopes
with their copyright-profiles, go into `Preferences/Settings->Editor>Copyright` and use the `+` to add
the associations `apache2/apache2` and `commercial/commercial`.

Configuring these options in IntelliJ can be quite buggy, so do not be alarmed if you have to open/close
the settings window and/or restart IntelliJ to see your changes take effect.
> **NOTE:** If you have imported the project into IntelliJ IDEA the project will
> be automatically configured to add the correct license header to new source
> files based on the source location.
### Creating A Distribution

Expand Down
6 changes: 3 additions & 3 deletions TESTING.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ Default value provided below in [brackets].

=== Load balancing and caches.

By default the tests run on multiple processes using all the available cores on all
By default the tests run on multiple processes using all the available cores on all
available CPUs. Not including hyper-threading.
If you want to explicitly specify the number of JVMs you can do so on the command
line:
Expand Down Expand Up @@ -555,7 +555,7 @@ version 5.3.2 run:
Tests are ran for versions that are not yet released but with which the current version will be compatible with.
These are automatically checked out and built from source.
See link:./buildSrc/src/main/java/org/elasticsearch/gradle/VersionCollection.java[VersionCollection]
and link:./distribution/bwc/build.gradle[distribution/bwc/build.gradle]
and link:./distribution/bwc/build.gradle[distribution/bwc/build.gradle]
for more information.

When running `./gradlew check`, minimal bwc checks are also run against compatible versions that are not yet released.
Expand Down Expand Up @@ -595,7 +595,7 @@ fetching the latest from the remote.
== Test coverage analysis

Generating test coverage reports for Elasticsearch is currently not possible through Gradle.
However, it _is_ possible to gain insight in code coverage using IntelliJ's built-in coverage
However, it _is_ possible to gain insight in code coverage using IntelliJ's built-in coverage
analysis tool that can measure coverage upon executing specific tests. Eclipse may also be able
to do the same using the EclEmma plugin.

Expand Down
54 changes: 10 additions & 44 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ plugins {
apply plugin: 'nebula.info-scm'
apply from: 'gradle/build-scan.gradle'
apply from: 'gradle/build-complete.gradle'
apply from: 'gradle/ide.gradle'

// common maven publishing configuration
allprojects {
Expand Down Expand Up @@ -133,10 +134,9 @@ allprojects {
project.ext {
// for ide hacks...
isEclipse = System.getProperty("eclipse.launcher") != null || // Detects gradle launched from Eclipse's IDE
System.getProperty("eclipse.application") != null || // Detects gradle launched from the Eclipse compiler server
gradle.startParameter.taskNames.contains('eclipse') || // Detects gradle launched from the command line to do eclipse stuff
gradle.startParameter.taskNames.contains('cleanEclipse')
isIdea = System.getProperty("idea.active") != null || gradle.startParameter.taskNames.contains('idea') || gradle.startParameter.taskNames.contains('cleanIdea')
System.getProperty("eclipse.application") != null || // Detects gradle launched from the Eclipse compiler server
gradle.startParameter.taskNames.contains('eclipse') || // Detects gradle launched from the command line to do eclipse stuff
gradle.startParameter.taskNames.contains('cleanEclipse')

// for BWC testing
bwcVersions = versions
Expand All @@ -150,8 +150,8 @@ task verifyVersions {
if (gradle.startParameter.isOffline()) {
throw new GradleException("Must run in online mode to verify versions")
}
// Read the list from maven central.
// Fetch the metadata an parse the xml into Version instances because it's more straight forward here
// Read the list from maven central.
// Fetch the metadata an parse the xml into Version instances because it's more straight forward here
// rather than bwcVersion ( VersionCollection ).
new URL('https://repo1.maven.org/maven2/org/elasticsearch/elasticsearch/maven-metadata.xml').openStream().withStream { s ->
bwcVersions.compareToAuthoritative(
Expand Down Expand Up @@ -397,40 +397,6 @@ gradle.projectsEvaluated {
}
}

// intellij configuration
allprojects {
apply plugin: 'idea'

if (isIdea) {
project.buildDir = file('build-idea')
}
idea {
module {
inheritOutputDirs = false
outputDir = file('build-idea/classes/main')
testOutputDir = file('build-idea/classes/test')

// also ignore other possible build dirs
excludeDirs += file('build')
excludeDirs += file('build-eclipse')
}
}

task cleanIdeaBuildDir(type: Delete) {
delete 'build-idea'
}
cleanIdeaBuildDir.setGroup("ide")
cleanIdeaBuildDir.setDescription("Deletes the IDEA build directory.")

tasks.cleanIdea.dependsOn(cleanIdeaBuildDir)
}

idea {
project {
vcs = 'Git'
}
}

// eclipse configuration
allprojects {
apply plugin: 'eclipse'
Expand Down Expand Up @@ -481,7 +447,7 @@ allprojects {
// otherwise the eclipse merging is *super confusing*
tasks.eclipse.dependsOn(cleanEclipse, copyEclipseSettings)

// work arround https://github.com/gradle/gradle/issues/6582
// work arround https://github.com/gradle/gradle/issues/6582
tasks.eclipseProject.mustRunAfter tasks.cleanEclipseProject
tasks.matching { it.name == 'eclipseClasspath' }.all {
it.mustRunAfter { tasks.cleanEclipseClasspath }
Expand All @@ -501,7 +467,7 @@ allprojects {
* *is* needed for projects that depends on the project doing the shadowing.
* Without this they won't properly depend on the shadowed project.
*/
if (isEclipse || isIdea) {
if (isEclipse) {
project.plugins.withType(ShadowPlugin).whenPluginAdded {
project.afterEvaluate {
project.configurations.compile.extendsFrom project.configurations.bundle
Expand Down Expand Up @@ -604,14 +570,14 @@ allprojects {

allprojects {
task checkPart1
task checkPart2
task checkPart2
tasks.matching { it.name == "check" }.all { check ->
if (check.path.startsWith(":x-pack:")) {
checkPart2.dependsOn check
} else {
checkPart1.dependsOn check
}
}
}
}


Expand Down
126 changes: 126 additions & 0 deletions gradle/ide.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import org.jetbrains.gradle.ext.Remote
import org.jetbrains.gradle.ext.JUnit

buildscript {
repositories {
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath "gradle.plugin.org.jetbrains.gradle.plugin.idea-ext:gradle-idea-ext:0.7"
}
}

apply plugin: org.jetbrains.gradle.ext.IdeaExtPlugin

allprojects {
apply plugin: 'idea'

tasks.named('idea') {
doFirst { throw new GradleException("Use of the 'idea' task has been deprecated. For details on importing into IntelliJ see CONTRIBUTING.md.") }
}
}

tasks.register('configureIdeaGradleJvm') {
group = 'ide'
description = 'Configures the appropriate JVM for Gradle'

doLast {
modifyXml('.idea/gradle.xml') { xml ->
def gradleSettings = xml.component.find { it.'@name' == 'GradleSettings' }.option[0].GradleProjectSettings
// Remove configured JVM option to force IntelliJ to use the project JDK for Gradle
gradleSettings.option.findAll { it.'@name' == 'gradleJvm' }.each { it.parent().remove(it) }
}
}
}

idea {
project {
vcs = 'Git'
jdkName = '12'

settings {
delegateActions {
delegateBuildRunToGradle = false
testRunner = 'choose_per_test'
}
taskTriggers {
afterSync tasks.named('configureIdeaGradleJvm')
}
codeStyle {
java {
classCountToUseImportOnDemand = 999
}
}
encodings {
encoding = 'UTF-8'
}
compiler {
parallelCompilation = true
javac {
generateDeprecationWarnings = false
}
}
runConfigurations {
'Debug Elasticsearch'(Remote) {
mode = 'attach'
host = 'localhost'
port = 5005
}
defaults(JUnit) {
vmParameters = '-ea -Djava.locale.providers=SPI,COMPAT'
}
}
copyright {
useDefault = 'Apache'
scopes = ['x-pack': 'Elastic']
profiles {
Apache {
keyword = 'Licensed to Elasticsearch under one or more contributor'
notice = '''\
Licensed to Elasticsearch under one or more contributor
license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright
ownership. Elasticsearch licenses this file to you 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.'''.stripIndent()
}
Elastic {
keyword = 'Licensed under the Elastic License'
notice = '''\
Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
or more contributor license agreements. Licensed under the Elastic License;
you may not use this file except in compliance with the Elastic License.'''.stripIndent()
}
}
}
}
}
}

/**
* Parses a given XML file, applies a set of changes, and writes those changes back to the original file.
*
* @param path Path to existing XML file
* @param action Action to perform on parsed XML document
*/
void modifyXml(Object path, Action<? super Node> action) {
File xmlFile = project.file(path)
Node xml = new XmlParser().parse(xmlFile)
action.execute(xml)

xmlFile.withPrintWriter { writer ->
new XmlNodePrinter(writer).print(xml)
}
}

0 comments on commit 0d66ab2

Please sign in to comment.