├── .github ├── ISSUE_TEMPLATE │ └── config.yml └── workflows │ └── gradle.yml ├── .gitignore ├── LICENSE.txt ├── README.md ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src ├── main ├── groovy │ └── org │ │ └── jetbrains │ │ └── gradle │ │ └── ext │ │ ├── ActionDelegationConfig.groovy │ │ ├── CodeStyle.groovy │ │ ├── Compiler.groovy │ │ ├── Copyright.groovy │ │ ├── EncodingConfiguration.groovy │ │ ├── Facets.groovy │ │ ├── Frameworks.groovy │ │ ├── GradleUtils.groovy │ │ ├── GroovyCompiler.groovy │ │ ├── IdeArtifacts.groovy │ │ ├── IdeaExtPlugin.groovy │ │ ├── IdeaFilesProcessor.groovy │ │ ├── Inspections.groovy │ │ ├── LayoutFileBuildService.groovy │ │ ├── MapConvertible.groovy │ │ ├── ModuleTypesConfig.groovy │ │ ├── PackagePrefixContainer.groovy │ │ ├── PropertiesEncodingConfiguration.groovy │ │ ├── RunConfigurations.groovy │ │ ├── TaskTriggersConfig.groovy │ │ └── internal │ │ ├── DefaultRunConfigurationContainer.groovy │ │ └── DefaultRunConfigurationContainer51.groovy └── kotlin │ └── org │ └── jetbrains │ └── gradle │ └── ext │ └── IdeaModelUtils.kt └── test ├── groovy ├── TestIdeaExtPlugin.groovy └── TestIdeaExtPluginOnKotlinBuildFile.groovy ├── java └── org │ └── jetbrains │ └── gradle │ └── ext │ └── SerializationUtil.java └── kotlin └── org └── jetbrains └── gradle └── ext ├── BuildIdeArtifactTest.kt ├── IdeaFilesProcessorTest.kt └── SerializationTests.kt /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Consolidate issues in JetBrains YouTrack 4 | url: https://youtrack.jetbrains.com/newIssue?project=IDEA&summary=Gradle%20IDEA%20Ext&c=Subsystem%20Build.%20Gradle&c=tag%20idea_ext 5 | about: Please, consider opening issues in the JetBrains YouTrack instance 6 | -------------------------------------------------------------------------------- /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | # This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time 6 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle 7 | 8 | name: Java CI with Gradle 9 | 10 | on: 11 | push: 12 | branches: [ "master" ] 13 | pull_request: 14 | branches: [ "master" ] 15 | 16 | jobs: 17 | build: 18 | 19 | runs-on: ubuntu-latest 20 | permissions: 21 | contents: read 22 | 23 | steps: 24 | - uses: actions/checkout@v4 25 | - name: Set up JDK 11 26 | uses: actions/setup-java@v4 27 | with: 28 | java-version: '11' 29 | distribution: 'temurin' 30 | 31 | # Configure Gradle for optimal use in GitHub Actions, including caching of downloaded dependencies. 32 | # See: https://github.com/gradle/actions/blob/main/setup-gradle/README.md 33 | - name: Setup Gradle 34 | uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 35 | 36 | - name: Build with Gradle Wrapper 37 | run: ./gradlew build 38 | 39 | # NOTE: The Gradle Wrapper is the default and recommended way to run Gradle (https://docs.gradle.org/current/userguide/gradle_wrapper.html). 40 | # If your project does not have the Gradle Wrapper configured, you can use the following configuration to run Gradle with a specified version. 41 | # 42 | # - name: Setup Gradle 43 | # uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 44 | # with: 45 | # gradle-version: '8.9' 46 | # 47 | # - name: Build with Gradle 8.9 48 | # run: gradle build 49 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .gradle 3 | out 4 | build 5 | *.iml -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gradle-idea-ext-plugin 2 | 3 | [![JetBrains team project](http://jb.gg/badges/team.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub) 4 | [![Gradle Plugin Portal][gradle-plugin-badge]][gradle-plugin-page] 5 | 6 | Plugin to store some IntelliJ IDEA settings in gradle script 7 | 8 | ### How to build 9 | 10 | as simple as 11 | 12 | ./gradlew build 13 | 14 | ### How to apply 15 | 16 | Apply from Gradle plugin repository 17 | 18 | plugins { 19 | id "org.jetbrains.gradle.plugin.idea-ext" version "1.1.10" 20 | } 21 | 22 | Or build and drop resulting jar into root directory of a project and add following snippet to `build.gradle` 23 | 24 | buildscript { 25 | dependencies { 26 | classpath files('gradle-idea-ext.jar') 27 | } 28 | } 29 | apply plugin: 'org.jetbrains.gradle.plugin.idea-ext' 30 | 31 | ### How to use 32 | 33 | See the [Wiki](https://github.com/JetBrains/gradle-idea-ext-plugin/wiki) for full DSL documentation 34 | 35 | 36 | Version 1.0 requires IntelliJ IDEA 2020.3 37 | 38 | [gradle-plugin-badge]: https://img.shields.io/gradle-plugin-portal/v/org.jetbrains.gradle.plugin.idea-ext?color=green&label=Gradle%20Plugin%20Portal&logo=gradle 39 | [gradle-plugin-page]: https://plugins.gradle.org/plugin/org.jetbrains.gradle.plugin.idea-ext 40 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | import org.gradle.kotlin.dsl.ProjectExtensionsKt 2 | 3 | plugins { 4 | id 'groovy' 5 | id 'java-gradle-plugin' 6 | id "org.jetbrains.kotlin.jvm" version "1.3.61" 7 | id "com.gradle.plugin-publish" version "0.11.0" 8 | } 9 | 10 | group = 'org.jetbrains.gradle.plugin.idea-ext' 11 | version = '1.1.11-SNAPSHOT' 12 | 13 | repositories { 14 | mavenCentral() 15 | } 16 | 17 | dependencies { 18 | implementation 'com.google.code.gson:gson:2.10.1' 19 | implementation 'com.google.guava:guava:28.2-jre' 20 | implementation ProjectExtensionsKt.gradleKotlinDsl(project) 21 | 22 | testImplementation 'org.jetbrains.kotlin:kotlin-stdlib' 23 | testImplementation('org.spockframework:spock-core:1.3-groovy-2.5') { 24 | exclude group: 'org.codehaus.groovy' 25 | } 26 | testImplementation "org.assertj:assertj-core:3.15.0" 27 | } 28 | 29 | gradlePlugin { 30 | plugins { 31 | ideaExtPlugin { 32 | id = "org.jetbrains.gradle.plugin.idea-ext" 33 | implementationClass = "org.jetbrains.gradle.ext.IdeaExtPlugin" 34 | } 35 | } 36 | } 37 | 38 | pluginBundle { 39 | website = 'https://github.com/jetbrains/gradle-idea-ext-plugin' 40 | vcsUrl = 'https://github.com/jetbrains/gradle-idea-ext-plugin' 41 | 42 | description = 'Extends the Gradle\'s "idea" DSL with specific settings: code style, facets, run configurations etc.' 43 | tags = ['intellij', 'idea', 'settings'] 44 | 45 | plugins { 46 | ideaExtPlugin { 47 | id = 'org.jetbrains.gradle.plugin.idea-ext' 48 | displayName = 'Gradle Idea Extension plugin' 49 | } 50 | } 51 | } 52 | 53 | wrapper { 54 | gradleVersion = "5.6.4" 55 | distributionType = Wrapper.DistributionType.BIN 56 | } 57 | 58 | tasks.named('compileGroovy') { 59 | // do not depend on compileJava 60 | classpath = sourceSets.main.compileClasspath 61 | } 62 | // depend on compileGroovy 63 | compileKotlin.classpath += files(sourceSets.main.groovy.classesDirectory) 64 | compileKotlin.kotlinOptions.jvmTarget=1.8 65 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JetBrains/gradle-idea-ext-plugin/e33d0ee10e2b9154207ff4e43fa06dac2e7e54ec/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | # Determine the Java command to use to start the JVM. 86 | if [ -n "$JAVA_HOME" ] ; then 87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 88 | # IBM's JDK on AIX uses strange locations for the executables 89 | JAVACMD="$JAVA_HOME/jre/sh/java" 90 | else 91 | JAVACMD="$JAVA_HOME/bin/java" 92 | fi 93 | if [ ! -x "$JAVACMD" ] ; then 94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 95 | 96 | Please set the JAVA_HOME variable in your environment to match the 97 | location of your Java installation." 98 | fi 99 | else 100 | JAVACMD="java" 101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 102 | 103 | Please set the JAVA_HOME variable in your environment to match the 104 | location of your Java installation." 105 | fi 106 | 107 | # Increase the maximum file descriptors if we can. 108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 109 | MAX_FD_LIMIT=`ulimit -H -n` 110 | if [ $? -eq 0 ] ; then 111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 112 | MAX_FD="$MAX_FD_LIMIT" 113 | fi 114 | ulimit -n $MAX_FD 115 | if [ $? -ne 0 ] ; then 116 | warn "Could not set maximum file descriptor limit: $MAX_FD" 117 | fi 118 | else 119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 120 | fi 121 | fi 122 | 123 | # For Darwin, add options to specify how the application appears in the dock 124 | if $darwin; then 125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 126 | fi 127 | 128 | # For Cygwin, switch paths to Windows format before running java 129 | if $cygwin ; then 130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 132 | JAVACMD=`cygpath --unix "$JAVACMD"` 133 | 134 | # We build the pattern for arguments to be converted via cygpath 135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 136 | SEP="" 137 | for dir in $ROOTDIRSRAW ; do 138 | ROOTDIRS="$ROOTDIRS$SEP$dir" 139 | SEP="|" 140 | done 141 | OURCYGPATTERN="(^($ROOTDIRS))" 142 | # Add a user-defined pattern to the cygpath arguments 143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 145 | fi 146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 147 | i=0 148 | for arg in "$@" ; do 149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 151 | 152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 154 | else 155 | eval `echo args$i`="\"$arg\"" 156 | fi 157 | i=$((i+1)) 158 | done 159 | case $i in 160 | (0) set -- ;; 161 | (1) set -- "$args0" ;; 162 | (2) set -- "$args0" "$args1" ;; 163 | (3) set -- "$args0" "$args1" "$args2" ;; 164 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 165 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 166 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 167 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 168 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 169 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 170 | esac 171 | fi 172 | 173 | # Escape application args 174 | save () { 175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 176 | echo " " 177 | } 178 | APP_ARGS=$(save "$@") 179 | 180 | # Collect all arguments for the java command, following the shell quoting and substitution rules 181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 182 | 183 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 184 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 185 | cd "$(dirname "$0")" 186 | fi 187 | 188 | exec "$JAVACMD" "$@" 189 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem http://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 33 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 34 | 35 | @rem Find java.exe 36 | if defined JAVA_HOME goto findJavaFromJavaHome 37 | 38 | set JAVA_EXE=java.exe 39 | %JAVA_EXE% -version >NUL 2>&1 40 | if "%ERRORLEVEL%" == "0" goto init 41 | 42 | echo. 43 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 44 | echo. 45 | echo Please set the JAVA_HOME variable in your environment to match the 46 | echo location of your Java installation. 47 | 48 | goto fail 49 | 50 | :findJavaFromJavaHome 51 | set JAVA_HOME=%JAVA_HOME:"=% 52 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 53 | 54 | if exist "%JAVA_EXE%" goto init 55 | 56 | echo. 57 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 58 | echo. 59 | echo Please set the JAVA_HOME variable in your environment to match the 60 | echo location of your Java installation. 61 | 62 | goto fail 63 | 64 | :init 65 | @rem Get command-line arguments, handling Windows variants 66 | 67 | if not "%OS%" == "Windows_NT" goto win9xME_args 68 | 69 | :win9xME_args 70 | @rem Slurp the command line arguments. 71 | set CMD_LINE_ARGS= 72 | set _SKIP=2 73 | 74 | :win9xME_args_slurp 75 | if "x%~1" == "x" goto execute 76 | 77 | set CMD_LINE_ARGS=%* 78 | 79 | :execute 80 | @rem Setup the command line 81 | 82 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 83 | 84 | @rem Execute Gradle 85 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 86 | 87 | :end 88 | @rem End local scope for the variables with windows NT shell 89 | if "%ERRORLEVEL%"=="0" goto mainEnd 90 | 91 | :fail 92 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 93 | rem the _cmd.exe /c_ return code! 94 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 95 | exit /b 1 96 | 97 | :mainEnd 98 | if "%OS%"=="Windows_NT" endlocal 99 | 100 | :omega 101 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * This settings file was generated by the Gradle 'init' task. 3 | * 4 | * The settings file is used to specify which projects to include in your build. 5 | * In a single project build this file can be empty or even removed. 6 | * 7 | * Detailed information about configuring a multi-project build in Gradle can be found 8 | * in the user guide at https://docs.gradle.org/4.0/userguide/multi_project_builds.html 9 | */ 10 | 11 | 12 | rootProject.name = 'gradle-idea-ext' 13 | -------------------------------------------------------------------------------- /src/main/groovy/org/jetbrains/gradle/ext/ActionDelegationConfig.groovy: -------------------------------------------------------------------------------- 1 | package org.jetbrains.gradle.ext 2 | 3 | import groovy.transform.CompileStatic 4 | 5 | @CompileStatic 6 | class ActionDelegationConfig implements MapConvertible { 7 | enum TestRunner { PLATFORM, GRADLE, CHOOSE_PER_TEST } 8 | Boolean delegateBuildRunToGradle 9 | TestRunner testRunner 10 | 11 | Map toMap() { 12 | return ["delegateBuildRunToGradle": delegateBuildRunToGradle, "testRunner": testRunner] 13 | } 14 | } -------------------------------------------------------------------------------- /src/main/groovy/org/jetbrains/gradle/ext/CodeStyle.groovy: -------------------------------------------------------------------------------- 1 | package org.jetbrains.gradle.ext 2 | 3 | import groovy.transform.CompileStatic 4 | import org.gradle.api.Action 5 | 6 | /** 7 | * @deprecated Use .editorconfig to defined codestyle 8 | */ 9 | @CompileStatic 10 | enum ForceBraces { 11 | DO_NOT_FORCE, FORCE_BRACES_IF_MULTILINE, FORCE_BRACES_ALWAYS 12 | } 13 | 14 | /** 15 | * @deprecated Use .editorconfig to defined codestyle 16 | */ 17 | @CompileStatic 18 | class CommonCodeStyleConfig implements MapConvertible { 19 | 20 | public Integer hardWrapAt 21 | public Boolean wrapCommentsAtRightMargin 22 | public ForceBraces ifForceBraces 23 | public ForceBraces doWhileForceBraces 24 | public ForceBraces whileForceBraces 25 | public ForceBraces forForceBraces 26 | public Boolean keepControlStatementInOneLine 27 | 28 | Map toMap() { 29 | return [ 30 | "RIGHT_MARGIN" : hardWrapAt, 31 | "WRAP_COMMENTS" : wrapCommentsAtRightMargin, 32 | "IF_BRACE_FORCE" : ifForceBraces, 33 | "DOWHILE_BRACE_FORCE" : doWhileForceBraces, 34 | "WHILE_BRACE_FORCE" : whileForceBraces, 35 | "FOR_BRACE_FORCE" : forForceBraces, 36 | "KEEP_CONTROL_STATEMENT_IN_ONE_LINE" : keepControlStatementInOneLine 37 | ] 38 | } 39 | } 40 | 41 | /** 42 | * @deprecated Use .editorconfig to defined codestyle 43 | */ 44 | class JavaCodeStyleConfig extends CommonCodeStyleConfig { 45 | public Integer classCountToUseImportOnDemand 46 | public Boolean alignParameterDescriptions 47 | public Boolean alignThrownExceptionDescriptions 48 | public Boolean generatePTagOnEmptyLines 49 | public Boolean keepEmptyParamTags 50 | public Boolean keepEmptyThrowsTags 51 | public Boolean keepEmptyReturnTags 52 | 53 | @Override 54 | Map toMap() { 55 | def result = super.toMap() 56 | return result << ([ 57 | "CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" : classCountToUseImportOnDemand, 58 | "JD_ALIGN_PARAM_COMMENTS" : alignParameterDescriptions, 59 | "JD_ALIGN_EXCEPTION_COMMENTS" : alignThrownExceptionDescriptions, 60 | "JD_P_AT_EMPTY_LINES" : generatePTagOnEmptyLines, 61 | "JD_KEEP_EMPTY_PARAMETER" : keepEmptyParamTags, 62 | "JD_KEEP_EMPTY_EXCEPTION" : keepEmptyThrowsTags, 63 | "JD_KEEP_EMPTY_RETURN" : keepEmptyReturnTags 64 | ] as Map) 65 | } 66 | } 67 | 68 | /** 69 | * @deprecated Use .editorconfig to defined codestyle 70 | */ 71 | class GroovyCodeStyleConfig extends CommonCodeStyleConfig { 72 | public Integer classCountToUseImportOnDemand 73 | public Boolean alignMultilineNamedArguments 74 | 75 | @Override 76 | Map toMap() { 77 | def result = super.toMap() 78 | return result << ([ 79 | "CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" : classCountToUseImportOnDemand, 80 | "ALIGN_NAMED_ARGS_IN_MAP" : alignMultilineNamedArguments 81 | ] as Map) 82 | } 83 | } 84 | 85 | /** 86 | * @deprecated Use .editorconfig to defined codestyle 87 | */ 88 | @CompileStatic 89 | class CodeStyleConfig implements MapConvertible { 90 | 91 | @Deprecated 92 | public Boolean USE_SAME_INDENTS 93 | public Integer hardWrapAt 94 | public Boolean keepControlStatementInOneLine 95 | 96 | Map languages = [:] 97 | 98 | def java(Action action) { 99 | if (!languages["java"]) { 100 | languages["java"] = new JavaCodeStyleConfig() 101 | } 102 | action.execute(languages["java"] as JavaCodeStyleConfig) 103 | } 104 | 105 | def groovy(Action action) { 106 | if (!languages["groovy"]) { 107 | languages["groovy"] = new GroovyCodeStyleConfig() 108 | } 109 | action.execute(languages["groovy"] as GroovyCodeStyleConfig) 110 | } 111 | 112 | Map toMap() { 113 | def map = [ 114 | "USE_SAME_INDENTS" : USE_SAME_INDENTS, 115 | "RIGHT_MARGIN" : hardWrapAt, 116 | "KEEP_CONTROL_STATEMENT_IN_ONE_LINE" : keepControlStatementInOneLine 117 | ] as Map 118 | 119 | def languages = this.languages.collectEntries { key, value -> 120 | [(key): value.toMap()].findAll { !it.value.isEmpty() } 121 | } 122 | if (!languages.isEmpty()) { 123 | map["languages"] = languages 124 | } 125 | return map 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/main/groovy/org/jetbrains/gradle/ext/Compiler.groovy: -------------------------------------------------------------------------------- 1 | package org.jetbrains.gradle.ext 2 | 3 | import groovy.transform.CompileStatic 4 | import org.gradle.api.Action 5 | import org.gradle.api.Project 6 | 7 | import javax.inject.Inject 8 | 9 | @CompileStatic 10 | class IdeaCompilerConfiguration implements MapConvertible { 11 | private final Project project 12 | String resourcePatterns 13 | Integer processHeapSize 14 | Boolean autoShowFirstErrorInEditor 15 | Boolean displayNotificationPopup 16 | Boolean clearOutputDirectory 17 | Boolean addNotNullAssertions 18 | Boolean enableAutomake 19 | Boolean parallelCompilation 20 | Boolean rebuildModuleOnDependencyChange 21 | String additionalVmOptions 22 | Boolean useReleaseOption 23 | JavacConfiguration javacConfig 24 | 25 | @Inject 26 | IdeaCompilerConfiguration(Project project) { 27 | this.project = project 28 | } 29 | 30 | JavacConfiguration getJavac() { 31 | if (javacConfig == null) { 32 | javacConfig = project.objects.newInstance(JavacConfiguration) 33 | } 34 | return javacConfig 35 | } 36 | 37 | void javac(Action action) { 38 | action.execute(getJavac()) 39 | } 40 | 41 | @Override 42 | Map toMap() { 43 | Map map = [:] 44 | if (resourcePatterns) map.put("resourcePatterns", resourcePatterns) 45 | if (processHeapSize != null) map.put("processHeapSize", processHeapSize) 46 | if (autoShowFirstErrorInEditor != null) map.put("autoShowFirstErrorInEditor", autoShowFirstErrorInEditor) 47 | if (displayNotificationPopup != null) map.put("displayNotificationPopup", displayNotificationPopup) 48 | if (clearOutputDirectory != null) map.put("clearOutputDirectory", clearOutputDirectory) 49 | if (addNotNullAssertions != null) map.put("addNotNullAssertions", addNotNullAssertions) 50 | if (enableAutomake != null) map.put("enableAutomake", enableAutomake) 51 | if (parallelCompilation != null) map.put("parallelCompilation", parallelCompilation) 52 | if (rebuildModuleOnDependencyChange != null) map.put("rebuildModuleOnDependencyChange", rebuildModuleOnDependencyChange) 53 | if (additionalVmOptions != null) map.put("additionalVmOptions", additionalVmOptions) 54 | if (useReleaseOption != null) map.put("useReleaseOption", useReleaseOption) 55 | if (javacConfig != null) map.put("javacOptions", javacConfig.toMap()) 56 | return map 57 | } 58 | } 59 | 60 | @CompileStatic 61 | class JavacConfiguration { 62 | Boolean preferTargetJDKCompiler 63 | String javacAdditionalOptions 64 | Map moduleJavacAdditionalOptions 65 | Boolean generateDebugInfo 66 | Boolean generateDeprecationWarnings 67 | Boolean generateNoWarnings 68 | 69 | def toMap() { 70 | def map = [:] 71 | if (preferTargetJDKCompiler != null) map.put("preferTargetJDKCompiler", preferTargetJDKCompiler) 72 | if (javacAdditionalOptions != null) map.put("javacAdditionalOptions", javacAdditionalOptions) 73 | if (moduleJavacAdditionalOptions != null) map.put("moduleJavacAdditionalOptions", moduleJavacAdditionalOptions) 74 | if (generateDebugInfo != null) map.put("generateDebugInfo", generateDebugInfo) 75 | if (generateDeprecationWarnings != null) map.put("generateDeprecationWarnings", generateDeprecationWarnings) 76 | if (generateNoWarnings != null) map.put("generateNoWarnings", generateNoWarnings) 77 | return map 78 | } 79 | } -------------------------------------------------------------------------------- /src/main/groovy/org/jetbrains/gradle/ext/Copyright.groovy: -------------------------------------------------------------------------------- 1 | package org.jetbrains.gradle.ext 2 | 3 | import groovy.transform.CompileStatic 4 | import org.gradle.api.Action 5 | import org.gradle.api.NamedDomainObjectContainer 6 | import org.gradle.api.Project 7 | 8 | import javax.inject.Inject 9 | 10 | @CompileStatic 11 | class CopyrightConfiguration implements MapConvertible { 12 | 13 | String useDefault 14 | Map scopes = [:] 15 | final NamedDomainObjectContainer profiles 16 | 17 | @Inject 18 | CopyrightConfiguration(Project project) { 19 | profiles = project.container(CopyrightProfile) 20 | } 21 | 22 | def profiles(Action> action) { 23 | action.execute(profiles) 24 | } 25 | 26 | @Override 27 | Map toMap() { 28 | def map = [:] 29 | if (useDefault) map.put("useDefault", useDefault) 30 | if (!scopes.isEmpty()) { 31 | map.put("scopes", scopes) 32 | } 33 | if (!profiles.isEmpty()) { 34 | map.put("profiles", profiles.asMap.collectEntries { k, v -> [k, v.toMap()] }) 35 | } 36 | return map 37 | } 38 | } 39 | 40 | @CompileStatic 41 | class CopyrightProfile { 42 | 43 | final String name 44 | String notice 45 | String keyword 46 | String allowReplaceRegexp 47 | 48 | CopyrightProfile(String name) { 49 | this.name = name 50 | } 51 | 52 | def toMap() { 53 | return [ 54 | "name" : name, 55 | "notice" : notice, 56 | "keyword" : keyword, 57 | "allowReplaceRegexp": allowReplaceRegexp 58 | ] 59 | } 60 | } -------------------------------------------------------------------------------- /src/main/groovy/org/jetbrains/gradle/ext/EncodingConfiguration.groovy: -------------------------------------------------------------------------------- 1 | package org.jetbrains.gradle.ext 2 | 3 | import groovy.transform.CompileStatic 4 | import org.gradle.api.Action 5 | import org.gradle.api.Project 6 | 7 | import javax.inject.Inject 8 | 9 | @CompileStatic 10 | class EncodingConfiguration implements MapConvertible { 11 | enum BomPolicy { 12 | WITH_BOM, WITH_NO_BOM, WITH_BOM_ON_WINDOWS 13 | } 14 | 15 | private final Project project 16 | 17 | String encoding = null 18 | BomPolicy bomPolicy = null 19 | Map mapping = [:] 20 | final PropertiesEncodingConfiguration properties = new PropertiesEncodingConfiguration() 21 | 22 | @Inject 23 | EncodingConfiguration(Project project) { 24 | this.project = project 25 | } 26 | 27 | void properties(Action action) { 28 | action.execute(properties) 29 | } 30 | 31 | @Override 32 | Map toMap() { 33 | def contentRoot = project.projectDir.absolutePath 34 | def absoluteMapping = new LinkedHashMap() 35 | for (def entry : mapping) { 36 | def path = entry.key 37 | def prefix = entry.value 38 | def file = new File(contentRoot, path) 39 | def sourceDir = file.path.replace(File.separator, '/') 40 | absoluteMapping.put(sourceDir, prefix) 41 | } 42 | return [ 43 | 'encoding' : encoding, 44 | 'bomPolicy' : bomPolicy?.toString(), 45 | 'properties': nullIfEmpty(properties.toMap()), 46 | 'mapping' : nullIfEmpty(absoluteMapping) 47 | ] 48 | } 49 | 50 | static Map nullIfEmpty(Map map) { 51 | def result = map.findAll { it.value != null } 52 | return result.isEmpty() ? null : result 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/groovy/org/jetbrains/gradle/ext/Facets.groovy: -------------------------------------------------------------------------------- 1 | package org.jetbrains.gradle.ext 2 | 3 | import groovy.transform.CompileStatic 4 | import org.gradle.api.Action 5 | import org.gradle.api.Named 6 | import org.gradle.api.NamedDomainObjectContainer 7 | import org.gradle.api.Project 8 | 9 | import javax.inject.Inject 10 | 11 | @CompileStatic 12 | interface Facet extends Named, MapConvertible { 13 | String getType() 14 | } 15 | 16 | class SpringFacet implements Facet { 17 | 18 | final String name 19 | final String type = "spring" 20 | final NamedDomainObjectContainer contexts 21 | 22 | @Inject 23 | SpringFacet(String name, Project project) { 24 | this.name = name 25 | contexts = project.container(SpringContext) 26 | } 27 | 28 | def contexts(Action> action) { 29 | action.execute(contexts) 30 | } 31 | 32 | Map toMap() { 33 | return [ 34 | "type" : type, 35 | "contexts": contexts.asList().collect { it.toMap() }, 36 | "name" : name 37 | ] 38 | } 39 | } 40 | 41 | @CompileStatic 42 | class SpringContext { 43 | 44 | final String name 45 | String file 46 | String parent 47 | 48 | SpringContext(String name) { 49 | this.name = name; 50 | } 51 | 52 | Map toMap() { 53 | return [ 54 | "file" : file, 55 | "name" : name, 56 | "parent": parent 57 | ] 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/groovy/org/jetbrains/gradle/ext/Frameworks.groovy: -------------------------------------------------------------------------------- 1 | package org.jetbrains.gradle.ext 2 | 3 | import groovy.transform.CompileStatic 4 | 5 | @CompileStatic 6 | class FrameworkDetectionExclusionSettings { 7 | List excludes = [] 8 | } -------------------------------------------------------------------------------- /src/main/groovy/org/jetbrains/gradle/ext/GradleUtils.groovy: -------------------------------------------------------------------------------- 1 | package org.jetbrains.gradle.ext 2 | 3 | import groovy.transform.PackageScope 4 | import org.gradle.api.ExtensiblePolymorphicDomainObjectContainer 5 | import org.gradle.api.Named 6 | import org.gradle.api.Project 7 | import org.gradle.api.internal.CollectionCallbackActionDecorator 8 | import org.gradle.api.internal.DefaultPolymorphicDomainObjectContainer 9 | import org.gradle.api.internal.project.ProjectInternal 10 | import org.gradle.internal.reflect.Instantiator 11 | import org.gradle.util.GradleVersion 12 | import org.jetbrains.gradle.ext.internal.DefaultRunConfigurationContainer 13 | import org.jetbrains.gradle.ext.internal.DefaultRunConfigurationContainer51 14 | 15 | /** 16 | * Helpers for Gradle internal APIs. 17 | */ 18 | @PackageScope 19 | class GradleUtils { 20 | 21 | static boolean is_Gradle_5_1_or_newer = GradleVersion.current().compareTo(GradleVersion.version("5.1")) >= 0 22 | 23 | static ExtensiblePolymorphicDomainObjectContainer polymorphicContainer(Project project, Class type) { 24 | def instantiator = (project as ProjectInternal).services.get(Instantiator.class) 25 | if (is_Gradle_5_1_or_newer) { 26 | return instantiator.newInstance(DefaultPolymorphicDomainObjectContainer, type, instantiator, CollectionCallbackActionDecorator.NOOP) 27 | } else { 28 | return instantiator.newInstance(DefaultPolymorphicDomainObjectContainer, type, instantiator) 29 | } 30 | } 31 | 32 | static RunConfigurationContainer runConfigurationsContainer(Project project) { 33 | if(is_Gradle_5_1_or_newer) { 34 | def instantiator = (project as ProjectInternal).services.get(Instantiator.class) 35 | return instantiator.newInstance(DefaultRunConfigurationContainer51, instantiator) 36 | } else { 37 | def instantiator = (project as ProjectInternal).services.get(Instantiator.class) 38 | return instantiator.newInstance(DefaultRunConfigurationContainer, instantiator) 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/groovy/org/jetbrains/gradle/ext/GroovyCompiler.groovy: -------------------------------------------------------------------------------- 1 | package org.jetbrains.gradle.ext 2 | 3 | import groovy.transform.CompileStatic 4 | import org.gradle.api.Action 5 | 6 | @CompileStatic 7 | class GroovyCompilerConfiguration implements MapConvertible { 8 | ExcludesConfig excludes 9 | 10 | def excludes(Action action) { 11 | if (excludes == null) { 12 | excludes = new ExcludesConfig() 13 | } 14 | action.execute(excludes) 15 | } 16 | 17 | @Override 18 | Map toMap() { 19 | return ["excludes": excludes?.data] 20 | } 21 | } 22 | 23 | @CompileStatic 24 | class ExcludesConfig { 25 | final List> data = [] 26 | 27 | def file(String path) { 28 | data.add([ 29 | "url" : path, 30 | "includeSubdirectories": false, 31 | "isFile" : true 32 | ]) 33 | } 34 | 35 | def dir(String path, Boolean includeSubdirectories = false) { 36 | data.add([ 37 | "url" : path, 38 | "includeSubdirectories": includeSubdirectories, 39 | "isFile" : false 40 | ]) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/groovy/org/jetbrains/gradle/ext/IdeArtifacts.groovy: -------------------------------------------------------------------------------- 1 | package org.jetbrains.gradle.ext 2 | 3 | import org.gradle.api.Action 4 | import org.gradle.api.DefaultTask 5 | import org.gradle.api.NamedDomainObjectFactory 6 | import org.gradle.api.Project 7 | import org.gradle.api.artifacts.ArtifactCollection 8 | import org.gradle.api.artifacts.Configuration 9 | import org.gradle.api.artifacts.component.ModuleComponentIdentifier 10 | import org.gradle.api.file.ConfigurableFileCollection 11 | import org.gradle.api.tasks.Internal 12 | import org.gradle.api.tasks.OutputDirectory 13 | import org.gradle.api.tasks.TaskAction 14 | 15 | import javax.inject.Inject 16 | import java.nio.file.Files 17 | import java.util.zip.ZipEntry 18 | import java.util.zip.ZipOutputStream 19 | 20 | abstract class TypedArtifact implements MapConvertible { 21 | Project project 22 | ArtifactType type 23 | 24 | TypedArtifact(Project project, ArtifactType type) { 25 | this.type = type 26 | this.project = project 27 | } 28 | 29 | @Override 30 | Map toMap() { 31 | return ["type":type] 32 | } 33 | 34 | abstract void buildTo(File destination) 35 | } 36 | 37 | abstract class RecursiveArtifact extends TypedArtifact { 38 | String name 39 | public final List children = new ArrayList<>() 40 | 41 | @Inject 42 | RecursiveArtifact(Project project, String name, ArtifactType type) { 43 | super(project, type) 44 | this.name = name 45 | } 46 | 47 | DirectoryArtifact directory(String name, Action action = {}) { 48 | RecursiveArtifact newDir = project.objects.newInstance(DirectoryArtifact, project, name) 49 | action.execute(newDir) 50 | children.add(newDir) 51 | return newDir 52 | } 53 | 54 | ArchiveArtifact archive(String name, Action action = {}) { 55 | RecursiveArtifact newArchive = project.objects.newInstance(ArchiveArtifact, project, name) 56 | action.execute(newArchive) 57 | children.add(newArchive) 58 | return newArchive 59 | } 60 | 61 | LibraryFiles libraryFiles(Configuration configuration) { 62 | def child = new LibraryFiles(project, configuration) 63 | children.add(child) 64 | return child 65 | } 66 | 67 | ModuleOutput moduleOutput(String moduleName) { 68 | def child = new ModuleOutput(project, moduleName) 69 | children.add(child) 70 | return child 71 | } 72 | 73 | ModuleTestOutput moduleTestOutput(String moduleName) { 74 | def child = new ModuleTestOutput(project, moduleName) 75 | children.add(child) 76 | return child 77 | } 78 | 79 | ModuleSrc moduleSrc(String moduleName) { 80 | def child = new ModuleSrc(project, moduleName) 81 | children.add(child) 82 | return child 83 | } 84 | 85 | ArtifactRef artifact(String artifactName) { 86 | def child = new ArtifactRef(project, artifactName) 87 | children.add(child) 88 | return child 89 | } 90 | 91 | FileCopy file(Object... files) { 92 | def child = new FileCopy(project, project.files(files)) 93 | children.add(child) 94 | return child 95 | } 96 | 97 | DirCopy directoryContent(Object... dirs) { 98 | def child = new DirCopy(project, project.files(dirs)) 99 | children.add(child) 100 | return child 101 | } 102 | 103 | ExtractedArchive extractedDirectory(Object... archivePaths) { 104 | def child = new ExtractedArchive(project, project.files(archivePaths)) 105 | children.add(child) 106 | return child 107 | } 108 | 109 | @Override 110 | Map toMap() { 111 | return super.toMap() << ["name": name, "children": children*.toMap() ] 112 | } 113 | } 114 | 115 | class TopLevelArtifact extends RecursiveArtifact { 116 | @Inject 117 | TopLevelArtifact(Project project, String name) { 118 | super(project, name, ArtifactType.ARTIFACT) 119 | } 120 | 121 | @Override 122 | void buildTo(File destination) { 123 | children.forEach { 124 | it.buildTo(destination) 125 | } 126 | } 127 | } 128 | 129 | class TopLevelArtifactFactory implements NamedDomainObjectFactory { 130 | Project myProject 131 | 132 | TopLevelArtifactFactory(Project project) { 133 | myProject = project 134 | } 135 | 136 | @Override 137 | TopLevelArtifact create(String name) { 138 | return myProject.objects.newInstance(TopLevelArtifact, myProject, name) 139 | } 140 | } 141 | 142 | class DirectoryArtifact extends RecursiveArtifact { 143 | @Inject 144 | DirectoryArtifact(Project project, String name) { 145 | super(project, name, ArtifactType.DIR) 146 | } 147 | 148 | @Override 149 | void buildTo(File destination) { 150 | def newDir = new File(destination, name) 151 | newDir.mkdirs() 152 | children.forEach { 153 | it.buildTo(newDir) 154 | } 155 | } 156 | } 157 | 158 | class ArchiveArtifact extends RecursiveArtifact { 159 | @Inject 160 | ArchiveArtifact(Project project, String name) { 161 | super(project, name, ArtifactType.ARCHIVE) 162 | } 163 | 164 | @Override 165 | void buildTo(File destination) { 166 | def randomName = "archive_" + (new Random().nextInt()) 167 | def temp = project.layout.buildDirectory.dir("tmp").get().dir(randomName).asFile 168 | temp.mkdirs() 169 | 170 | children.forEach { 171 | it.buildTo(temp) 172 | } 173 | 174 | createArchive(name, temp, destination) 175 | } 176 | 177 | static def createArchive(String archiveName, File inputDir, File destinationDir) { 178 | 179 | ZipOutputStream output = new ZipOutputStream(new FileOutputStream(new File(destinationDir, archiveName))) 180 | 181 | output.withCloseable { closeableOutput -> 182 | inputDir.eachFileRecurse { file -> 183 | if (!file.isFile()) { 184 | return 185 | } 186 | 187 | String relativePath = inputDir.relativePath(file); 188 | closeableOutput.putNextEntry(new ZipEntry(relativePath)); 189 | 190 | Files.copy(file.toPath(), closeableOutput) 191 | closeableOutput.closeEntry(); // End of current document in ZIP 192 | } 193 | 194 | } 195 | } 196 | } 197 | 198 | 199 | class LibraryFiles extends TypedArtifact { 200 | Configuration configuration 201 | 202 | LibraryFiles(Project project, Configuration configuration) { 203 | super(project, ArtifactType.LIBRARY_FILES) 204 | this.configuration = configuration 205 | } 206 | 207 | @Override 208 | Map toMap() { 209 | ArtifactCollection artifacts = configuration.getIncoming().artifactView({ 210 | it.lenient(true) 211 | //it.componentFilter(Specs.SATISFIES_ALL) 212 | }).getArtifacts() 213 | 214 | def libraries = artifacts.artifacts 215 | .collect { it.id.componentIdentifier } 216 | .findAll { it instanceof ModuleComponentIdentifier } 217 | .collect { ["group":it.group, "artifact": it.module, "version": it.version] } 218 | return super.toMap() << [ "libraries": libraries ] 219 | } 220 | 221 | @Override 222 | void buildTo(File destination) { 223 | ArtifactCollection artifacts = configuration.getIncoming().artifactView({ 224 | it.lenient(true) 225 | //it.componentFilter(Specs.SATISFIES_ALL) 226 | }).getArtifacts() 227 | 228 | def libraries = artifacts.artifacts 229 | .findAll { it.id.componentIdentifier instanceof ModuleComponentIdentifier } 230 | .collect { it.file } 231 | 232 | project.copy { 233 | from libraries 234 | into destination 235 | } 236 | } 237 | } 238 | 239 | abstract class ModuleBasedArtifact extends TypedArtifact { 240 | String moduleName 241 | ModuleBasedArtifact(Project project, String name, ArtifactType type) { 242 | super(project, type) 243 | moduleName = name 244 | } 245 | 246 | @Override 247 | Map toMap() { 248 | return super.toMap() << ["moduleName" : moduleName] 249 | } 250 | 251 | protected Project findProject() { 252 | def find = project.rootProject.allprojects.find { it.idea.module.name == moduleName } 253 | if (find == null) { 254 | project.logger.warn("Failed to find gradle project for with idea module name ${moduleName}") 255 | } 256 | return find 257 | } 258 | } 259 | 260 | class ModuleOutput extends ModuleBasedArtifact { 261 | ModuleOutput(Project project, String name) { 262 | super(project, name, ArtifactType.MODULE_OUTPUT) 263 | } 264 | 265 | @Override 266 | void buildTo(File destination) { 267 | def project = findProject() 268 | if (project == null) { 269 | return 270 | } 271 | def mainOutput = project.sourceSets["main"].output 272 | project.copy { 273 | from mainOutput.classesDirs + mainOutput.resourcesDir + mainOutput.dirs 274 | into destination 275 | } 276 | } 277 | } 278 | 279 | class ModuleTestOutput extends ModuleBasedArtifact { 280 | ModuleTestOutput(Project project, String name) { 281 | super(project, name, ArtifactType.MODULE_TEST_OUTPUT) 282 | } 283 | 284 | @Override 285 | void buildTo(File destination) { 286 | def project = findProject() 287 | if (project == null) { 288 | return 289 | } 290 | def testOutput = project.sourceSets["test"].output 291 | project.copy { 292 | from testOutput.classesDirs + testOutput.resourcesDir + testOutput.dirs 293 | into destination 294 | } 295 | } 296 | } 297 | 298 | class ModuleSrc extends ModuleBasedArtifact { 299 | ModuleSrc(Project project, String name) { 300 | super(project, name, ArtifactType.MODULE_SRC) 301 | } 302 | 303 | @Override 304 | void buildTo(File destination) { 305 | def project = findProject() 306 | if (project == null) { 307 | return 308 | } 309 | project.copy { 310 | from { project.sourceSets.collect { it.allSource } } 311 | into destination 312 | } 313 | } 314 | } 315 | 316 | class ArtifactRef extends TypedArtifact { 317 | String artifactName 318 | ArtifactRef(Project project, String name) { 319 | super(project, ArtifactType.ARTIFACT_REF) 320 | artifactName = name 321 | } 322 | @Override 323 | Map toMap() { 324 | return super.toMap() << ["artifactName" : artifactName] 325 | } 326 | 327 | @Override 328 | void buildTo(File destination) { 329 | def referredArtifact = project.idea.project.settings.ideArtifacts[artifactName] 330 | if (referredArtifact != null) { 331 | referredArtifact.buildTo(destination) 332 | } 333 | } 334 | } 335 | 336 | abstract class FileBasedArtifact extends TypedArtifact { 337 | ConfigurableFileCollection sources 338 | FileBasedArtifact(Project project, ConfigurableFileCollection sourceFiles, ArtifactType type) { 339 | super(project, type) 340 | sources = sourceFiles 341 | } 342 | 343 | protected abstract Set filter(Set sources) 344 | 345 | @Override 346 | Map toMap() { 347 | def filteredFiles = filter(sources.files) 348 | return super.toMap() << ["sourceFiles": filteredFiles.collect { it.absolutePath.replace('\\' as char, '/' as char) }] 349 | } 350 | } 351 | 352 | class FileCopy extends FileBasedArtifact { 353 | FileCopy(Project project, ConfigurableFileCollection sourceFiles) { 354 | super(project, sourceFiles, ArtifactType.FILE) 355 | } 356 | 357 | @Override 358 | Set filter(Set sources) { 359 | return sources.findAll { it.isFile() } 360 | } 361 | 362 | @Override 363 | void buildTo(File destination) { 364 | if (!destination.isDirectory()) { 365 | return 366 | } 367 | 368 | def filtered = filter(sources.files) 369 | project.copy { 370 | from filtered 371 | into destination 372 | } 373 | } 374 | } 375 | 376 | class DirCopy extends FileBasedArtifact { 377 | DirCopy(Project project, ConfigurableFileCollection sourceDirs) { 378 | super(project, sourceDirs, ArtifactType.DIR_CONTENT) 379 | } 380 | 381 | @Override 382 | Set filter(Set sources) { 383 | return sources.findAll { it.isDirectory() } 384 | } 385 | 386 | @Override 387 | void buildTo(File destination) { 388 | if (!destination.isDirectory()) { 389 | return 390 | } 391 | 392 | def filtered = filter(sources.files) 393 | project.copy { 394 | from filtered 395 | into destination 396 | } 397 | } 398 | } 399 | 400 | class ExtractedArchive extends FileBasedArtifact { 401 | ExtractedArchive(Project project, ConfigurableFileCollection sourceArchives) { 402 | super(project, sourceArchives, ArtifactType.EXTRACTED_DIR) 403 | } 404 | 405 | @Override 406 | Set filter(Set sources) { 407 | return sources.findAll { it.isFile() } 408 | } 409 | 410 | @Override 411 | void buildTo(File destination) { 412 | if (!destination.isDirectory()) { 413 | return 414 | } 415 | 416 | def filtered = filter(sources.files) 417 | project.copy { 418 | from filtered.collect { project.zipTree(it) } 419 | into destination 420 | } 421 | } 422 | } 423 | 424 | enum ArtifactType { 425 | ARTIFACT, 426 | DIR, ARCHIVE, 427 | LIBRARY_FILES, MODULE_OUTPUT, MODULE_TEST_OUTPUT, MODULE_SRC, FILE, DIR_CONTENT, EXTRACTED_DIR, 428 | ARTIFACT_REF 429 | } 430 | 431 | class BuildIdeArtifact extends DefaultTask { 432 | public static final String DEFAULT_DESTINATION = "idea-artifacts" 433 | 434 | @Internal 435 | RecursiveArtifact artifact 436 | File outputDirectory = null 437 | 438 | BuildIdeArtifact() { 439 | // The artifact cannot be used as input, so the task cannot declare its inputs 440 | // and should always be out-of-date. 441 | outputs.upToDateWhen { false } 442 | } 443 | 444 | @TaskAction 445 | void createArtifact() { 446 | if (artifact == null) { 447 | project.logger.warn("artifact not specified for task ${this.name}") 448 | return 449 | } 450 | 451 | artifact.buildTo(getOutputDirectory()) 452 | } 453 | 454 | @OutputDirectory 455 | File getOutputDirectory() { 456 | if (outputDirectory == null) { 457 | outputDirectory = project.layout.buildDirectory 458 | .dir(DEFAULT_DESTINATION).get() 459 | .dir(artifact.name) 460 | .asFile 461 | } 462 | outputDirectory.mkdirs() 463 | return outputDirectory 464 | } 465 | 466 | } -------------------------------------------------------------------------------- /src/main/groovy/org/jetbrains/gradle/ext/IdeaExtPlugin.groovy: -------------------------------------------------------------------------------- 1 | package org.jetbrains.gradle.ext 2 | 3 | import com.google.gson.Gson 4 | import com.google.gson.GsonBuilder 5 | import groovy.transform.CompileStatic 6 | import org.gradle.api.Action 7 | import org.gradle.api.GradleException 8 | import org.gradle.api.Plugin 9 | import org.gradle.api.PolymorphicDomainObjectContainer 10 | import org.gradle.api.Project 11 | import org.gradle.api.XmlProvider 12 | import org.gradle.api.plugins.ExtensionAware 13 | import org.gradle.api.plugins.ExtensionContainer 14 | import org.gradle.api.reflect.TypeOf 15 | import org.gradle.api.tasks.SourceSet 16 | import org.gradle.plugins.ide.idea.model.IdeaModel 17 | import org.gradle.util.GradleVersion 18 | 19 | @CompileStatic 20 | class IdeaExtPlugin implements Plugin { 21 | void apply(Project p) { 22 | p.apply plugin: 'idea' 23 | extend(p) 24 | } 25 | 26 | def extend(Project project) { 27 | def ideaModel = project.extensions.findByName('idea') as IdeaModel 28 | if (!ideaModel) { return } 29 | 30 | IdeaFilesProcessor ideaFilesProcessor = new IdeaFilesProcessor(project) 31 | if (ideaModel.project) { 32 | def projectSettings = (ideaModel.project as ExtensionAware).extensions.create("settings", ProjectSettings, project, ideaFilesProcessor) 33 | 34 | def settingsExt = (projectSettings as ExtensionAware).extensions 35 | 36 | settingsExt.create("delegateActions", ActionDelegationConfig) 37 | settingsExt.create("taskTriggers", TaskTriggersConfig, project) 38 | settingsExt.create("compiler", IdeaCompilerConfiguration, project) 39 | settingsExt.create("groovyCompiler", GroovyCompilerConfiguration) 40 | settingsExt.create("codeStyle", CodeStyleConfig) 41 | settingsExt.create("copyright", CopyrightConfiguration, project) 42 | settingsExt.create("encodings", EncodingConfiguration, project) 43 | addRunConfigurations(settingsExt, project) 44 | addInspections(settingsExt, project) 45 | addArtifacts(settingsExt, project) 46 | } 47 | 48 | def ideaModule = ideaModel.module 49 | if (ideaModule) { 50 | def moduleSettings = (ideaModel.module as ExtensionAware).extensions.create("settings", ModuleSettings, project, ideaFilesProcessor) 51 | 52 | def settingsExt = (moduleSettings as ExtensionAware).extensions 53 | 54 | settingsExt.create("packagePrefix", PackagePrefixContainer, ideaModule) 55 | settingsExt.create("moduleType", ModuleTypesConfig, project, settingsExt) 56 | } 57 | } 58 | 59 | static void addRunConfigurations(ExtensionContainer container, Project project) { 60 | RunConfigurationContainer runConfigurations = GradleUtils.runConfigurationsContainer(project) 61 | 62 | runConfigurations.registerFactory(Application) { String name -> project.objects.newInstance(Application, name, project) } 63 | runConfigurations.registerFactory(JUnit) { String name -> project.objects.newInstance(JUnit, name) } 64 | runConfigurations.registerFactory(Remote) { String name -> project.objects.newInstance(Remote, name) } 65 | runConfigurations.registerFactory(TestNG) { String name -> project.objects.newInstance(TestNG, name) } 66 | runConfigurations.registerFactory(Gradle) { String name -> project.objects.newInstance(Gradle, name) } 67 | runConfigurations.registerFactory(JarApplication) { String name -> project.objects.newInstance(JarApplication, name, project) } 68 | 69 | container.add(RunConfigurationContainer, "runConfigurations", runConfigurations) 70 | } 71 | static void addInspections(ExtensionContainer container, Project project) { 72 | def inspections = project.container(Inspection) 73 | container.add("inspections", inspections) 74 | } 75 | 76 | static void addArtifacts(ExtensionContainer container, Project project) { 77 | def artifacts = project.container(TopLevelArtifact, new TopLevelArtifactFactory(project)) 78 | container.add("ideArtifacts", artifacts) 79 | } 80 | } 81 | 82 | abstract class AbstractExtensibleSettings { 83 | TypeOf mapConvertibleType = TypeOf.typeOf(MapConvertible) 84 | TypeOf iterableType = TypeOf.typeOf(Iterable) 85 | 86 | Map collectExtensionsMap() { 87 | def result = [:] 88 | if (this instanceof ExtensionAware) { 89 | def extContainer = (this as ExtensionAware).extensions 90 | if (GradleVersion.current() >= GradleVersion.version("4.5")) { 91 | extContainer.extensionsSchema.each { schema -> 92 | def name = schema.name 93 | def typeOfExt = schema.publicType 94 | 95 | def converted = convertToMapOrList(typeOfExt, extContainer.findByName(name)) 96 | if (converted != null) { 97 | result.put(name, converted) 98 | } 99 | } 100 | } else { 101 | try { 102 | def schemaMethod = extContainer.class.getMethod("getSchema") 103 | Map schema = schemaMethod.invoke(extContainer) as Map 104 | schema.each { name, typeOfExt -> 105 | def converted = convertToMapOrList(typeOfExt, extContainer.findByName(name)) 106 | if (converted != null) { 107 | result.put(name, converted) 108 | } 109 | } 110 | } catch (NoSuchMethodException e) { 111 | throw new GradleException("Can not collect extensions information in IDEA settings." + 112 | " Please, use Gradle 4.2 or later.", e) 113 | } 114 | } 115 | } 116 | return result 117 | } 118 | 119 | def convertToMapOrList(TypeOf typeOfExt, def extension) { 120 | if (extension == null) { 121 | return null 122 | } 123 | 124 | if (mapConvertibleType.isAssignableFrom(typeOfExt)) { 125 | def map = (extension as MapConvertible).toMap().findAll { it.value != null } 126 | return map.isEmpty() ? null : map 127 | } 128 | 129 | if (iterableType.isAssignableFrom(typeOfExt)) { 130 | def converted = (extension as Iterable) 131 | .findAll { it instanceof MapConvertible } 132 | .collect { (it as MapConvertible).toMap().findAll { it.value != null }} 133 | .findAll { !it.isEmpty() } 134 | 135 | if (converted.size() > 0) { 136 | return converted 137 | } else { 138 | return null 139 | } 140 | } 141 | } 142 | } 143 | 144 | @CompileStatic 145 | class ProjectSettings extends AbstractExtensibleSettings { 146 | private Project project 147 | private FrameworkDetectionExclusionSettings detectExclusions 148 | private IdeaFilesProcessor ideaFilesProcessor 149 | private Boolean generateImlFiles; 150 | 151 | private Gson gson = new Gson() 152 | 153 | ProjectSettings(Project project, IdeaFilesProcessor processor) { 154 | this.project = project 155 | ideaFilesProcessor = processor 156 | } 157 | 158 | def withIDEADir(Action action) { 159 | ideaFilesProcessor.withIDEAdir(action) 160 | } 161 | 162 | def withIDEAFileXml(String relativeFilePath, Action action) { 163 | ideaFilesProcessor.withIDEAFileXml(relativeFilePath, action) 164 | } 165 | 166 | def doNotDetectFrameworks(String... ids) { 167 | if (detectExclusions == null) { 168 | detectExclusions = project.objects.newInstance(FrameworkDetectionExclusionSettings) 169 | } 170 | detectExclusions.excludes.addAll(ids) 171 | } 172 | 173 | boolean getGenerateImlFiles() { 174 | return generateImlFiles 175 | } 176 | 177 | void setGenerateImlFiles(boolean generateImlFiles) { 178 | this.generateImlFiles = generateImlFiles 179 | } 180 | 181 | String toString() { 182 | def map = collectExtensionsMap() 183 | 184 | if (detectExclusions != null) { 185 | map.put("frameworkDetectionExcludes", detectExclusions.excludes) 186 | } 187 | 188 | if (ideaFilesProcessor.hasPostprocessors()) { 189 | map.put("requiresPostprocessing", true) 190 | } 191 | 192 | boolean hasNestedPostprocessors = project.allprojects.any { 193 | def ideaModel = it.extensions.findByName('idea') as IdeaModel 194 | if (!ideaModel) { return false } 195 | def moduleSettings = (ideaModel.module as ExtensionAware).extensions.findByName("settings") as ModuleSettings 196 | if (!moduleSettings) { return false } 197 | return moduleSettings.ideaFilesProcessor.hasPostprocessors() 198 | } 199 | 200 | if (hasNestedPostprocessors) { 201 | map.put("requiresPostprocessing", true) 202 | } 203 | 204 | if (generateImlFiles != null) { 205 | map.put("generateImlFiles", generateImlFiles); 206 | } 207 | 208 | return gson.toJson(map) 209 | } 210 | } 211 | 212 | 213 | class ModuleSettings extends AbstractExtensibleSettings { 214 | final PolymorphicDomainObjectContainer facets 215 | final IdeaFilesProcessor ideaFilesProcessor 216 | 217 | ModuleSettings(Project project, IdeaFilesProcessor processor) { 218 | def facets = GradleUtils.polymorphicContainer(project, Facet) 219 | 220 | facets.registerFactory(SpringFacet) { String name -> project.objects.newInstance(SpringFacet, name, project) } 221 | this.facets = facets 222 | ideaFilesProcessor = processor 223 | } 224 | 225 | def facets(Action> action) { 226 | action.execute(facets) 227 | } 228 | 229 | def withModuleFile(SourceSet s, Action action) { 230 | ideaFilesProcessor.withModuleFile(s, action) 231 | } 232 | 233 | def withModuleFile(Action action) { 234 | ideaFilesProcessor.withModuleFile(null, action) 235 | } 236 | 237 | def withModuleXml(SourceSet s, Action action) { 238 | ideaFilesProcessor.withModuleXml(s, action) 239 | } 240 | 241 | def withModuleXml(Action action) { 242 | ideaFilesProcessor.withModuleXml(null, action) 243 | } 244 | 245 | @Override 246 | String toString() { 247 | def map = collectExtensionsMap() 248 | if (!facets.isEmpty()) { 249 | map["facets"] = facets.asList().collect { it.toMap() } 250 | } 251 | return new GsonBuilder() 252 | .disableHtmlEscaping() 253 | .serializeNulls() 254 | .create() 255 | .toJson(map) 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /src/main/groovy/org/jetbrains/gradle/ext/IdeaFilesProcessor.groovy: -------------------------------------------------------------------------------- 1 | package org.jetbrains.gradle.ext 2 | 3 | import com.google.gson.Gson 4 | import groovy.transform.CompileStatic 5 | import groovy.transform.Synchronized 6 | import org.gradle.BuildListener 7 | import org.gradle.BuildResult 8 | import org.gradle.api.Action 9 | import org.gradle.api.DefaultTask 10 | import org.gradle.api.Project 11 | import org.gradle.api.Task 12 | import org.gradle.api.UnknownTaskException 13 | import org.gradle.api.XmlProvider 14 | import org.gradle.api.initialization.Settings 15 | import org.gradle.api.provider.Property 16 | import org.gradle.api.services.BuildServiceSpec 17 | import org.gradle.api.tasks.Internal 18 | import org.gradle.api.tasks.SourceSet 19 | import org.gradle.api.tasks.TaskAction 20 | import org.gradle.api.tasks.TaskProvider 21 | import org.gradle.internal.xml.XmlTransformer 22 | import org.gradle.util.GradleVersion 23 | import org.gradle.api.logging.Logger 24 | 25 | import javax.inject.Inject 26 | import java.nio.charset.StandardCharsets 27 | 28 | @CompileStatic 29 | class IdeaFilesProcessor { 30 | Logger logger 31 | String imlKey 32 | File layoutFile 33 | 34 | List> ideaDirCallbacks = new ArrayList<>() 35 | Map>> ideaFileXmlCallbacks = new HashMap>>() 36 | Map>> imlsCallbacks = new HashMap>>() 37 | Map>> xmlCallbacks = new HashMap>>() 38 | 39 | IdeaFilesProcessor(Project project) { 40 | logger = project.logger 41 | imlKey = project.path == ":" ? project.name : project.path 42 | layoutFile = project.rootProject.file("layout.json") 43 | 44 | def myProject = project 45 | if (GradleVersion.current() > GradleVersion.version("6.1")) { 46 | def serviceProvider = project.getGradle().getSharedServices() 47 | .registerIfAbsent("layoutFile", LayoutFileBuildService.class, 48 | new Action>() { 49 | @Override 50 | void execute(BuildServiceSpec spec) { 51 | spec.getParameters().getLayoutFile().set(myProject.rootProject.file("layout.json")) 52 | } 53 | }) 54 | def that = this 55 | 56 | if (findExistingTask(myProject) != null) { 57 | logger.warn("Using more than one ${this.class.name} for project [$myProject.path] is not expected") 58 | } else { 59 | myProject.tasks.register("processIdeaSettings", ProcessIdeaFilesWithServiceTask, 60 | new Action() { 61 | @Override 62 | void execute(ProcessIdeaFilesWithServiceTask task) { 63 | task.usesService(serviceProvider) 64 | task.service.set(serviceProvider) 65 | task.myProcessor = that 66 | } 67 | } 68 | ) 69 | } 70 | } else { 71 | myProject.tasks.create("processIdeaSettings", ProcessIdeaFilesTask, this) 72 | installBuildFinishedListener(project) 73 | } 74 | } 75 | 76 | private static TaskProvider findExistingTask(Project myProject) { 77 | try { 78 | return myProject.tasks.named("processIdeaSettings") 79 | } catch (UnknownTaskException ignore) { } 80 | return null 81 | } 82 | 83 | def withIDEAdir(Action callback) { 84 | ideaDirCallbacks.add(callback) 85 | } 86 | 87 | def withIDEAFileXml(String relativeFilePath, Action callback) { 88 | put(ideaFileXmlCallbacks, relativeFilePath, callback) 89 | } 90 | 91 | def withModuleFile(SourceSet s, Action callback) { 92 | put(imlsCallbacks, getName(s), callback) 93 | } 94 | 95 | def withModuleXml(SourceSet s, Action callback) { 96 | put(xmlCallbacks, getName(s), callback) 97 | } 98 | 99 | static void put(Map> collection, String key, V value) { 100 | collection.computeIfAbsent(key, { new ArrayList() }).add(value) 101 | } 102 | 103 | static String getName(SourceSet s) { 104 | return s == null ? "" : "$s.name" 105 | } 106 | 107 | def process() { 108 | process(layoutFile) 109 | } 110 | 111 | def process(File layoutFile) { 112 | def gson = new Gson() 113 | def file = layoutFile 114 | if (!file.exists()) { 115 | logger.lifecycle("IDEA layout file 'layout.json' was not found, terminating.") 116 | return 117 | } 118 | 119 | final IdeaLayoutJson layout = gson.fromJson(file.text, IdeaLayoutJson) 120 | def ideaDir = new File(layout.ideaDirPath) 121 | ideaDirCallbacks.each { it.execute(ideaDir) } 122 | 123 | ideaFileXmlCallbacks.each { entry -> 124 | def ideaFile = new File(ideaDir, entry.getKey()) 125 | def transformer = new XmlTransformer() 126 | entry.value.each {transformer.addAction(it) } 127 | def result = transformer.transform(ideaFile.text) 128 | ideaFile.write(result, StandardCharsets.UTF_8.name()) 129 | } 130 | 131 | imlsCallbacks.each { entry -> 132 | def imlPath = lookUpImlPath(layout, entry.key) 133 | if (imlPath == null) { 134 | logger.warn("No path to iml is present for key [${entry.key}].\nLayout: $layout") 135 | return 136 | } 137 | def moduleFile = new File(imlPath) 138 | entry.value.each { it.execute(moduleFile) } 139 | } 140 | 141 | xmlCallbacks.each { entry -> 142 | String imlPath = lookUpImlPath(layout, entry.key) 143 | if (imlPath == null) { 144 | logger.warn("No path to iml is present for key [${entry.key}].\\nLayout: $layout") 145 | return 146 | } 147 | def moduleFile = new File(imlPath) 148 | def transformer = new XmlTransformer() 149 | entry.value.each { transformer.addAction(it) } 150 | def result = transformer.transform(moduleFile.text) 151 | moduleFile.write(result, StandardCharsets.UTF_8.name()) 152 | } 153 | } 154 | 155 | private String lookUpImlPath(IdeaLayoutJson layout, String sourceSetName) { 156 | def imlKey = this.imlKey 157 | if (sourceSetName != "") { 158 | imlKey = imlKey + ":${sourceSetName}" 159 | } 160 | return layout.modulesMap.get(imlKey) 161 | } 162 | 163 | boolean hasPostprocessors() { 164 | return !ideaDirCallbacks.isEmpty() || 165 | !imlsCallbacks.isEmpty() || 166 | !ideaFileXmlCallbacks.isEmpty() || 167 | !xmlCallbacks.isEmpty() 168 | } 169 | 170 | @Synchronized 171 | void installBuildFinishedListener(Project project) { 172 | def listener = new FilesProcessorBuildListener() 173 | project.gradle.addBuildListener(listener) 174 | } 175 | } 176 | 177 | class ProcessIdeaFilesTask extends DefaultTask { 178 | @Internal 179 | IdeaFilesProcessor myProcessor 180 | 181 | @Inject 182 | ProcessIdeaFilesTask(IdeaFilesProcessor processor) { 183 | myProcessor = processor 184 | } 185 | @TaskAction 186 | def process() { 187 | myProcessor.process() 188 | } 189 | } 190 | 191 | abstract class ProcessIdeaFilesWithServiceTask extends DefaultTask { 192 | @Internal 193 | IdeaFilesProcessor myProcessor 194 | 195 | @Internal 196 | abstract Property getService(); 197 | 198 | @TaskAction 199 | def process() { 200 | myProcessor.process(getService().get().getFilePath()) 201 | } 202 | } 203 | 204 | 205 | class IdeaLayoutJson { 206 | public String ideaDirPath 207 | public Map modulesMap 208 | } 209 | 210 | class FilesProcessorBuildListener implements BuildListener { 211 | @Override 212 | void buildStarted(org.gradle.api.invocation.Gradle gradle) { 213 | } 214 | 215 | @Override 216 | void settingsEvaluated(Settings settings) { 217 | } 218 | 219 | @Override 220 | void projectsLoaded(org.gradle.api.invocation.Gradle gradle) { 221 | } 222 | 223 | @Override 224 | void projectsEvaluated(org.gradle.api.invocation.Gradle gradle) { 225 | } 226 | 227 | @Override 228 | void buildFinished(BuildResult buildResult) { 229 | def file = buildResult.getGradle().getRootProject().file("layout.json") 230 | if (file.exists()) { 231 | file.delete() 232 | } 233 | } 234 | } 235 | 236 | -------------------------------------------------------------------------------- /src/main/groovy/org/jetbrains/gradle/ext/Inspections.groovy: -------------------------------------------------------------------------------- 1 | package org.jetbrains.gradle.ext 2 | 3 | import groovy.transform.CompileStatic 4 | 5 | import javax.inject.Inject 6 | 7 | @CompileStatic 8 | class Inspection implements MapConvertible { 9 | final String name 10 | Boolean enabled 11 | 12 | @Inject 13 | Inspection(String name) { 14 | this.name = name 15 | } 16 | 17 | @Override 18 | Map toMap() { 19 | return [ 20 | "enabled": enabled, 21 | "name" : name 22 | ] 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/groovy/org/jetbrains/gradle/ext/LayoutFileBuildService.groovy: -------------------------------------------------------------------------------- 1 | package org.jetbrains.gradle.ext 2 | 3 | import groovy.transform.CompileStatic 4 | import org.gradle.api.provider.Property 5 | import org.gradle.api.services.BuildService 6 | import org.gradle.api.services.BuildServiceParameters 7 | 8 | @CompileStatic 9 | abstract class LayoutFileBuildService implements BuildService, AutoCloseable { 10 | interface Params extends BuildServiceParameters { 11 | Property getLayoutFile(); 12 | } 13 | 14 | File getFilePath() { 15 | return getParameters().getLayoutFile().get() 16 | } 17 | 18 | @Override 19 | void close() throws Exception { 20 | def file = getParameters().getLayoutFile().get() 21 | if (file != null && file.exists()) { 22 | file.delete() 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/groovy/org/jetbrains/gradle/ext/MapConvertible.groovy: -------------------------------------------------------------------------------- 1 | package org.jetbrains.gradle.ext 2 | 3 | import groovy.transform.CompileStatic 4 | 5 | @CompileStatic 6 | interface MapConvertible { 7 | Map toMap() 8 | } -------------------------------------------------------------------------------- /src/main/groovy/org/jetbrains/gradle/ext/ModuleTypesConfig.groovy: -------------------------------------------------------------------------------- 1 | package org.jetbrains.gradle.ext 2 | 3 | import org.gradle.api.Project 4 | import org.gradle.api.plugins.ExtensionContainer 5 | import org.gradle.api.plugins.ExtraPropertiesExtension 6 | import org.gradle.api.tasks.SourceSet 7 | 8 | import javax.inject.Inject 9 | 10 | class ModuleTypesConfig implements MapConvertible { 11 | 12 | Project project 13 | Map typesMap = new LinkedHashMap<>() 14 | String rootType 15 | private propertiesExtension 16 | 17 | @Inject 18 | ModuleTypesConfig(Project project, ExtensionContainer extensionContainer) { 19 | this.project = project 20 | propertiesExtension = extensionContainer.getByName("ext") as ExtraPropertiesExtension 21 | propertiesExtension.set("rootModuleType", "") 22 | } 23 | 24 | void putAt(SourceSet targetSourceSet, String moduleType) { 25 | if (!project.hasProperty('sourceSets')) { 26 | project.logger.warn("Can not register a module type [$moduleType] for a source set [$targetSourceSet] as no source sets are configured") 27 | return 28 | } 29 | if (!project.sourceSets.contains(targetSourceSet)) { 30 | project.logger.warn("Attempt to set a module type [$moduleType] for a source set [$targetSourceSet] that does not belong to current project") 31 | return 32 | } 33 | typesMap[targetSourceSet] = moduleType 34 | } 35 | 36 | String getAt(SourceSet s) { 37 | return typesMap[s] 38 | } 39 | 40 | @Override 41 | Map toMap() { 42 | 43 | Map result = [:] 44 | def rootType = propertiesExtension.get("rootModuleType") 45 | if (rootType != null && "" != rootType) { 46 | result << ["": rootType] 47 | } 48 | result << typesMap.collectEntries { 49 | [it.key.name, it.value] 50 | } 51 | 52 | return result 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/groovy/org/jetbrains/gradle/ext/PackagePrefixContainer.groovy: -------------------------------------------------------------------------------- 1 | package org.jetbrains.gradle.ext 2 | 3 | import groovy.transform.CompileStatic 4 | import org.gradle.plugins.ide.idea.model.IdeaModule 5 | 6 | import javax.inject.Inject 7 | 8 | @CompileStatic 9 | class PackagePrefixContainer extends LinkedHashMap implements MapConvertible { 10 | 11 | private final IdeaModule module 12 | 13 | @Inject 14 | PackagePrefixContainer(IdeaModule module) { 15 | this.module = module 16 | } 17 | 18 | @Override 19 | Map toMap() { 20 | def contentRoot = module.contentRoot.absolutePath 21 | def result = new LinkedHashMap() 22 | for (def entry : this) { 23 | def path = entry.key 24 | def prefix = entry.value 25 | def file = new File(contentRoot, path) 26 | def sourceDir = file.path.replace(File.separator, '/') 27 | result.put(sourceDir, prefix) 28 | } 29 | return result 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/groovy/org/jetbrains/gradle/ext/PropertiesEncodingConfiguration.groovy: -------------------------------------------------------------------------------- 1 | package org.jetbrains.gradle.ext 2 | 3 | import groovy.transform.CompileStatic 4 | 5 | @CompileStatic 6 | class PropertiesEncodingConfiguration implements MapConvertible { 7 | 8 | String encoding = null 9 | Boolean transparentNativeToAsciiConversion = null 10 | 11 | @Override 12 | Map toMap() { 13 | return [ 14 | 'encoding' : encoding, 15 | 'transparentNativeToAsciiConversion': transparentNativeToAsciiConversion 16 | ] 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/groovy/org/jetbrains/gradle/ext/RunConfigurations.groovy: -------------------------------------------------------------------------------- 1 | package org.jetbrains.gradle.ext 2 | 3 | import groovy.transform.CompileStatic 4 | import org.codehaus.groovy.runtime.DefaultGroovyMethods 5 | import org.gradle.api.Action 6 | import org.gradle.api.ExtensiblePolymorphicDomainObjectContainer 7 | import org.gradle.api.Named 8 | import org.gradle.api.PolymorphicDomainObjectContainer 9 | import org.gradle.api.Project 10 | import org.gradle.api.Task 11 | import org.gradle.api.tasks.SourceSet 12 | import org.gradle.api.tasks.SourceSetContainer 13 | 14 | import javax.inject.Inject 15 | 16 | @CompileStatic 17 | interface RunConfigurationContainer extends ExtensiblePolymorphicDomainObjectContainer { 18 | public void defaults(Class type, Action action) 19 | } 20 | 21 | @CompileStatic 22 | interface RunConfiguration extends Named, MapConvertible { 23 | String getType() 24 | boolean isDefaults() 25 | void setDefaults(boolean defaults) 26 | } 27 | 28 | @CompileStatic 29 | abstract class BaseRunConfiguration implements RunConfiguration { 30 | boolean defaults 31 | protected String name 32 | protected String type 33 | 34 | @Override 35 | String getType() { 36 | return type 37 | } 38 | 39 | @Override 40 | String getName() { 41 | return name 42 | } 43 | 44 | @Override 45 | Map toMap() { 46 | return [ 47 | "defaults" : defaults, 48 | "type": type, 49 | "name": name 50 | ] 51 | } 52 | } 53 | 54 | @CompileStatic 55 | abstract class ModuleRunConfiguration extends BaseRunConfiguration { 56 | String moduleName 57 | ModuleRef moduleRef 58 | 59 | void setModuleName(String name) { 60 | moduleName = name 61 | moduleRef = null 62 | } 63 | 64 | void setModuleRef(ModuleRef ref) { 65 | moduleRef = ref 66 | moduleName = null 67 | } 68 | 69 | def moduleRef(Project project, SourceSet sourceSet = null) { 70 | setModuleRef(new ModuleRef(project, sourceSet)) 71 | } 72 | 73 | @Override 74 | Map toMap() { 75 | String resultingModuleName = null 76 | 77 | if (moduleName != null) { 78 | resultingModuleName = moduleName 79 | } else if (moduleRef != null) { 80 | resultingModuleName = moduleRef.toModuleName() 81 | } 82 | 83 | return super.toMap() << [ 84 | "moduleName" : resultingModuleName, 85 | ] 86 | } 87 | } 88 | 89 | @CompileStatic 90 | abstract class JavaRunConfiguration extends ModuleRunConfiguration { 91 | String workingDirectory 92 | String jvmArgs 93 | String programParameters 94 | Map envs 95 | 96 | final PolymorphicDomainObjectContainer beforeRun 97 | 98 | static PolymorphicDomainObjectContainer createBeforeRun(Project project) { 99 | def beforeRun = GradleUtils.polymorphicContainer(project, BeforeRunTask) 100 | beforeRun.registerFactory(Make) { String name -> project.objects.newInstance(Make, name) } 101 | beforeRun.registerFactory(GradleTask) { String name -> project.objects.newInstance(GradleTask, name) } 102 | beforeRun.registerFactory(BuildArtifact) { String name -> project.objects.newInstance(BuildArtifact, name) } 103 | return beforeRun 104 | } 105 | 106 | JavaRunConfiguration(Project project) { 107 | this.beforeRun = createBeforeRun(project) 108 | } 109 | 110 | def beforeRun(Action> action) { 111 | action.execute(beforeRun) 112 | } 113 | 114 | @Override 115 | Map toMap() { 116 | return super.toMap() << [ 117 | "envs" : envs, 118 | "workingDirectory" : workingDirectory, 119 | "beforeRun" : DefaultGroovyMethods.collect(beforeRun.toList() as Collection) { it.toMap() }, 120 | "jvmArgs" : jvmArgs, 121 | "programParameters": programParameters 122 | ] 123 | } 124 | } 125 | 126 | @CompileStatic 127 | class Application extends JavaRunConfiguration { 128 | String mainClass 129 | ShortenCommandLine shortenCommandLine 130 | Boolean includeProvidedDependencies = false 131 | 132 | @Inject 133 | Application(String nameParam, Project project) { 134 | super(project) 135 | name = nameParam 136 | type = "application" 137 | } 138 | 139 | @Override 140 | Map toMap() { 141 | return super.toMap() << [ 142 | "mainClass" : mainClass, 143 | "shortenCommandLine": shortenCommandLine, 144 | "includeProvidedDependencies": includeProvidedDependencies 145 | ] 146 | } 147 | } 148 | 149 | @CompileStatic 150 | class JarApplication extends JavaRunConfiguration { 151 | String jarPath 152 | 153 | @Inject 154 | JarApplication(String nameParam, Project project) { 155 | super(project) 156 | name = nameParam 157 | type = "jarApplication" 158 | } 159 | 160 | @Override 161 | Map toMap() { 162 | return super.toMap() << [ 163 | "jarPath": jarPath 164 | ] 165 | } 166 | } 167 | 168 | @CompileStatic 169 | abstract class BeforeRunTask implements Named, MapConvertible { 170 | protected String type 171 | protected String name 172 | 173 | @Override 174 | String getName() { 175 | return name 176 | } 177 | 178 | @Override 179 | Map toMap() { 180 | return ["type" : type] 181 | } 182 | } 183 | 184 | @CompileStatic 185 | class Make extends BeforeRunTask { 186 | Boolean enabled = true 187 | 188 | @Inject 189 | Make(String nameParam) { 190 | type = "make" 191 | name = nameParam 192 | } 193 | 194 | @Override 195 | Map toMap() { 196 | return super.toMap() << ["enabled" : enabled] 197 | } 198 | } 199 | 200 | @CompileStatic 201 | class GradleTask extends BeforeRunTask { 202 | Task task 203 | 204 | @Inject 205 | GradleTask(String nameParam) { 206 | type = "gradleTask" 207 | name = nameParam 208 | } 209 | 210 | @Override 211 | Map toMap() { 212 | return super.toMap() << [ 213 | "projectPath": task.project.projectDir.absolutePath.replaceAll("\\\\", "/"), 214 | "taskName": task.name 215 | ] 216 | } 217 | } 218 | 219 | @CompileStatic 220 | class BuildArtifact extends BeforeRunTask { 221 | String artifactName 222 | 223 | @Inject 224 | BuildArtifact(String nameParam) { 225 | type = "buildArtifact" 226 | name = nameParam 227 | } 228 | 229 | @Override 230 | Map toMap() { 231 | return super.toMap() << ["artifactName" : artifactName] 232 | } 233 | } 234 | 235 | @CompileStatic 236 | class TestNG extends ModuleRunConfiguration { 237 | 238 | // only one type will be used 239 | String packageName 240 | String className 241 | String method 242 | String group 243 | String suite 244 | String pattern 245 | // 246 | 247 | String workingDirectory 248 | String vmParameters 249 | Boolean passParentEnvs 250 | Map envs 251 | ShortenCommandLine shortenCommandLine 252 | 253 | @Inject 254 | TestNG(String nameParam) { 255 | name = nameParam 256 | type = "testng" 257 | } 258 | 259 | @Override 260 | Map toMap() { 261 | return super.toMap() << [ 262 | "type": type, 263 | "name": name, 264 | 265 | "package": packageName, 266 | "class": className, 267 | "method": method, 268 | "group": group, 269 | "suite": suite, 270 | "pattern": pattern, 271 | "workingDirectory": workingDirectory, 272 | "vmParameters": vmParameters, 273 | "passParentEnvs": passParentEnvs, 274 | "envs": envs, 275 | "shortenCommandLine": shortenCommandLine 276 | ] 277 | } 278 | } 279 | 280 | @CompileStatic 281 | class JUnit extends ModuleRunConfiguration { 282 | 283 | // only one (first not null) type will be used 284 | String packageName 285 | String directory 286 | String pattern 287 | String className 288 | String method 289 | String category 290 | // end of type list 291 | 292 | String repeat 293 | String workingDirectory 294 | String vmParameters 295 | Boolean passParentEnvs 296 | Map envs 297 | ShortenCommandLine shortenCommandLine 298 | 299 | @Inject 300 | JUnit(String nameParam) { 301 | name = nameParam 302 | type = "junit" 303 | } 304 | 305 | @Override 306 | Map toMap() { 307 | return super.toMap() << [ 308 | "directory" : directory, 309 | "repeat" : repeat, 310 | "envs" : envs, 311 | "vmParameters" : vmParameters, 312 | "category" : category, 313 | "workingDirectory": workingDirectory, 314 | "className" : className, 315 | "passParentEnvs" : passParentEnvs, 316 | "packageName" : packageName, 317 | "defaults" : defaults, 318 | "pattern" : pattern, 319 | "method" : method, 320 | "shortenCommandLine" : shortenCommandLine 321 | ] 322 | } 323 | } 324 | 325 | @CompileStatic 326 | class Remote extends BaseRunConfiguration { 327 | static enum RemoteMode { 328 | ATTACH, LISTEN 329 | } 330 | 331 | static enum RemoteTransport { 332 | SOCKET, SHARED_MEM 333 | } 334 | 335 | RemoteTransport transport = RemoteTransport.SOCKET 336 | RemoteMode mode = RemoteMode.ATTACH 337 | String host 338 | Integer port 339 | String sharedMemoryAddress 340 | Boolean autoRestart = false 341 | 342 | @Inject 343 | Remote(String nameParam) { 344 | name = nameParam 345 | type = "remote" 346 | } 347 | 348 | @Override 349 | Map toMap() { 350 | return super.toMap() << [ 351 | "mode" : mode, 352 | "port" : port, 353 | "transport" : transport, 354 | "host" : host, 355 | "sharedMemoryAddress": sharedMemoryAddress, 356 | "autoRestart" : autoRestart 357 | ] 358 | } 359 | } 360 | 361 | @CompileStatic 362 | class Gradle extends BaseRunConfiguration { 363 | 364 | String projectPath 365 | Collection taskNames 366 | Map envs 367 | String jvmArgs 368 | String scriptParameters 369 | 370 | @Inject 371 | Gradle(String nameParam) { 372 | name = nameParam 373 | type = "gradle" 374 | } 375 | 376 | def setProject(Project project) { 377 | projectPath = project.projectDir.absolutePath 378 | } 379 | 380 | @Override 381 | Map toMap() { 382 | return super.toMap() << [ 383 | "projectPath" : projectPath, 384 | "taskNames" : taskNames, 385 | "envs" : envs, 386 | "jvmArgs" : jvmArgs, 387 | "scriptParameters": scriptParameters 388 | ] 389 | } 390 | } 391 | 392 | @CompileStatic 393 | class ModuleRef { 394 | 395 | Project project 396 | String sourceSetName = "" 397 | 398 | /** 399 | * Creates a reference to a module based on project and a source set 400 | * If a source set is not null, it must belong to the project 401 | * @param project Gradle project 402 | * @param sourceSet Source set within project, optional. 403 | * @throw IllegalArgumentException if source set does not belong to project 404 | */ 405 | ModuleRef(Project project, SourceSet sourceSet) { 406 | this.project = project 407 | if (sourceSet != null && project.hasProperty("sourceSets")) { 408 | SourceSetContainer sourceSets = project.property("sourceSets") as SourceSetContainer 409 | if (sourceSets.contains(sourceSet)) { 410 | this.sourceSetName = sourceSet.name 411 | } else { 412 | throw new IllegalArgumentException("Source set $sourceSet does not belong to $project") 413 | } 414 | } 415 | } 416 | 417 | ModuleRef(Project project) { 418 | this(project, null) 419 | } 420 | 421 | String toModuleName() { 422 | def name = project.rootProject.name 423 | if (project.path == ":") { 424 | return addSourceSetName(name) 425 | } 426 | return addSourceSetName(name + project.path.replaceAll(":", ".")) 427 | } 428 | 429 | String addSourceSetName(String moduleName) { 430 | return moduleName + (sourceSetName.isEmpty() ? "" : "." + sourceSetName) 431 | } 432 | } 433 | 434 | @CompileStatic 435 | enum ShortenCommandLine { 436 | NONE, 437 | MANIFEST, 438 | CLASSPATH_FILE, 439 | ARGS_FILE 440 | } -------------------------------------------------------------------------------- /src/main/groovy/org/jetbrains/gradle/ext/TaskTriggersConfig.groovy: -------------------------------------------------------------------------------- 1 | package org.jetbrains.gradle.ext 2 | 3 | import com.google.common.collect.ListMultimap 4 | import com.google.common.collect.Multimaps 5 | import org.gradle.api.Project 6 | import org.gradle.api.Task 7 | import org.gradle.api.provider.Provider 8 | import org.gradle.api.tasks.TaskCollection 9 | 10 | class TaskTriggersConfig implements MapConvertible { 11 | 12 | Project project 13 | ListMultimap phaseMap = Multimaps 14 | .newListMultimap(new LinkedHashMap>(), 15 | { new ArrayList()}) 16 | 17 | TaskTriggersConfig(Project project) { 18 | this.project = project 19 | } 20 | 21 | void beforeSync(Object... tasks) { 22 | phaseMap.putAll("beforeSync", Arrays.asList(tasks)) 23 | } 24 | void afterSync(Object... tasks) { 25 | phaseMap.putAll("afterSync", Arrays.asList(tasks)) 26 | } 27 | void beforeBuild(Object... tasks) { 28 | phaseMap.putAll("beforeBuild", Arrays.asList(tasks)) 29 | } 30 | void afterBuild(Object... tasks) { 31 | phaseMap.putAll("afterBuild", Arrays.asList(tasks)) 32 | } 33 | void beforeRebuild(Object... tasks) { 34 | phaseMap.putAll("beforeRebuild", Arrays.asList(tasks)) 35 | } 36 | void afterRebuild(Object... tasks) { 37 | phaseMap.putAll("afterRebuild", Arrays.asList(tasks)) 38 | } 39 | 40 | @Override 41 | Map toMap() { 42 | def result = new LinkedHashMap() 43 | phaseMap.keySet().each { String phase -> 44 | List tasksObjects = phaseMap.get(phase) 45 | List tasks = (List) tasksObjects.collect { resolveTasks(it) }.findAll { !it.isEmpty() }.flatten() 46 | def taskInfos = tasks.collect { task -> ["taskPath" : task.name, "projectPath" : task.project.projectDir.path.replaceAll("\\\\", "/")] } 47 | result.put(phase, taskInfos) 48 | } 49 | return result 50 | } 51 | 52 | List resolveTasks(Object taskObject) { 53 | if (taskObject instanceof Task) { 54 | return Collections.singletonList((Task)taskObject) 55 | } else if (taskObject instanceof Provider) { 56 | return resolveTasks(((Provider)taskObject).get()) 57 | } else if (taskObject instanceof String) { 58 | return Collections.singletonList(project.tasks.findByPath(taskObject)); 59 | } else if (taskObject instanceof TaskCollection) { 60 | return ((TaskCollection)taskObject).asList() 61 | } else { 62 | return Collections.emptyList() 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /src/main/groovy/org/jetbrains/gradle/ext/internal/DefaultRunConfigurationContainer.groovy: -------------------------------------------------------------------------------- 1 | package org.jetbrains.gradle.ext.internal 2 | 3 | import groovy.transform.CompileStatic 4 | import org.gradle.api.Action 5 | import org.gradle.api.internal.DefaultPolymorphicDomainObjectContainer 6 | import org.gradle.internal.reflect.Instantiator 7 | import org.jetbrains.gradle.ext.RunConfiguration 8 | import org.jetbrains.gradle.ext.RunConfigurationContainer 9 | 10 | import javax.inject.Inject 11 | 12 | /** 13 | * This class is only used with Gradle <5.1. 14 | * With 5.1+ the {@link DefaultRunConfigurationContainer51} will be created 15 | */ 16 | @CompileStatic 17 | class DefaultRunConfigurationContainer 18 | extends DefaultPolymorphicDomainObjectContainer 19 | implements RunConfigurationContainer { 20 | 21 | @Inject 22 | DefaultRunConfigurationContainer(Instantiator instantiator) { 23 | super(RunConfiguration, instantiator) 24 | } 25 | 26 | @Override 27 | public void defaults(Class type, Action action) { 28 | def defaults = maybeCreate("default_$type.name", type) 29 | defaults.defaults = true 30 | action.execute(defaults) 31 | } 32 | } 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/main/groovy/org/jetbrains/gradle/ext/internal/DefaultRunConfigurationContainer51.groovy: -------------------------------------------------------------------------------- 1 | package org.jetbrains.gradle.ext.internal 2 | 3 | import groovy.transform.CompileStatic 4 | import org.gradle.api.Action 5 | import org.gradle.api.internal.CollectionCallbackActionDecorator 6 | import org.gradle.api.internal.DefaultPolymorphicDomainObjectContainer 7 | import org.gradle.internal.reflect.Instantiator 8 | import org.jetbrains.gradle.ext.RunConfiguration 9 | import org.jetbrains.gradle.ext.RunConfigurationContainer 10 | 11 | import javax.inject.Inject 12 | 13 | @CompileStatic 14 | class DefaultRunConfigurationContainer51 extends DefaultPolymorphicDomainObjectContainer 15 | implements RunConfigurationContainer { 16 | 17 | @Inject 18 | DefaultRunConfigurationContainer51(Instantiator instantiator) { 19 | super(RunConfiguration, instantiator, CollectionCallbackActionDecorator.NOOP) 20 | } 21 | 22 | @Override 23 | public void defaults(Class type, Action action) { 24 | def defaults = maybeCreate("default_$type.name", type) 25 | defaults.defaults = true 26 | action.execute(defaults) 27 | } 28 | } -------------------------------------------------------------------------------- /src/main/kotlin/org/jetbrains/gradle/ext/IdeaModelUtils.kt: -------------------------------------------------------------------------------- 1 | package org.jetbrains.gradle.ext 2 | 3 | import org.gradle.api.NamedDomainObjectContainer 4 | import org.gradle.api.plugins.ExtensionAware 5 | import org.gradle.api.tasks.SourceSet 6 | import org.gradle.kotlin.dsl.the 7 | import org.gradle.plugins.ide.idea.model.IdeaModule 8 | import org.gradle.plugins.ide.idea.model.IdeaProject 9 | 10 | val IdeaProject.settings: ProjectSettings 11 | get() = (this as ExtensionAware).the() 12 | 13 | fun IdeaProject.settings(configure: ProjectSettings.() -> Unit) { 14 | configure(this.settings) 15 | } 16 | 17 | // compiler 18 | val ProjectSettings.compiler: IdeaCompilerConfiguration 19 | get() = (this as ExtensionAware).the() 20 | 21 | fun ProjectSettings.compiler(configure: IdeaCompilerConfiguration.() -> Unit) { 22 | configure(this.compiler) 23 | } 24 | 25 | //delegateActions 26 | val ProjectSettings.delegateActions: ActionDelegationConfig 27 | get() = (this as ExtensionAware).the() 28 | 29 | fun ProjectSettings.delegateActions(configure: ActionDelegationConfig.() -> Unit) { 30 | configure(this.delegateActions) 31 | } 32 | //taskTriggers 33 | val ProjectSettings.taskTriggers: TaskTriggersConfig 34 | get() = (this as ExtensionAware).the() 35 | 36 | fun ProjectSettings.taskTriggers(configure: TaskTriggersConfig.() -> Unit) { 37 | configure(this.taskTriggers) 38 | } 39 | 40 | //groovyCompiler 41 | val ProjectSettings.groovyCompiler: GroovyCompilerConfiguration 42 | get() = (this as ExtensionAware).the() 43 | 44 | fun ProjectSettings.groovyCompiler(configure: GroovyCompilerConfiguration.() -> Unit) { 45 | configure(this.groovyCompiler) 46 | } 47 | 48 | //copyright 49 | val ProjectSettings.copyright: CopyrightConfiguration 50 | get() = (this as ExtensionAware).the() 51 | 52 | fun ProjectSettings.copyright(configure: CopyrightConfiguration.() -> Unit) { 53 | configure(this.copyright) 54 | } 55 | 56 | //encodings 57 | val ProjectSettings.encodings: EncodingConfiguration 58 | get() = (this as ExtensionAware).the() 59 | 60 | fun ProjectSettings.encodings(configure: EncodingConfiguration.() -> Unit) { 61 | configure(this.encodings) 62 | } 63 | 64 | //runConfigurations 65 | val ProjectSettings.runConfigurations: RunConfigurationContainer 66 | get() = (this as ExtensionAware).the() 67 | 68 | fun ProjectSettings.runConfigurations(configure: RunConfigurationContainer.() -> Unit) { 69 | configure(this.runConfigurations) 70 | } 71 | 72 | //inspections 73 | val ProjectSettings.inspections: NamedDomainObjectContainer 74 | get() = (this as ExtensionAware).the() 75 | 76 | fun ProjectSettings.inspections(configure: NamedDomainObjectContainer.() -> Unit) { 77 | configure(this.inspections) 78 | } 79 | 80 | //ideArtifacts 81 | val ProjectSettings.ideArtifacts: NamedDomainObjectContainer 82 | get() = (this as ExtensionAware).the() 83 | 84 | fun ProjectSettings.ideArtifacts(configure: NamedDomainObjectContainer.() -> Unit) { 85 | configure(this.ideArtifacts) 86 | } 87 | 88 | 89 | 90 | val IdeaModule.settings: ModuleSettings 91 | get() = (this as ExtensionAware).the() 92 | fun IdeaModule.settings(configure: ModuleSettings.() -> Unit) { 93 | configure(this.settings) 94 | } 95 | 96 | //packagePrefix 97 | val ModuleSettings.packagePrefix: PackagePrefixContainer 98 | get() = (this as ExtensionAware).the() 99 | 100 | fun ModuleSettings.packagePrefix(configure: PackagePrefixContainer.() -> Unit) { 101 | configure(this.packagePrefix) 102 | } 103 | 104 | //moduleType 105 | val ModuleSettings.moduleType: ModuleTypesConfig 106 | get() = (this as ExtensionAware).the() 107 | 108 | fun ModuleSettings.moduleType(configure: ModuleTypesConfig.() -> Unit) { 109 | configure(this.moduleType) 110 | } 111 | 112 | operator fun ModuleTypesConfig.set(sourceSet: SourceSet, typeName: String?) { 113 | this.putAt(sourceSet, typeName) 114 | } 115 | 116 | -------------------------------------------------------------------------------- /src/test/groovy/TestIdeaExtPlugin.groovy: -------------------------------------------------------------------------------- 1 | import com.google.gson.Gson 2 | import org.gradle.testkit.runner.GradleRunner 3 | import org.gradle.testkit.runner.TaskOutcome 4 | import org.gradle.util.GradleVersion 5 | import org.jetbrains.gradle.ext.SerializationUtil 6 | import org.junit.Rule 7 | import org.junit.rules.TemporaryFolder 8 | import spock.lang.Specification 9 | 10 | import static org.assertj.core.api.Assertions.assertThat 11 | 12 | class IdeaModelExtensionFunctionalTest extends Specification { 13 | @Rule 14 | TemporaryFolder testProjectDir = new TemporaryFolder() 15 | File buildFile 16 | File settingsFile 17 | 18 | static List gradleVersionList = ["5.0", "5.6.4", "6.0", "6.8.3", "7.0", "7.2"] 19 | 20 | def setup() { 21 | buildFile = testProjectDir.newFile('build.gradle') 22 | settingsFile = testProjectDir.newFile('settings.gradle') 23 | } 24 | 25 | def "test project settings"() { 26 | given: 27 | settingsFile << """ 28 | rootProject.name = "ProjectName" 29 | """ 30 | // language=groovy 31 | buildFile << """ 32 | import org.jetbrains.gradle.ext.* 33 | 34 | plugins { 35 | id 'org.jetbrains.gradle.plugin.idea-ext' 36 | } 37 | 38 | idea { 39 | project { 40 | settings { 41 | copyright { 42 | profiles { 43 | myProfile { 44 | notice = "My private license text placeholder" 45 | } 46 | } 47 | useDefault = "myProfile" 48 | } 49 | generateImlFiles = true 50 | compiler.resourcePatterns '!*.java;!*.class' 51 | inspections { 52 | "some" { enabled = true } 53 | } 54 | runConfigurations { 55 | "Run my app"(Application) { 56 | mainClass = 'foo.App' 57 | workingDirectory = "\$projectDir" 58 | moduleRef project.getProject() 59 | includeProvidedDependencies = true 60 | } 61 | defaults(Application) { 62 | jvmArgs = '-DmyKey=myVal' 63 | } 64 | } 65 | } 66 | } 67 | } 68 | 69 | 70 | task printSettings { 71 | doLast { 72 | println(projectDir) 73 | println(project.idea.project.settings) 74 | } 75 | } 76 | """ 77 | when: 78 | def result = GradleRunner.create() 79 | .withGradleVersion(gradleVersion) 80 | .withProjectDir(testProjectDir.root) 81 | .withArguments("printSettings", "-q") 82 | .withPluginClasspath() 83 | .build() 84 | then: 85 | 86 | def lines = result.output.readLines() 87 | def projectDir = lines[0] 88 | def prettyOutput = prettyPrintJSON(lines[1]) 89 | prettyOutput.contains( 90 | """"compiler": { 91 | "resourcePatterns": "!*.java;!*.class" 92 | }""") 93 | prettyOutput.contains( 94 | """"inspections": [ 95 | { 96 | "enabled": true, 97 | "name": "some" 98 | } 99 | ]""") 100 | prettyOutput.contains( 101 | """"runConfigurations": [ 102 | { 103 | "defaults": false, 104 | "type": "application", 105 | "name": "Run my app", 106 | "moduleName": "ProjectName", 107 | "workingDirectory": ${new Gson().toJson(projectDir)}, 108 | "beforeRun": [], 109 | "mainClass": "foo.App", 110 | "includeProvidedDependencies": true 111 | }, 112 | { 113 | "defaults": true, 114 | "type": "application", 115 | "name": "default_org.jetbrains.gradle.ext.Application", 116 | "beforeRun": [], 117 | "jvmArgs": "-DmyKey=myVal", 118 | "includeProvidedDependencies": false 119 | } 120 | ]""") 121 | prettyOutput.contains( 122 | """"copyright": { 123 | "useDefault": "myProfile", 124 | "profiles": { 125 | "myProfile": { 126 | "name": "myProfile", 127 | "notice": "My private license text placeholder" 128 | } 129 | } 130 | }""" 131 | ) 132 | prettyOutput.contains(""" 133 | "generateImlFiles": true 134 | """ 135 | ) 136 | 137 | result.task(":printSettings").outcome == TaskOutcome.SUCCESS 138 | where: 139 | gradleVersion << gradleVersionList 140 | } 141 | 142 | def "test groovy compiler settings"() { 143 | given: 144 | buildFile << """ 145 | plugins { 146 | id 'org.jetbrains.gradle.plugin.idea-ext' 147 | } 148 | 149 | idea { 150 | project { 151 | settings { 152 | groovyCompiler { 153 | excludes { 154 | file("/some/myFile") 155 | dir("/a/dir", true) 156 | } 157 | } 158 | } 159 | } 160 | } 161 | 162 | task printSettings { 163 | doLast { 164 | println(project.idea.project.settings) 165 | } 166 | } 167 | """ 168 | 169 | when: 170 | def result = GradleRunner.create() 171 | .withGradleVersion(gradleVersion) 172 | .withProjectDir(testProjectDir.root) 173 | .withArguments("printSettings", "-q") 174 | .withPluginClasspath() 175 | .build() 176 | then: 177 | 178 | def lines = result.output.readLines() 179 | lines[0] == '{"groovyCompiler":{"excludes":[{"url":"/some/myFile","includeSubdirectories":false,"isFile":true},' + 180 | '{"url":"/a/dir","includeSubdirectories":true,"isFile":false}]}}' 181 | where: 182 | gradleVersion << gradleVersionList 183 | } 184 | 185 | def "test artifacts settings"() { 186 | given: 187 | buildFile << """ 188 | plugins { 189 | id 'org.jetbrains.gradle.plugin.idea-ext' 190 | } 191 | 192 | idea { 193 | project { 194 | settings { 195 | ideArtifacts { 196 | myArt { 197 | directory("dir1") { 198 | archive("my.zip") { 199 | file("build.gradle") 200 | } 201 | } 202 | } 203 | } 204 | } 205 | } 206 | } 207 | 208 | task printSettings { 209 | doLast { 210 | println(project.idea.project.settings) 211 | } 212 | } 213 | """ 214 | 215 | when: 216 | def result = GradleRunner.create() 217 | .withGradleVersion(gradleVersion) 218 | .withProjectDir(testProjectDir.root) 219 | .withArguments("printSettings", "-q") 220 | .withPluginClasspath() 221 | .build() 222 | then: 223 | 224 | def lines = result.output.readLines() 225 | prettyPrintJSON(lines[0]) == """{ 226 | "ideArtifacts": [ 227 | { 228 | "type": "ARTIFACT", 229 | "name": "myArt", 230 | "children": [ 231 | { 232 | "type": "DIR", 233 | "name": "dir1", 234 | "children": [ 235 | { 236 | "type": "ARCHIVE", 237 | "name": "my.zip", 238 | "children": [ 239 | { 240 | "type": "FILE", 241 | "sourceFiles": [ 242 | "${buildFile.canonicalPath.replace('\\\\' as char, '/' as char)}" 243 | ] 244 | } 245 | ] 246 | } 247 | ] 248 | } 249 | ] 250 | } 251 | ] 252 | }""" 253 | 254 | where: 255 | gradleVersion << gradleVersionList 256 | } 257 | 258 | def "test build ide artifact reference"() { 259 | given: 260 | buildFile << """ 261 | plugins { 262 | id 'org.jetbrains.gradle.plugin.idea-ext' 263 | } 264 | 265 | idea { 266 | project { 267 | settings { 268 | ideArtifacts { 269 | ref { 270 | directory("dir1") { 271 | file("build.gradle") 272 | } 273 | } 274 | root { 275 | artifact("ref") 276 | } 277 | } 278 | } 279 | } 280 | } 281 | 282 | task buildIdeArtifact(type: org.jetbrains.gradle.ext.BuildIdeArtifact) { 283 | artifact = idea.project.settings.ideArtifacts["root"] 284 | } 285 | """ 286 | 287 | when: 288 | GradleRunner.create() 289 | .withGradleVersion(gradleVersion) 290 | .withProjectDir(testProjectDir.root) 291 | .withArguments("buildIdeArtifact") 292 | .withPluginClasspath() 293 | .build() 294 | then: 295 | new File(testProjectDir.root, "build/idea-artifacts/root/dir1/build.gradle").exists() 296 | 297 | where: 298 | gradleVersion << gradleVersionList 299 | } 300 | 301 | 302 | def "test code style settings"() { 303 | given: 304 | buildFile << """ 305 | plugins { 306 | id 'org.jetbrains.gradle.plugin.idea-ext' 307 | } 308 | 309 | idea { 310 | project { 311 | settings { 312 | codeStyle { 313 | hardWrapAt = 200 314 | java { 315 | alignParameterDescriptions = false 316 | } 317 | groovy { 318 | alignMultilineNamedArguments = false 319 | classCountToUseImportOnDemand = 999 320 | } 321 | } 322 | } 323 | } 324 | } 325 | 326 | task printSettings { 327 | doLast { 328 | println(project.idea.project.settings) 329 | } 330 | } 331 | """ 332 | 333 | when: 334 | def result = GradleRunner.create() 335 | .withGradleVersion(gradleVersion) 336 | .withProjectDir(testProjectDir.root) 337 | .withArguments("printSettings", "-q") 338 | .withPluginClasspath() 339 | .build() 340 | then: 341 | 342 | def lines = result.output.readLines() 343 | prettyPrintJSON(lines[0]) == 344 | """{ 345 | "codeStyle": { 346 | "RIGHT_MARGIN": 200, 347 | "languages": { 348 | "java": { 349 | "JD_ALIGN_PARAM_COMMENTS": false 350 | }, 351 | "groovy": { 352 | "CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND": 999, 353 | "ALIGN_NAMED_ARGS_IN_MAP": false 354 | } 355 | } 356 | } 357 | }""" 358 | 359 | where: 360 | gradleVersion << gradleVersionList 361 | } 362 | 363 | def "idea extension plugin can be applied in multiproject build"() { 364 | given: 365 | settingsFile << """ 366 | include 'p1', 'p2', 'p3' 367 | """ 368 | 369 | buildFile << """ 370 | plugins { 371 | id 'org.jetbrains.gradle.plugin.idea-ext' apply false 372 | } 373 | 374 | allprojects { 375 | apply plugin: 'org.jetbrains.gradle.plugin.idea-ext' 376 | } 377 | 378 | """ 379 | when: 380 | def result = GradleRunner.create() 381 | .withGradleVersion(gradleVersion) 382 | .withProjectDir(testProjectDir.root) 383 | .withArguments("projects", "--stacktrace") 384 | .withPluginClasspath() 385 | .build() 386 | then: 387 | // result.output.contains("p1") 388 | result.task(":projects").outcome == TaskOutcome.SUCCESS 389 | 390 | where: 391 | gradleVersion << gradleVersionList 392 | } 393 | 394 | def "test module settings"() { 395 | given: 396 | // language=groovy 397 | buildFile << """ 398 | import org.jetbrains.gradle.ext.* 399 | 400 | plugins { 401 | id 'org.jetbrains.gradle.plugin.idea-ext' 402 | id 'java' 403 | } 404 | 405 | idea { 406 | module { 407 | settings { 408 | facets { 409 | spring(SpringFacet) { 410 | contexts { 411 | p1 { 412 | file = 'spring_parent.xml' 413 | } 414 | 415 | p2 { 416 | file = 'spring_child.xml' 417 | parent = 'p1' 418 | } 419 | } 420 | } 421 | } 422 | 423 | rootModuleType = "SOME_TYPE" 424 | moduleType[sourceSets.main] = "JAVA_MODULE" 425 | moduleType[sourceSets.test] = "PYTHON_MODULE" 426 | } 427 | } 428 | } 429 | 430 | idea.module.settings.facets.spring.contexts.p2.file = 'spring_new_child.xml' 431 | 432 | task printSettings { 433 | doLast { 434 | println project.projectDir 435 | println project.idea.module.settings 436 | } 437 | } 438 | """ 439 | when: 440 | def result = GradleRunner.create() 441 | .withGradleVersion(gradleVersion) 442 | .withProjectDir(testProjectDir.root) 443 | .withArguments("printSettings", "-q") 444 | .withPluginClasspath() 445 | .build() 446 | then: 447 | def lines = result.output.readLines() 448 | lines[1] == '{"moduleType":{"":"SOME_TYPE","main":"JAVA_MODULE","test":"PYTHON_MODULE"},' + 449 | '"facets":[{"type":"spring","contexts":' + 450 | '[{"file":"spring_parent.xml","name":"p1","parent":null},' + 451 | '{"file":"spring_new_child.xml","name":"p2","parent":"p1"}],"name":"spring"}]}' 452 | 453 | result.task(":printSettings").outcome == TaskOutcome.SUCCESS 454 | 455 | where: 456 | gradleVersion << gradleVersionList 457 | } 458 | 459 | def "test module package prefix settings"() { 460 | given: 461 | buildFile << """ 462 | import org.jetbrains.gradle.ext.* 463 | 464 | plugins { 465 | id 'org.jetbrains.gradle.plugin.idea-ext' 466 | } 467 | 468 | idea { 469 | module { 470 | settings { 471 | packagePrefix["main/groovy"] = "com.example.main.groovy" 472 | packagePrefix["test/java"] = "com.example.test.java" 473 | } 474 | } 475 | } 476 | 477 | task printSettings { 478 | doLast { 479 | println project.projectDir 480 | println project.idea.module.settings 481 | } 482 | } 483 | """ 484 | when: 485 | def result = GradleRunner.create() 486 | .withGradleVersion(gradleVersion) 487 | .withProjectDir(testProjectDir.root) 488 | .withArguments("printSettings", "-q") 489 | .withPluginClasspath() 490 | .build() 491 | then: 492 | def lines = result.output.readLines() 493 | def moduleContentRoot = testProjectDir.root.canonicalPath.replace(File.separator, '/') 494 | lines[1] == '{"packagePrefix":{' + 495 | '"' + moduleContentRoot + '/main/groovy":"com.example.main.groovy",' + 496 | '"' + moduleContentRoot + '/test/java":"com.example.test.java"' + 497 | '}}' 498 | 499 | result.task(":printSettings").outcome == TaskOutcome.SUCCESS 500 | 501 | where: 502 | gradleVersion << gradleVersionList 503 | } 504 | 505 | def "test module settings with remote source root"() { 506 | given: 507 | buildFile << """ 508 | import org.jetbrains.gradle.ext.* 509 | 510 | plugins { 511 | id 'org.jetbrains.gradle.plugin.idea-ext' 512 | } 513 | 514 | apply plugin: "java" 515 | 516 | sourceSets { 517 | main.java.srcDirs += "src" 518 | main.java.srcDirs += "../subproject/src" 519 | } 520 | 521 | idea { 522 | module { 523 | settings { 524 | packagePrefix["src"] = "com.example.java" 525 | packagePrefix["../subproject/src"] = "com.example.java.sub" 526 | } 527 | } 528 | } 529 | 530 | task printSettings { 531 | doLast { 532 | println project.idea.module.settings 533 | } 534 | } 535 | """ 536 | when: 537 | def result = GradleRunner.create() 538 | .withGradleVersion(gradleVersion) 539 | .withProjectDir(testProjectDir.root) 540 | .withArguments("printSettings", "-q") 541 | .withPluginClasspath() 542 | .build() 543 | then: 544 | def lines = result.output.readLines() 545 | def moduleContentRoot = testProjectDir.root.canonicalPath.replace(File.separator, '/') 546 | lines[0] == '{"packagePrefix":{' + 547 | '"' + moduleContentRoot + '/src":"com.example.java",' + 548 | '"' + moduleContentRoot + '/../subproject/src":"com.example.java.sub"' + 549 | '}}' 550 | 551 | result.task(":printSettings").outcome == TaskOutcome.SUCCESS 552 | 553 | where: 554 | gradleVersion << gradleVersionList 555 | } 556 | 557 | def "test module settings with custom source root"() { 558 | given: 559 | buildFile << """ 560 | import org.jetbrains.gradle.ext.* 561 | 562 | plugins { 563 | id 'org.jetbrains.gradle.plugin.idea-ext' 564 | } 565 | 566 | apply plugin: "java" 567 | 568 | sourceSets { 569 | main.java.srcDirs += "src" 570 | } 571 | 572 | idea { 573 | module { 574 | settings { 575 | packagePrefix["src"] = "com.example.java" 576 | } 577 | } 578 | } 579 | 580 | task printSettings { 581 | doLast { 582 | println project.idea.module.settings 583 | } 584 | } 585 | """ 586 | when: 587 | def result = GradleRunner.create() 588 | .withGradleVersion(gradleVersion) 589 | .withProjectDir(testProjectDir.root) 590 | .withArguments("printSettings", "-q") 591 | .withPluginClasspath() 592 | .build() 593 | then: 594 | def lines = result.output.readLines() 595 | def moduleContentRoot = testProjectDir.root.canonicalPath.replace(File.separator, '/') 596 | lines[0] == '{"packagePrefix":{"' + moduleContentRoot + '/src":"com.example.java"}}' 597 | 598 | result.task(":printSettings").outcome == TaskOutcome.SUCCESS 599 | 600 | where: 601 | gradleVersion << gradleVersionList 602 | } 603 | 604 | def "test complex project encoding settings"() { 605 | given: 606 | buildFile << """ 607 | import org.jetbrains.gradle.ext.* 608 | import org.jetbrains.gradle.ext.EncodingConfiguration.BomPolicy 609 | 610 | plugins { 611 | id 'org.jetbrains.gradle.plugin.idea-ext' 612 | } 613 | 614 | idea { 615 | project { 616 | settings { 617 | encodings { 618 | encoding = 'windows-1251' 619 | bomPolicy = BomPolicy.WITH_NO_BOM 620 | properties { 621 | encoding = 'windows-1251' 622 | transparentNativeToAsciiConversion = false 623 | } 624 | mapping['path/to/dir1'] = 'UTF-8' 625 | mapping['path/to/dir2'] = 'windows-1251' 626 | mapping['path/to/dir3'] = 'ISO-8859-1' 627 | mapping['../path/to/dir4'] = 'US-ASCII' 628 | } 629 | } 630 | } 631 | } 632 | 633 | task printSettings { 634 | doLast { 635 | println project.idea.project.settings 636 | } 637 | } 638 | """ 639 | when: 640 | def result = GradleRunner.create() 641 | .withGradleVersion(gradleVersion) 642 | .withProjectDir(testProjectDir.root) 643 | .withArguments("printSettings", "-q") 644 | .withPluginClasspath() 645 | .build() 646 | then: 647 | def lines = result.output.readLines() 648 | def moduleContentRoot = testProjectDir.root.canonicalPath.replace(File.separator, '/') 649 | lines[0] == '{"encodings":{' + 650 | '"encoding":"windows-1251",' + 651 | '"bomPolicy":"WITH_NO_BOM",' + 652 | '"properties":{' + 653 | '"encoding":"windows-1251",' + 654 | '"transparentNativeToAsciiConversion":false' + 655 | '},' + 656 | '"mapping":{' + 657 | '"' + moduleContentRoot + '/path/to/dir1":"UTF-8",' + 658 | '"' + moduleContentRoot + '/path/to/dir2":"windows-1251",' + 659 | '"' + moduleContentRoot + '/path/to/dir3":"ISO-8859-1",' + 660 | '"' + moduleContentRoot + '/../path/to/dir4":"US-ASCII"' + 661 | '}' + 662 | '}' + 663 | '}' 664 | 665 | result.task(":printSettings").outcome == TaskOutcome.SUCCESS 666 | 667 | where: 668 | gradleVersion << gradleVersionList 669 | } 670 | 671 | def "test partial project encoding settings"() { 672 | given: 673 | buildFile << """ 674 | import org.jetbrains.gradle.ext.* 675 | 676 | plugins { 677 | id 'org.jetbrains.gradle.plugin.idea-ext' 678 | } 679 | 680 | idea { 681 | project { 682 | settings { 683 | encodings { 684 | encoding = 'windows-1251' 685 | properties.encoding = 'windows-1251' 686 | } 687 | } 688 | } 689 | } 690 | 691 | task printSettings { 692 | doLast { 693 | println project.idea.project.settings 694 | } 695 | } 696 | """ 697 | when: 698 | def result = GradleRunner.create() 699 | .withGradleVersion(gradleVersion) 700 | .withProjectDir(testProjectDir.root) 701 | .withArguments("printSettings", "-q") 702 | .withPluginClasspath() 703 | .build() 704 | then: 705 | def lines = result.output.readLines() 706 | lines[0] == '{"encodings":{' + 707 | '"encoding":"windows-1251",' + 708 | '"properties":{' + 709 | '"encoding":"windows-1251"' + 710 | '}' + 711 | '}' + 712 | '}' 713 | 714 | result.task(":printSettings").outcome == TaskOutcome.SUCCESS 715 | 716 | where: 717 | gradleVersion << gradleVersionList 718 | } 719 | 720 | def "test extending the DSL with custom run configurations and facets"() { 721 | given: 722 | buildFile << """ 723 | import org.jetbrains.gradle.ext.* 724 | import org.gradle.plugins.ide.idea.model.IdeaModel 725 | import org.gradle.api.plugins.ExtensionAware 726 | import org.gradle.api.* 727 | import javax.inject.Inject 728 | 729 | class MyRC extends BaseRunConfiguration { 730 | def name = "value" 731 | def aKey = "a value" 732 | def type = "myRunConfiguration" 733 | @Inject 734 | MyRC(String param) { name = param } 735 | String getName() { return name } 736 | String getType() { return type } 737 | Map toMap() { return [ "name": name, "type":type, "aKey": aKey ] } 738 | } 739 | 740 | class MyFacet implements Facet { 741 | def type = "MyFacetType" 742 | def name = "facet_name" 743 | def facetKey = "facet_value" 744 | @Inject 745 | MyFacet(param) { name = param } 746 | String getName() { return name } 747 | String getType() { return type } 748 | Map toMap() { return [ "type": type, "facetKey": facetKey ] } 749 | } 750 | 751 | class TestPlugin implements Plugin { 752 | void apply(Project project) { 753 | project.apply plugin: 'org.jetbrains.gradle.plugin.idea-ext' 754 | def ideaModel = project.extensions.findByName('idea') as IdeaModel 755 | 756 | def projectSettings = (ideaModel.project as ExtensionAware).extensions.findByName("settings") as ProjectSettings 757 | projectSettings.runConfigurations.registerFactory(MyRC) { String name -> project.objects.newInstance(MyRC, name) } 758 | 759 | def moduleSettings = (ideaModel.module as ExtensionAware).extensions.findByName("settings") as ModuleSettings 760 | moduleSettings.facets.registerFactory(MyFacet) { String name -> project.objects.newInstance(MyFacet, name) } 761 | } 762 | } 763 | 764 | plugins { 765 | id 'org.jetbrains.gradle.plugin.idea-ext' 766 | } 767 | 768 | // Apply the plugin 769 | apply plugin: TestPlugin 770 | 771 | idea { 772 | project { 773 | settings { 774 | runConfigurations { 775 | "testRunConfig"(MyRC) { 776 | aKey = "project_test_value" 777 | } 778 | } 779 | } 780 | } 781 | module.settings.facets { 782 | testFacet(MyFacet) { 783 | facetKey = "module_facet_value" 784 | } 785 | } 786 | } 787 | 788 | task printSettings { 789 | doLast { 790 | println(project.idea.project.settings) 791 | println(project.idea.module.settings) 792 | } 793 | } 794 | """ 795 | 796 | when: 797 | def result = GradleRunner.create() 798 | .withGradleVersion(gradleVersion) 799 | .withProjectDir(testProjectDir.root) 800 | .withArguments("printSettings", "-q") 801 | .withPluginClasspath() 802 | .build() 803 | then: 804 | 805 | def lines = result.output.readLines() 806 | lines[0] == '{"runConfigurations":[{"name":"testRunConfig","type":"myRunConfiguration","aKey":"project_test_value"}]}' 807 | lines[1] == '{"facets":[{"type":"MyFacetType","facetKey":"module_facet_value"}]}' 808 | 809 | where: 810 | gradleVersion << gradleVersionList 811 | 812 | } 813 | 814 | 815 | def "test extending the DSL with simple class"() { 816 | given: 817 | buildFile << """ 818 | import org.jetbrains.gradle.ext.* 819 | import org.gradle.plugins.ide.idea.model.IdeaModel 820 | import org.gradle.api.plugins.ExtensionAware 821 | import org.gradle.api.* 822 | 823 | class TestExtSettings implements MapConvertible { 824 | def name = "value" 825 | Map toMap() { return [ "name": name ] } 826 | } 827 | 828 | class TestPlugin implements Plugin { 829 | void apply(Project project) { 830 | project.apply plugin: 'org.jetbrains.gradle.plugin.idea-ext' 831 | def ideaModel = project.extensions.findByName('idea') as IdeaModel 832 | 833 | def projectSettings = (ideaModel.project as ExtensionAware).extensions.findByName("settings") as ExtensionAware 834 | projectSettings.extensions.create("myTest", TestExtSettings) 835 | 836 | def moduleSettings = (ideaModel.module as ExtensionAware).extensions.findByName("settings") as ExtensionAware 837 | moduleSettings.extensions.create("myTest", TestExtSettings) 838 | } 839 | } 840 | 841 | plugins { 842 | id 'org.jetbrains.gradle.plugin.idea-ext' 843 | } 844 | 845 | // Apply the plugin 846 | apply plugin: TestPlugin 847 | 848 | idea { 849 | project { 850 | settings { 851 | myTest { 852 | name = "test_project_value" 853 | } 854 | } 855 | } 856 | module.settings.myTest.name = "test_module_value" 857 | } 858 | 859 | task printSettings { 860 | doLast { 861 | println(project.idea.project.settings) 862 | println(project.idea.module.settings) 863 | } 864 | } 865 | """ 866 | 867 | when: 868 | def result = GradleRunner.create() 869 | .withGradleVersion(gradleVersion) 870 | .withProjectDir(testProjectDir.root) 871 | .withArguments("printSettings", "-q") 872 | .withPluginClasspath() 873 | .build() 874 | then: 875 | 876 | def lines = result.output.readLines() 877 | lines[0] == '{"myTest":{"name":"test_project_value"}}' 878 | lines[1] == '{"myTest":{"name":"test_module_value"}}' 879 | 880 | where: 881 | gradleVersion << gradleVersionList 882 | 883 | } 884 | 885 | 886 | def "test extending the DSL with NameObjectContainer"() { 887 | given: 888 | buildFile << """ 889 | import org.jetbrains.gradle.ext.* 890 | import org.gradle.plugins.ide.idea.model.IdeaModel 891 | import org.gradle.api.plugins.ExtensionAware 892 | import org.gradle.api.* 893 | 894 | class TestExtSettings implements MapConvertible, Named { 895 | private String name = "value" 896 | def param = 0 897 | 898 | def TestExtSettings(String constructedName) { 899 | name = constructedName 900 | } 901 | 902 | String getName() { return name } 903 | 904 | Map toMap() { return [ "name": name, "param": param ] } 905 | } 906 | 907 | class TestPlugin implements Plugin { 908 | void apply(Project project) { 909 | project.apply plugin: 'org.jetbrains.gradle.plugin.idea-ext' 910 | def ideaModel = project.extensions.findByName('idea') as IdeaModel 911 | 912 | def projectSettings = (ideaModel.project as ExtensionAware).extensions.findByName("settings") as ExtensionAware 913 | projectSettings.extensions.add("myTest", project.container(TestExtSettings)) 914 | 915 | def moduleSettings = (ideaModel.module as ExtensionAware).extensions.findByName("settings") as ExtensionAware 916 | moduleSettings.extensions.add("myTest", project.container(TestExtSettings)) 917 | } 918 | } 919 | 920 | plugins { 921 | id 'org.jetbrains.gradle.plugin.idea-ext' 922 | } 923 | 924 | // Apply the plugin 925 | apply plugin: TestPlugin 926 | 927 | idea { 928 | project { 929 | settings { 930 | myTest { 931 | "projectName1" { 932 | param = 1 933 | } 934 | "projectName2" { 935 | param = 2 936 | } 937 | } 938 | } 939 | } 940 | module.settings.myTest { 941 | "moduleName1" { 942 | param = 1 943 | } 944 | } 945 | } 946 | 947 | task printSettings { 948 | doLast { 949 | println(project.idea.project.settings) 950 | println(project.idea.module.settings) 951 | } 952 | } 953 | """ 954 | 955 | when: 956 | def result = GradleRunner.create() 957 | .withGradleVersion(gradleVersion) 958 | .withProjectDir(testProjectDir.root) 959 | .withArguments("printSettings", "-q") 960 | .withPluginClasspath() 961 | .build() 962 | then: 963 | 964 | def lines = result.output.readLines() 965 | lines[0] == '{"myTest":[{"name":"projectName1","param":1},{"name":"projectName2","param":2}]}' 966 | lines[1] == '{"myTest":[{"name":"moduleName1","param":1}]}' 967 | 968 | where: 969 | gradleVersion << gradleVersionList 970 | } 971 | 972 | 973 | def "test task triggers settings"() { 974 | given: 975 | settingsFile << """ 976 | rootProject.name = "ProjectName" 977 | """ 978 | // language=groovy 979 | buildFile << """ 980 | 981 | import org.gradle.api.DefaultTask 982 | import org.jetbrains.gradle.ext.* 983 | 984 | plugins { 985 | id 'org.jetbrains.gradle.plugin.idea-ext' 986 | } 987 | 988 | 989 | ext.lazyFlagForTaskTrigger = false 990 | def provider = tasks.register("LazyTask", DefaultTask) { 991 | lazyFlagForTaskTrigger = true 992 | } 993 | 994 | idea.project.settings { 995 | taskTriggers { 996 | beforeSync(":help", provider) 997 | } 998 | } 999 | 1000 | 1001 | task printSettings { 1002 | doLast { 1003 | println("LazyFlag before=[\${project.lazyFlagForTaskTrigger}]") 1004 | println(project.idea.project.settings) 1005 | println("LazyFlag after=[\${project.lazyFlagForTaskTrigger}]") 1006 | println(projectDir.absolutePath.replace('\\\\', '/')) 1007 | } 1008 | } 1009 | """ 1010 | when: 1011 | def result = GradleRunner.create() 1012 | .withGradleVersion(gradleVersion) 1013 | .withProjectDir(testProjectDir.root) 1014 | .withArguments("printSettings", "-q") 1015 | .withPluginClasspath() 1016 | .build() 1017 | then: 1018 | 1019 | def lines = result.output.readLines() 1020 | def projectDir = lines[3] 1021 | "LazyFlag before=[false]" == lines[0] 1022 | "LazyFlag after=[true]" == lines[2] 1023 | def prettyOutput = prettyPrintJSON(lines[1]) 1024 | prettyOutput == 1025 | """{ 1026 | "taskTriggers": { 1027 | "beforeSync": [ 1028 | { 1029 | "taskPath": "help", 1030 | "projectPath": "$projectDir" 1031 | }, 1032 | { 1033 | "taskPath": "LazyTask", 1034 | "projectPath": "$projectDir" 1035 | } 1036 | ] 1037 | } 1038 | }""" 1039 | 1040 | result.task(":printSettings").outcome == TaskOutcome.SUCCESS 1041 | 1042 | where: 1043 | gradleVersion << gradleVersionList.findAll { (GradleVersion.version(it) >= GradleVersion.version("5.0")) } 1044 | } 1045 | 1046 | def "test process IDEA project files task"() { 1047 | given: 1048 | def layoutFile = testProjectDir.newFile('layout.json') 1049 | def rootPath = testProjectDir.root.absolutePath.replace('\\', '/') 1050 | layoutFile << """ 1051 | { 1052 | "ideaDirPath": "${rootPath}/.idea", 1053 | "modulesMap": { 1054 | "ProjectName": "${rootPath}/.idea/modules/ProjectName.iml", 1055 | "ProjectName:test": "${rootPath}/.idea/modules/ProjectName.test.iml", 1056 | "ProjectName:main": "${rootPath}/.idea/modules/ProjectName.main.iml" 1057 | } 1058 | } 1059 | """ 1060 | def modulesFolder = testProjectDir.newFolder(".idea", "modules") 1061 | def ideaDir = modulesFolder.parentFile 1062 | def vcsFile = new File(ideaDir, "vcs.xml") 1063 | // language=xml 1064 | vcsFile << """ 1065 | 1066 | 1067 | 1068 | 1069 | """ 1070 | 1071 | def parentModuleFile = new File(modulesFolder, 'ProjectName.iml') 1072 | // language=xml 1073 | parentModuleFile << """ 1074 | 1075 | 1076 | 1077 | 1078 | 1079 | 1080 | 1081 | 1082 | 1083 | 1084 | 1085 | """ 1086 | def mainModuleFile = new File(modulesFolder, 'ProjectName.main.iml') 1087 | // language=xml 1088 | mainModuleFile << """ 1089 | 1090 | 1091 | 1092 | 1093 | 1094 | 1095 | 1096 | 1097 | 1098 | 1099 | 1100 | 1101 | 1102 | """ 1103 | settingsFile << """ 1104 | rootProject.name = "ProjectName" 1105 | """ 1106 | // language=groovy 1107 | buildFile << """ 1108 | 1109 | import org.gradle.api.DefaultTask 1110 | import org.gradle.api.XmlProvider 1111 | import org.jetbrains.gradle.ext.* 1112 | import org.w3c.dom.Element 1113 | import org.w3c.dom.Node 1114 | 1115 | plugins { 1116 | id 'org.jetbrains.gradle.plugin.idea-ext' 1117 | id 'java' 1118 | } 1119 | 1120 | idea.project.settings { 1121 | withIDEADir { File dir -> 1122 | println("Callback 1 executed with: " + dir.absolutePath) 1123 | } 1124 | 1125 | withIDEADir { File dir -> 1126 | println("Callback 2 executed with: " + dir.absolutePath) 1127 | } 1128 | 1129 | withIDEAFileXml("vcs.xml") { XmlProvider p -> 1130 | p.asNode().component 1131 | .find { it.@name == 'VcsDirectoryMappings' } 1132 | .mapping.@vcs = 'SVN' 1133 | } 1134 | } 1135 | 1136 | idea.module.settings { 1137 | withModuleFile { File file -> 1138 | println("Callback for parent module executed with " + file.absolutePath) 1139 | } 1140 | withModuleXml { XmlProvider p -> 1141 | p.asNode().appendNode("test", ["key":"value"]) 1142 | } 1143 | withModuleXml { XmlProvider p -> 1144 | p.asNode().appendNode("test2", ["key":"value"]) 1145 | } 1146 | withModuleFile(sourceSets.main) { File file -> 1147 | println("Callback for main module executed with " + file.absolutePath) 1148 | } 1149 | withModuleXml(sourceSets.main) { XmlProvider p -> 1150 | p.asNode().appendNode("testMain", ["key":"valueMain"]) 1151 | } 1152 | } 1153 | """ 1154 | when: 1155 | def result = GradleRunner.create() 1156 | .withGradleVersion(gradleVersion) 1157 | .withProjectDir(testProjectDir.root) 1158 | .withArguments("processIdeaSettings") 1159 | .withPluginClasspath() 1160 | .withDebug(true) 1161 | .build() 1162 | then: 1163 | 1164 | result.task(":processIdeaSettings").outcome == TaskOutcome.SUCCESS 1165 | 1166 | String ideaDirPath = ideaDir.absolutePath 1167 | def lines = result.output.readLines() 1168 | assertThat(lines).contains("Callback 1 executed with: " + ideaDirPath, 1169 | "Callback 2 executed with: " + ideaDirPath, 1170 | "Callback for parent module executed with " + parentModuleFile.absolutePath, 1171 | "Callback for main module executed with " + mainModuleFile.absolutePath) 1172 | 1173 | assertThat(vcsFile).hasContent(""" 1174 | 1175 | 1176 | 1177 | 1178 | """) 1179 | 1180 | assertThat(parentModuleFile.text) 1181 | .contains("""""") 1182 | .contains("""""") 1183 | assertThat(mainModuleFile.text).contains("""""") 1184 | 1185 | assertThat(layoutFile).doesNotExist() 1186 | 1187 | where: 1188 | gradleVersion << gradleVersionList 1189 | } 1190 | 1191 | def "test process IDEA project files in multi-module project"() { 1192 | given: 1193 | File layoutFile = testProjectDir.newFile('layout.json') 1194 | def rootPath = testProjectDir.root.absolutePath.replace('\\', '/') 1195 | layoutFile << """ 1196 | { 1197 | "ideaDirPath": "${rootPath}/.idea", 1198 | "modulesMap": { 1199 | "ProjectName": "${rootPath}/.idea/modules/ProjectName.iml", 1200 | "ProjectName:test": "${rootPath}/.idea/modules/ProjectName.test.iml", 1201 | "ProjectName:main": "${rootPath}/.idea/modules/ProjectName.main.iml", 1202 | ":Sub": "${rootPath}/.idea/modules/ProjectName.Sub.iml", 1203 | ":ProjectName" : "${rootPath}/.idea/modules/ProjectName/ProjectName.ProjectName.iml", 1204 | ":ProjectName:main" : "${rootPath}/.idea/modules/ProjectName/ProjectName.ProjectName.main.iml" 1205 | } 1206 | } 1207 | """ 1208 | def modulesFolder = testProjectDir.newFolder(".idea", "modules") 1209 | def ideaDir = modulesFolder.parentFile 1210 | def vcsFile = new File(ideaDir, "vcs.xml") 1211 | // language=xml 1212 | vcsFile << """ 1213 | 1214 | 1215 | 1216 | 1217 | """ 1218 | 1219 | def parentModuleFile = new File(modulesFolder, 'ProjectName.iml') 1220 | // language=xml 1221 | parentModuleFile << """ 1222 | 1223 | 1224 | 1225 | 1226 | 1227 | 1228 | 1229 | 1230 | 1231 | 1232 | 1233 | """ 1234 | def mainModuleFile = new File(modulesFolder, 'ProjectName.main.iml') 1235 | // language=xml 1236 | mainModuleFile << """ 1237 | 1238 | 1239 | 1240 | 1241 | 1242 | 1243 | 1244 | 1245 | 1246 | 1247 | 1248 | 1249 | 1250 | """ 1251 | def subModuleFile = new File(modulesFolder, 'ProjectName.Sub.iml') 1252 | // language=xml 1253 | subModuleFile << """ 1254 | 1255 | 1256 | 1257 | 1258 | 1259 | 1260 | 1261 | 1262 | 1263 | 1264 | 1265 | 1266 | """ 1267 | 1268 | def projectNameDir = testProjectDir.newFolder(".idea", "modules", "ProjectName") 1269 | def projectNameDuplicate = new File(projectNameDir, "ProjectName.ProjectName.iml") 1270 | // language=xml 1271 | projectNameDuplicate << """ 1272 | 1273 | """ 1274 | def projectNameDuplicateMain = new File(projectNameDir, "ProjectName.ProjectName.main.iml") 1275 | // language=xml 1276 | projectNameDuplicateMain << """ 1277 | 1278 | """ 1279 | 1280 | settingsFile << """ 1281 | rootProject.name = "ProjectName" 1282 | include 'Sub' 1283 | include 'ProjectName' 1284 | """ 1285 | // language=groovy 1286 | buildFile << """ 1287 | plugins { 1288 | id 'org.jetbrains.gradle.plugin.idea-ext' 1289 | id 'java' 1290 | } 1291 | """ 1292 | File subBuildFile = new File(testProjectDir.newFolder("Sub"), "build.gradle") 1293 | 1294 | // language=groovy 1295 | subBuildFile << """import org.gradle.api.DefaultTask 1296 | import org.gradle.api.XmlProvider 1297 | import org.jetbrains.gradle.ext.* 1298 | import org.w3c.dom.Element 1299 | import org.w3c.dom.Node 1300 | 1301 | plugins { 1302 | id 'org.jetbrains.gradle.plugin.idea-ext' 1303 | id 'java' 1304 | } 1305 | 1306 | rootProject.idea.project.settings { 1307 | withIDEAFileXml("vcs.xml") { XmlProvider p -> 1308 | p.asNode().appendNode("test1", ["key1":"value1"]) 1309 | } 1310 | } 1311 | 1312 | idea.module.settings { 1313 | withModuleXml { XmlProvider p -> 1314 | p.asNode().appendNode("test2", ["key2":"value2"]) 1315 | } 1316 | } 1317 | """ 1318 | 1319 | File projectNameDuplicateBuild = new File(testProjectDir.newFolder("ProjectName"), "build.gradle") 1320 | projectNameDuplicateBuild << """import org.gradle.api.DefaultTask 1321 | import org.gradle.api.XmlProvider 1322 | import org.jetbrains.gradle.ext.* 1323 | import org.w3c.dom.Element 1324 | import org.w3c.dom.Node 1325 | 1326 | plugins { 1327 | id 'org.jetbrains.gradle.plugin.idea-ext' 1328 | id 'java' 1329 | } 1330 | 1331 | idea.module.settings { 1332 | withModuleXml { XmlProvider p -> 1333 | p.asNode().appendNode("test", ["k":"v"]) 1334 | } 1335 | 1336 | withModuleXml(sourceSets.main) { XmlProvider p -> 1337 | p.asNode().appendNode("test.main", ["k":"v"]) 1338 | } 1339 | } 1340 | """ 1341 | 1342 | when: 1343 | def result = GradleRunner.create() 1344 | .withGradleVersion(gradleVersion) 1345 | .withProjectDir(testProjectDir.root) 1346 | .withArguments("processIdeaSettings", "-s") 1347 | .withPluginClasspath() 1348 | .withDebug(true) 1349 | .build() 1350 | then: 1351 | 1352 | result.task(":processIdeaSettings").outcome == TaskOutcome.SUCCESS 1353 | 1354 | assertThat(vcsFile.text) 1355 | .contains("""""") 1356 | 1357 | assertThat(subModuleFile.text) 1358 | .contains("""""") 1359 | 1360 | assertThat(projectNameDuplicate.text) 1361 | .contains("""""") 1362 | 1363 | assertThat(projectNameDuplicateMain.text) 1364 | .contains("""""") 1365 | 1366 | assertThat(layoutFile).doesNotExist() 1367 | 1368 | where: 1369 | gradleVersion << gradleVersionList 1370 | } 1371 | 1372 | private static String prettyPrintJSON(String line) { 1373 | return SerializationUtil.prettyPrintJsonStr(line) 1374 | } 1375 | } -------------------------------------------------------------------------------- /src/test/groovy/TestIdeaExtPluginOnKotlinBuildFile.groovy: -------------------------------------------------------------------------------- 1 | import com.google.gson.Gson 2 | import org.gradle.testkit.runner.GradleRunner 3 | import org.gradle.testkit.runner.TaskOutcome 4 | import org.gradle.util.GradleVersion 5 | import org.jetbrains.gradle.ext.SerializationUtil 6 | import org.junit.Rule 7 | import org.junit.rules.TemporaryFolder 8 | import spock.lang.Ignore 9 | import spock.lang.Specification 10 | 11 | import static org.assertj.core.api.Assertions.assertThat 12 | 13 | class IdeaModelExtensionOnKotlinBuildFileFunctionalTest extends Specification { 14 | @Rule 15 | TemporaryFolder testProjectDir = new TemporaryFolder() 16 | File buildFile 17 | File settingsFile 18 | 19 | static List gradleVersionList = ["5.0", "5.6.4", "6.0", "6.8.3", "7.0", "7.2", "7.3"] 20 | 21 | def setup() { 22 | buildFile = testProjectDir.newFile('build.gradle.kts') 23 | settingsFile = testProjectDir.newFile('settings.gradle.kts') 24 | } 25 | 26 | def "test compiler settings"() { 27 | given: 28 | settingsFile << """ 29 | rootProject.name = "ProjectName" 30 | """ 31 | // language=kotlin 32 | buildFile << """ 33 | import org.jetbrains.gradle.ext.* 34 | 35 | plugins { 36 | id ("org.jetbrains.gradle.plugin.idea-ext") 37 | } 38 | idea.project { 39 | settings { 40 | compiler { 41 | processHeapSize = 8192 42 | parallelCompilation = false 43 | javac { 44 | javacAdditionalOptions = listOf( 45 | "-Werror", 46 | "-Xlint:all", 47 | "-Xlint:-options", 48 | "-Xlint:-serial", 49 | "-proc:none", 50 | "-Xdiags:verbose", 51 | "-parameters" 52 | ).joinToString(separator = " ") 53 | } 54 | } 55 | } 56 | } 57 | 58 | tasks.register("printSettings") { 59 | doLast { 60 | println(idea.project.settings.toString()) 61 | } 62 | } 63 | """ 64 | when: 65 | def result = GradleRunner.create() 66 | .withGradleVersion(gradleVersion) 67 | .withProjectDir(testProjectDir.root) 68 | .withArguments("printSettings", "-q") 69 | .withPluginClasspath() 70 | .build() 71 | then: 72 | 73 | def lines = result.output.readLines() 74 | def prettyOutput = SerializationUtil.prettyPrintJsonStr(lines[0]) 75 | prettyOutput.contains( 76 | """"compiler": { 77 | "processHeapSize": 8192, 78 | "parallelCompilation": false, 79 | "javacOptions": { 80 | "javacAdditionalOptions": "-Werror -Xlint:all -Xlint:-options -Xlint:-serial -proc:none -Xdiags:verbose -parameters" 81 | } 82 | }""") 83 | 84 | result.task(":printSettings").outcome == TaskOutcome.SUCCESS 85 | where: 86 | gradleVersion << gradleVersionList 87 | } 88 | 89 | def "test module package prefix settings"() { 90 | given: 91 | // language=kotlin 92 | buildFile << """ 93 | import org.jetbrains.gradle.ext.* 94 | 95 | plugins { 96 | id ("org.jetbrains.gradle.plugin.idea-ext") 97 | } 98 | 99 | idea { 100 | module { 101 | settings { 102 | packagePrefix["main/groovy"] = "com.example.main.groovy" 103 | packagePrefix["test/java"] = "com.example.test.java" 104 | } 105 | } 106 | } 107 | tasks.register("printSettings") { 108 | doLast { 109 | println(project.projectDir) 110 | println(project.idea.module.settings.toString()) 111 | } 112 | } 113 | """ 114 | when: 115 | def result = GradleRunner.create() 116 | .withGradleVersion(gradleVersion) 117 | .withProjectDir(testProjectDir.root) 118 | .withArguments("printSettings", "-q") 119 | .withPluginClasspath() 120 | .build() 121 | then: 122 | def lines = result.output.readLines() 123 | def moduleContentRoot = testProjectDir.root.canonicalPath.replace(File.separator, '/') 124 | lines[1] == '{"packagePrefix":{' + 125 | '"' + moduleContentRoot + '/main/groovy":"com.example.main.groovy",' + 126 | '"' + moduleContentRoot + '/test/java":"com.example.test.java"' + 127 | '}}' 128 | 129 | result.task(":printSettings").outcome == TaskOutcome.SUCCESS 130 | 131 | where: 132 | gradleVersion << gradleVersionList 133 | } 134 | 135 | 136 | def "test project settings"() { 137 | given: 138 | settingsFile << """ 139 | rootProject.name = "ProjectName" 140 | """ 141 | // language=kotlin 142 | buildFile << """ 143 | import org.gradle.plugins.ide.idea.model.IdeaModel 144 | import org.jetbrains.gradle.ext.* 145 | import org.jetbrains.gradle.ext.Application 146 | 147 | plugins { 148 | id("org.jetbrains.gradle.plugin.idea-ext") 149 | } 150 | 151 | idea { 152 | project { 153 | settings { 154 | copyright { 155 | profiles { 156 | create("myProfile") { 157 | notice = "My private license text placeholder" 158 | } 159 | } 160 | useDefault = "myProfile" 161 | } 162 | compiler.resourcePatterns = "!*.java;!*.class" 163 | inspections { 164 | register("some") { enabled = true } 165 | } 166 | runConfigurations { 167 | create("Run my app", Application::class.java) { 168 | mainClass = "foo.App" 169 | workingDirectory = "\$projectDir" 170 | moduleRef(project.getProject()) 171 | includeProvidedDependencies = true 172 | } 173 | defaults(Application::class.java) { 174 | jvmArgs = "-DmyKey=myVal" 175 | } 176 | } 177 | } 178 | } 179 | } 180 | 181 | 182 | tasks.register("printSettings") { 183 | doLast { 184 | println(projectDir) 185 | println(idea.project.settings.toString()) 186 | } 187 | } 188 | """ 189 | when: 190 | def result = GradleRunner.create() 191 | .withGradleVersion(gradleVersion) 192 | .withProjectDir(testProjectDir.root) 193 | .withArguments("printSettings", "-q") 194 | .withPluginClasspath() 195 | .build() 196 | then: 197 | 198 | def lines = result.output.readLines() 199 | def projectDir = lines[0] 200 | def prettyOutput = prettyPrintJSON(lines[1]) 201 | prettyOutput.contains( 202 | """"compiler": { 203 | "resourcePatterns": "!*.java;!*.class" 204 | }""") 205 | prettyOutput.contains( 206 | """"inspections": [ 207 | { 208 | "enabled": true, 209 | "name": "some" 210 | } 211 | ]""") 212 | prettyOutput.contains( 213 | """"runConfigurations": [ 214 | { 215 | "defaults": false, 216 | "type": "application", 217 | "name": "Run my app", 218 | "moduleName": "ProjectName", 219 | "workingDirectory": ${new Gson().toJson(projectDir)}, 220 | "beforeRun": [], 221 | "mainClass": "foo.App", 222 | "includeProvidedDependencies": true 223 | }, 224 | { 225 | "defaults": true, 226 | "type": "application", 227 | "name": "default_org.jetbrains.gradle.ext.Application", 228 | "beforeRun": [], 229 | "jvmArgs": "-DmyKey=myVal", 230 | "includeProvidedDependencies": false 231 | } 232 | ]""") 233 | prettyOutput.contains( 234 | """"copyright": { 235 | "useDefault": "myProfile", 236 | "profiles": { 237 | "myProfile": { 238 | "name": "myProfile", 239 | "notice": "My private license text placeholder" 240 | } 241 | } 242 | }""" 243 | ) 244 | 245 | result.task(":printSettings").outcome == TaskOutcome.SUCCESS 246 | where: 247 | gradleVersion << gradleVersionList 248 | } 249 | 250 | def "test groovy compiler settings"() { 251 | given: 252 | // language=kotlin 253 | buildFile << """ 254 | import org.jetbrains.gradle.ext.* 255 | plugins { 256 | id("org.jetbrains.gradle.plugin.idea-ext") 257 | } 258 | 259 | idea { 260 | project { 261 | settings { 262 | groovyCompiler { 263 | excludes { 264 | file("/some/myFile") 265 | dir("/a/dir", true) 266 | } 267 | } 268 | } 269 | } 270 | } 271 | 272 | tasks.register("printSettings") { 273 | doLast { 274 | println(idea.project.settings) 275 | } 276 | } 277 | """ 278 | 279 | when: 280 | def result = GradleRunner.create() 281 | .withGradleVersion(gradleVersion) 282 | .withProjectDir(testProjectDir.root) 283 | .withArguments("printSettings", "-q") 284 | .withPluginClasspath() 285 | .build() 286 | then: 287 | 288 | def lines = result.output.readLines() 289 | lines[0] == '{"groovyCompiler":{"excludes":[{"url":"/some/myFile","includeSubdirectories":false,"isFile":true},' + 290 | '{"url":"/a/dir","includeSubdirectories":true,"isFile":false}]}}' 291 | where: 292 | gradleVersion << gradleVersionList 293 | } 294 | 295 | @Ignore(value = "ide artifacts DSL in kotlin is not yet supported") 296 | def "test artifacts settings"() { 297 | given: 298 | // language=kotlin 299 | buildFile << """ 300 | import org.jetbrains.gradle.ext.* 301 | plugins { 302 | id("org.jetbrains.gradle.plugin.idea-ext") 303 | } 304 | 305 | idea { 306 | project { 307 | settings { 308 | ideArtifacts { 309 | create("myArt") { 310 | directory("dir1") { 311 | archive("my.zip") { 312 | file("build.gradle") 313 | } 314 | } 315 | } 316 | } 317 | } 318 | } 319 | } 320 | 321 | tasks.register("printSettings") { 322 | doLast { 323 | println(idea.project.settings) 324 | } 325 | } 326 | """ 327 | 328 | when: 329 | def result = GradleRunner.create() 330 | .withGradleVersion(gradleVersion) 331 | .withProjectDir(testProjectDir.root) 332 | .withArguments("printSettings", "-q") 333 | .withPluginClasspath() 334 | .build() 335 | then: 336 | 337 | def lines = result.output.readLines() 338 | prettyPrintJSON(lines[0]) == """{ 339 | "ideArtifacts": [ 340 | { 341 | "type": "ARTIFACT", 342 | "name": "myArt", 343 | "children": [ 344 | { 345 | "type": "DIR", 346 | "name": "dir1", 347 | "children": [ 348 | { 349 | "type": "ARCHIVE", 350 | "name": "my.zip", 351 | "children": [ 352 | { 353 | "type": "FILE", 354 | "sourceFiles": [ 355 | "${buildFile.canonicalPath.replace('\\\\' as char, '/' as char)}" 356 | ] 357 | } 358 | ] 359 | } 360 | ] 361 | } 362 | ] 363 | } 364 | ] 365 | }""" 366 | 367 | where: 368 | gradleVersion << gradleVersionList 369 | } 370 | 371 | @Ignore(value = "ide artifacts DSL in kotlin is not yet supported") 372 | def "test build ide artifact reference"() { 373 | given: 374 | // language=kotlin 375 | buildFile << """ 376 | import org.jetbrains.gradle.ext.* 377 | plugins { 378 | id("org.jetbrains.gradle.plugin.idea-ext") 379 | } 380 | 381 | idea { 382 | project { 383 | settings { 384 | ideArtifacts { 385 | create("ref"){ 386 | directory("dir1") { 387 | file("build.gradle") 388 | } 389 | } 390 | create("root"){ 391 | artifact("ref") 392 | } 393 | } 394 | } 395 | } 396 | } 397 | 398 | tasks.register("buildIdeArtifact") { 399 | artifact = idea.project.settings.ideArtifacts["root"] 400 | } 401 | """ 402 | 403 | when: 404 | GradleRunner.create() 405 | .withGradleVersion(gradleVersion) 406 | .withProjectDir(testProjectDir.root) 407 | .withArguments("buildIdeArtifact") 408 | .withPluginClasspath() 409 | .build() 410 | then: 411 | new File(testProjectDir.root, "build/idea-artifacts/root/dir1/build.gradle").exists() 412 | 413 | where: 414 | gradleVersion << gradleVersionList 415 | } 416 | 417 | 418 | def "test module settings"() { 419 | given: 420 | // language=kotlin 421 | buildFile << """ 422 | import org.jetbrains.gradle.ext.* 423 | 424 | plugins { 425 | id("org.jetbrains.gradle.plugin.idea-ext") 426 | id("java") 427 | } 428 | 429 | idea { 430 | module { 431 | settings { 432 | facets { 433 | create("spring", SpringFacet::class.java) { 434 | contexts { 435 | create("p1") { 436 | file = "spring_parent.xml" 437 | } 438 | 439 | create("p2") { 440 | file = "spring_child.xml" 441 | parent = "p1" 442 | } 443 | } 444 | } 445 | } 446 | 447 | // TODO: support rootModuleType in KTS 448 | // ext.set("rootModuleType", "SOME_TYPE") 449 | moduleType[sourceSets.getByName("main")] = "JAVA_MODULE" 450 | moduleType[sourceSets.getByName("test")] = "PYTHON_MODULE" 451 | } 452 | } 453 | } 454 | 455 | val springFacet: SpringFacet = idea.module.settings.facets.getByName("spring") as SpringFacet 456 | val p2 = springFacet.contexts.getByName("p2") 457 | p2.file = "spring_new_child.xml" 458 | 459 | tasks.register("printSettings") { 460 | doLast { 461 | println(project.projectDir) 462 | println(idea.module.settings) 463 | } 464 | } 465 | """ 466 | when: 467 | def result = GradleRunner.create() 468 | .withGradleVersion(gradleVersion) 469 | .withProjectDir(testProjectDir.root) 470 | .withArguments("printSettings", "-q") 471 | .withPluginClasspath() 472 | .build() 473 | then: 474 | def lines = result.output.readLines() 475 | lines[1] == '{"moduleType":{"main":"JAVA_MODULE","test":"PYTHON_MODULE"},' + 476 | '"facets":[{"type":"spring","contexts":' + 477 | '[{"file":"spring_parent.xml","name":"p1","parent":null},' + 478 | '{"file":"spring_new_child.xml","name":"p2","parent":"p1"}],"name":"spring"}]}' 479 | 480 | result.task(":printSettings").outcome == TaskOutcome.SUCCESS 481 | 482 | where: 483 | gradleVersion << gradleVersionList 484 | } 485 | 486 | def "test module settings with remote source root"() { 487 | given: 488 | // language=kotlin 489 | buildFile << """ 490 | import org.jetbrains.gradle.ext.* 491 | 492 | plugins { 493 | id("org.jetbrains.gradle.plugin.idea-ext") 494 | id("java") 495 | } 496 | 497 | sourceSets { 498 | main { 499 | java { 500 | srcDir("src") 501 | srcDir("../subproject/src") 502 | } 503 | } 504 | } 505 | 506 | idea { 507 | module { 508 | settings { 509 | packagePrefix["src"] = "com.example.java" 510 | packagePrefix["../subproject/src"] = "com.example.java.sub" 511 | } 512 | } 513 | } 514 | 515 | tasks.register("printSettings") { 516 | doLast { 517 | println(idea.module.settings) 518 | } 519 | } 520 | """ 521 | when: 522 | def result = GradleRunner.create() 523 | .withGradleVersion(gradleVersion) 524 | .withProjectDir(testProjectDir.root) 525 | .withArguments("printSettings", "-q") 526 | .withPluginClasspath() 527 | .build() 528 | then: 529 | def lines = result.output.readLines() 530 | def moduleContentRoot = testProjectDir.root.canonicalPath.replace(File.separator, '/') 531 | lines[0] == '{"packagePrefix":{' + 532 | '"' + moduleContentRoot + '/src":"com.example.java",' + 533 | '"' + moduleContentRoot + '/../subproject/src":"com.example.java.sub"' + 534 | '}}' 535 | 536 | result.task(":printSettings").outcome == TaskOutcome.SUCCESS 537 | 538 | where: 539 | gradleVersion << gradleVersionList 540 | } 541 | 542 | def "test module settings with custom source root"() { 543 | given: 544 | // language=kotlin 545 | buildFile << """ 546 | import org.jetbrains.gradle.ext.* 547 | 548 | plugins { 549 | id("org.jetbrains.gradle.plugin.idea-ext") 550 | id("java") 551 | } 552 | 553 | sourceSets { 554 | main { 555 | java.srcDir("src") 556 | } 557 | } 558 | 559 | idea { 560 | module { 561 | settings { 562 | packagePrefix["src"] = "com.example.java" 563 | } 564 | } 565 | } 566 | 567 | tasks.register("printSettings") { 568 | doLast { 569 | println(idea.module.settings) 570 | } 571 | } 572 | """ 573 | when: 574 | def result = GradleRunner.create() 575 | .withGradleVersion(gradleVersion) 576 | .withProjectDir(testProjectDir.root) 577 | .withArguments("printSettings", "-q") 578 | .withPluginClasspath() 579 | .build() 580 | then: 581 | def lines = result.output.readLines() 582 | def moduleContentRoot = testProjectDir.root.canonicalPath.replace(File.separator, '/') 583 | lines[0] == '{"packagePrefix":{"' + moduleContentRoot + '/src":"com.example.java"}}' 584 | 585 | result.task(":printSettings").outcome == TaskOutcome.SUCCESS 586 | 587 | where: 588 | gradleVersion << gradleVersionList 589 | } 590 | 591 | def "test complex project encoding settings"() { 592 | given: 593 | // language=kotlin 594 | buildFile << """ 595 | import org.jetbrains.gradle.ext.* 596 | import org.jetbrains.gradle.ext.EncodingConfiguration.BomPolicy 597 | 598 | plugins { 599 | id("org.jetbrains.gradle.plugin.idea-ext") 600 | } 601 | 602 | idea { 603 | project { 604 | settings { 605 | encodings { 606 | encoding = "windows-1251" 607 | bomPolicy = BomPolicy.WITH_NO_BOM 608 | properties { 609 | encoding = "windows-1251" 610 | transparentNativeToAsciiConversion = false 611 | } 612 | mapping["path/to/dir1"] = "UTF-8" 613 | mapping["path/to/dir2"] = "windows-1251" 614 | mapping["path/to/dir3"] = "ISO-8859-1" 615 | mapping["../path/to/dir4"] = "US-ASCII" 616 | } 617 | } 618 | } 619 | } 620 | 621 | tasks.register("printSettings") { 622 | doLast { 623 | println(idea.project.settings) 624 | } 625 | } 626 | """ 627 | when: 628 | def result = GradleRunner.create() 629 | .withGradleVersion(gradleVersion) 630 | .withProjectDir(testProjectDir.root) 631 | .withArguments("printSettings", "-q") 632 | .withPluginClasspath() 633 | .build() 634 | then: 635 | def lines = result.output.readLines() 636 | def moduleContentRoot = testProjectDir.root.canonicalPath.replace(File.separator, '/') 637 | lines[0] == '{"encodings":{' + 638 | '"encoding":"windows-1251",' + 639 | '"bomPolicy":"WITH_NO_BOM",' + 640 | '"properties":{' + 641 | '"encoding":"windows-1251",' + 642 | '"transparentNativeToAsciiConversion":false' + 643 | '},' + 644 | '"mapping":{' + 645 | '"' + moduleContentRoot + '/path/to/dir1":"UTF-8",' + 646 | '"' + moduleContentRoot + '/path/to/dir2":"windows-1251",' + 647 | '"' + moduleContentRoot + '/path/to/dir3":"ISO-8859-1",' + 648 | '"' + moduleContentRoot + '/../path/to/dir4":"US-ASCII"' + 649 | '}' + 650 | '}' + 651 | '}' 652 | 653 | result.task(":printSettings").outcome == TaskOutcome.SUCCESS 654 | 655 | where: 656 | gradleVersion << gradleVersionList 657 | } 658 | 659 | def "test partial project encoding settings"() { 660 | given: 661 | // language=kotlin 662 | buildFile << """ 663 | import org.jetbrains.gradle.ext.* 664 | 665 | plugins { 666 | id("org.jetbrains.gradle.plugin.idea-ext") 667 | } 668 | 669 | idea { 670 | project { 671 | settings { 672 | encodings { 673 | encoding = "windows-1251" 674 | properties.encoding = "windows-1251" 675 | } 676 | } 677 | } 678 | } 679 | 680 | tasks.register("printSettings") { 681 | doLast { 682 | println(idea.project.settings) 683 | } 684 | } 685 | """ 686 | when: 687 | def result = GradleRunner.create() 688 | .withGradleVersion(gradleVersion) 689 | .withProjectDir(testProjectDir.root) 690 | .withArguments("printSettings", "-q") 691 | .withPluginClasspath() 692 | .build() 693 | then: 694 | def lines = result.output.readLines() 695 | lines[0] == '{"encodings":{' + 696 | '"encoding":"windows-1251",' + 697 | '"properties":{' + 698 | '"encoding":"windows-1251"' + 699 | '}' + 700 | '}' + 701 | '}' 702 | 703 | result.task(":printSettings").outcome == TaskOutcome.SUCCESS 704 | 705 | where: 706 | gradleVersion << gradleVersionList 707 | } 708 | 709 | def "test task triggers settings"() { 710 | given: 711 | // language=kotlin 712 | settingsFile << """ 713 | rootProject.name = "ProjectName" 714 | """ 715 | // language=kotlin 716 | buildFile << """ 717 | 718 | import org.gradle.api.DefaultTask 719 | import org.jetbrains.gradle.ext.* 720 | 721 | plugins { 722 | id("org.jetbrains.gradle.plugin.idea-ext") 723 | } 724 | 725 | 726 | var lazyFlagForTaskTrigger: Boolean by extra 727 | lazyFlagForTaskTrigger = false 728 | val provider = tasks.register("LazyTask") { 729 | lazyFlagForTaskTrigger = true 730 | } 731 | 732 | idea.project.settings { 733 | taskTriggers { 734 | beforeSync(":help", provider) 735 | } 736 | } 737 | 738 | 739 | tasks.register("printSettings") { 740 | doLast { 741 | println("LazyFlag before=[\${lazyFlagForTaskTrigger}]") 742 | println(idea.project.settings) 743 | println("LazyFlag after=[\${lazyFlagForTaskTrigger}]") 744 | println(projectDir.absolutePath.replace('\\\\', '/')) 745 | } 746 | } 747 | """ 748 | when: 749 | def result = GradleRunner.create() 750 | .withGradleVersion(gradleVersion) 751 | .withProjectDir(testProjectDir.root) 752 | .withArguments("printSettings", "-q") 753 | .withPluginClasspath() 754 | .build() 755 | then: 756 | 757 | def lines = result.output.readLines() 758 | def projectDir = lines[3] 759 | "LazyFlag before=[false]" == lines[0] 760 | "LazyFlag after=[true]" == lines[2] 761 | def prettyOutput = prettyPrintJSON(lines[1]) 762 | prettyOutput == 763 | """{ 764 | "taskTriggers": { 765 | "beforeSync": [ 766 | { 767 | "taskPath": "help", 768 | "projectPath": "$projectDir" 769 | }, 770 | { 771 | "taskPath": "LazyTask", 772 | "projectPath": "$projectDir" 773 | } 774 | ] 775 | } 776 | }""" 777 | 778 | result.task(":printSettings").outcome == TaskOutcome.SUCCESS 779 | 780 | where: 781 | gradleVersion << gradleVersionList.findAll { (GradleVersion.version(it) >= GradleVersion.version("5.0")) } 782 | } 783 | 784 | def "test process IDEA project files task"() { 785 | given: 786 | def layoutFile = testProjectDir.newFile('layout.json') 787 | def rootPath = testProjectDir.root.absolutePath.replace('\\', '/') 788 | 789 | layoutFile << """ 790 | { 791 | "ideaDirPath": "${rootPath}/.idea", 792 | "modulesMap": { 793 | "ProjectName": "${rootPath}/.idea/modules/ProjectName.iml", 794 | "ProjectName:test": "${rootPath}/.idea/modules/ProjectName.test.iml", 795 | "ProjectName:main": "${rootPath}/.idea/modules/ProjectName.main.iml" 796 | } 797 | } 798 | """ 799 | def modulesFolder = testProjectDir.newFolder(".idea", "modules") 800 | def ideaDir = modulesFolder.parentFile 801 | def vcsFile = new File(ideaDir, "vcs.xml") 802 | // language=xml 803 | vcsFile << """ 804 | 805 | 806 | 807 | 808 | """ 809 | 810 | def parentModuleFile = new File(modulesFolder, 'ProjectName.iml') 811 | // language=xml 812 | parentModuleFile << """ 813 | 814 | 815 | 816 | 817 | 818 | 819 | 820 | 821 | 822 | 823 | 824 | """ 825 | def mainModuleFile = new File(modulesFolder, 'ProjectName.main.iml') 826 | // language=xml 827 | mainModuleFile << """ 828 | 829 | 830 | 831 | 832 | 833 | 834 | 835 | 836 | 837 | 838 | 839 | 840 | 841 | """ 842 | settingsFile << """ 843 | rootProject.name = "ProjectName" 844 | """ 845 | // language=kotlin 846 | buildFile << """ 847 | 848 | import org.gradle.api.DefaultTask 849 | import org.gradle.api.XmlProvider 850 | import org.jetbrains.gradle.ext.* 851 | import groovy.util.Node 852 | 853 | plugins { 854 | id("org.jetbrains.gradle.plugin.idea-ext") 855 | id("java") 856 | } 857 | 858 | idea.project.settings { 859 | withIDEADir(object: Action { 860 | override fun execute(dir: File) { println("Callback 1 executed with: " + dir.absolutePath) } 861 | }) 862 | 863 | withIDEADir(object: Action { 864 | override fun execute(dir: File) { println("Callback 2 executed with: " + dir.absolutePath) } 865 | }) 866 | 867 | withIDEAFileXml("vcs.xml", object: Action { 868 | // XmlProvider is very Groovy-centric and difficult to use from Kotlin 869 | override fun execute(p: XmlProvider) { 870 | val allNodes = p.asNode().depthFirst() as List 871 | allNodes 872 | .find { it.name() == "mapping" } 873 | ?.attributes()?.put("vcs", "SVN") 874 | } 875 | }) 876 | } 877 | 878 | idea.module.settings { 879 | withModuleFile(object: Action { 880 | override fun execute(file: File) { println("Callback for parent module executed with " + file.absolutePath) } 881 | }) 882 | withModuleXml(object: Action { 883 | override fun execute(p: XmlProvider) { p.asNode().appendNode("test", mapOf("key" to "value")) } 884 | }) 885 | withModuleXml(object: Action { 886 | override fun execute(p: XmlProvider) { p.asNode().appendNode("test2", mapOf("key" to "value")) } 887 | }) 888 | withModuleFile(sourceSets.getByName("main"), object: Action { 889 | override fun execute(file: File) { println("Callback for main module executed with " + file.absolutePath) } 890 | }) 891 | withModuleXml(sourceSets.getByName("main"), object: Action { 892 | override fun execute(p: XmlProvider) { p.asNode().appendNode("testMain", mapOf("key" to "valueMain")) } 893 | }) 894 | } 895 | """ 896 | when: 897 | def result = GradleRunner.create() 898 | .withGradleVersion(gradleVersion) 899 | .withProjectDir(testProjectDir.root) 900 | .withArguments("processIdeaSettings") 901 | .withPluginClasspath() 902 | .withDebug(true) 903 | .build() 904 | then: 905 | 906 | result.task(":processIdeaSettings").outcome == TaskOutcome.SUCCESS 907 | 908 | String ideaDirPath = ideaDir.absolutePath 909 | def lines = result.output.readLines() 910 | assertThat(lines).contains("Callback 1 executed with: " + ideaDirPath, 911 | "Callback 2 executed with: " + ideaDirPath, 912 | "Callback for parent module executed with " + parentModuleFile.absolutePath, 913 | "Callback for main module executed with " + mainModuleFile.absolutePath) 914 | 915 | assertThat(vcsFile).hasContent(""" 916 | 917 | 918 | 919 | 920 | """) 921 | 922 | assertThat(parentModuleFile.text) 923 | .contains("""""") 924 | .contains("""""") 925 | assertThat(mainModuleFile.text).contains("""""") 926 | 927 | where: 928 | gradleVersion << gradleVersionList.findAll { (GradleVersion.version(it) >= GradleVersion.version("7.0")) } 929 | } 930 | 931 | private static String prettyPrintJSON(String line) { 932 | return SerializationUtil.prettyPrintJsonStr(line) 933 | } 934 | } 935 | -------------------------------------------------------------------------------- /src/test/java/org/jetbrains/gradle/ext/SerializationUtil.java: -------------------------------------------------------------------------------- 1 | package org.jetbrains.gradle.ext; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | import com.google.gson.JsonElement; 6 | import com.google.gson.JsonParser; 7 | import com.google.gson.stream.JsonWriter; 8 | 9 | import java.io.IOException; 10 | import java.io.StringWriter; 11 | 12 | public class SerializationUtil { 13 | 14 | public static String prettyPrintJSON(Object object) { 15 | Gson gson = new GsonBuilder() 16 | .disableHtmlEscaping() 17 | .setPrettyPrinting() 18 | .serializeNulls() 19 | .create(); 20 | StringWriter sw = new StringWriter(); 21 | JsonWriter jsonWriter; 22 | try { 23 | jsonWriter = gson.newJsonWriter(sw); 24 | } catch (IOException e) { 25 | throw new RuntimeException("Failed to create new JsonWriter using GSON", e); 26 | } 27 | jsonWriter.setIndent(" "); 28 | if (object instanceof JsonElement) { 29 | gson.toJson((JsonElement) object, jsonWriter); 30 | } else { 31 | gson.toJson(gson.toJsonTree(object), jsonWriter); 32 | } 33 | return sw.toString(); 34 | } 35 | 36 | public static String prettyPrintJsonStr(String rawJson) { 37 | JsonElement jsonElement = JsonParser.parseString(rawJson); 38 | return prettyPrintJSON(jsonElement); 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /src/test/kotlin/org/jetbrains/gradle/ext/BuildIdeArtifactTest.kt: -------------------------------------------------------------------------------- 1 | package org.jetbrains.gradle.ext 2 | 3 | import junit.framework.Assert.assertEquals 4 | import junit.framework.Assert.assertFalse 5 | import org.assertj.core.api.Assertions.assertThat 6 | import org.gradle.api.NamedDomainObjectCollection 7 | import org.gradle.api.Project 8 | import org.gradle.api.tasks.SourceSet 9 | import org.gradle.internal.extensibility.DefaultConvention 10 | import org.gradle.plugins.ide.idea.model.IdeaModel 11 | import org.gradle.testfixtures.ProjectBuilder 12 | import org.jetbrains.gradle.ext.BuildIdeArtifact.DEFAULT_DESTINATION 13 | import org.junit.Before 14 | import org.junit.Test 15 | import java.io.BufferedOutputStream 16 | import java.io.File 17 | import java.io.FileOutputStream 18 | import java.nio.file.Files 19 | import java.util.zip.ZipEntry 20 | import java.util.zip.ZipOutputStream 21 | 22 | const val artifactName = "myArt" 23 | 24 | class BuildIdeArtifactTest { 25 | lateinit var myProject: Project 26 | lateinit var ideArtifact: BuildIdeArtifact 27 | 28 | @Before 29 | fun setup() { 30 | myProject = ProjectBuilder.builder().build() 31 | ideArtifact = myProject.tasks.create("testBuild", BuildIdeArtifact::class.java) 32 | ideArtifact.artifact = myProject.objects.newInstance(TopLevelArtifact::class.java, myProject, artifactName) 33 | } 34 | 35 | @Test 36 | fun `test empty task does nothing`() { 37 | ideArtifact.artifact = null // artifact was already set in setup 38 | ideArtifact.createArtifact() 39 | assertFalse(myProject.layout.buildDirectory.dir(DEFAULT_DESTINATION).get().asFile.exists()) 40 | } 41 | 42 | @Test 43 | fun `test empty artifact creates default destination dir`() { 44 | ideArtifact.createArtifact() 45 | 46 | val target = myProject.layout.buildDirectory.dir(DEFAULT_DESTINATION).get().dir(artifactName).asFile 47 | 48 | assertThat(target) 49 | .exists() 50 | .isDirectory() 51 | } 52 | 53 | @Test 54 | fun `test single file copy`() { 55 | val fileName = "some.txt" 56 | 57 | ideArtifact.artifact.file(fileName) 58 | 59 | val file = myProject.layout.projectDirectory.file(fileName).asFile 60 | val payload = "Payload" 61 | file.writeText(payload) 62 | 63 | ideArtifact.createArtifact() 64 | 65 | val target = myProject.layout.buildDirectory 66 | .dir(DEFAULT_DESTINATION).get() 67 | .dir(artifactName) 68 | .file(fileName) 69 | .asFile 70 | 71 | assertThat(target) 72 | .exists() 73 | .hasContent(payload) 74 | } 75 | 76 | @Test 77 | fun `test custom destination directory`() { 78 | val fileName = "some.txt" 79 | val file = myProject.layout.projectDirectory.file(fileName).asFile 80 | val payload = "Payload" 81 | file.writeText(payload) 82 | 83 | val customOutput = createTempDir() 84 | try { 85 | ideArtifact.artifact.file(fileName) 86 | ideArtifact.outputDirectory = customOutput 87 | 88 | ideArtifact.createArtifact() 89 | 90 | assertThat(File(customOutput, fileName)) 91 | .exists() 92 | .hasContent(payload) 93 | } finally { 94 | customOutput.deleteRecursively() 95 | } 96 | } 97 | 98 | @Test 99 | fun `test subdirectories copy`() { 100 | val fileName = "some.txt" 101 | val d1 = "dir1" 102 | val d2 = "dir2" 103 | ideArtifact.artifact.directory(d1) { 104 | it.directory(d2) { sub -> 105 | sub.file(fileName) 106 | } 107 | } 108 | 109 | val file = myProject.layout.projectDirectory.file(fileName).asFile 110 | val payload = "Payload" 111 | file.writeText(payload) 112 | 113 | ideArtifact.createArtifact() 114 | 115 | val target = myProject.layout.buildDirectory 116 | .dir(DEFAULT_DESTINATION).get() 117 | .dir(artifactName) 118 | .dir(d1) 119 | .dir(d2) 120 | .dir(fileName) 121 | .asFile 122 | 123 | assertThat(target) 124 | .exists() 125 | .hasContent(payload) 126 | } 127 | 128 | @Test 129 | fun `test archives copy`() { 130 | val fileName1 = "some1.txt" 131 | val fileName2 = "some2.txt" 132 | val topArchName = "arch.jar" 133 | val d1 = "dir1" 134 | val archName = "my.zip" 135 | val d2 = "dir2" 136 | 137 | ideArtifact.artifact.archive(topArchName) { topArch -> 138 | topArch.directory(d1) { sub -> 139 | sub.archive(archName) { arch -> 140 | arch.file(fileName1) 141 | arch.directory(d2) { sub1 -> 142 | sub1.file(fileName2) 143 | } 144 | } 145 | } 146 | } 147 | 148 | val payload = "Payload1" 149 | listOf(fileName1, fileName2).forEach { 150 | myProject.layout.projectDirectory.file(it).asFile.writeText(payload) 151 | } 152 | 153 | ideArtifact.createArtifact() 154 | 155 | val arch = myProject.layout.buildDirectory 156 | .dir(DEFAULT_DESTINATION).get() 157 | .dir(artifactName) 158 | .file(topArchName) 159 | .asFile 160 | 161 | assertThat(arch) 162 | .exists() 163 | .hasName(topArchName) 164 | 165 | val topContent = myProject.zipTree(arch).files 166 | assertEquals(1, topContent.size) 167 | 168 | val extracted = topContent.iterator().next() 169 | assertThat(extracted) 170 | .hasName(archName) 171 | assertThat(extracted.parentFile).hasName(d1) 172 | 173 | val innerContent = ArrayList(myProject.zipTree(extracted).files) 174 | innerContent.sortBy { it.name } 175 | assertEquals(2, innerContent.size) 176 | 177 | assertThat(innerContent[0]) 178 | .hasName(fileName1) 179 | .hasContent(payload) 180 | 181 | assertThat(innerContent[1]) 182 | .hasName(fileName2) 183 | .hasContent(payload) 184 | assertThat(innerContent[1].parentFile).hasName(d2) 185 | } 186 | 187 | @Test 188 | fun `test libraries are copied`() { 189 | myProject.repositories.mavenLocal() 190 | myProject.repositories.jcenter() 191 | val myCfg = myProject.configurations.create("myCfg") 192 | myProject.dependencies.add(myCfg.name, "junit:junit:4.12") 193 | 194 | ideArtifact.artifact.libraryFiles(myCfg) 195 | ideArtifact.createArtifact() 196 | 197 | val target = myProject.layout.buildDirectory 198 | .dir(DEFAULT_DESTINATION).get() 199 | .dir(artifactName).asFile 200 | 201 | val fileNames = target.listFiles().map { it.name } 202 | assertThat(fileNames).containsExactlyInAnyOrder("hamcrest-core-1.3.jar", "junit-4.12.jar") 203 | } 204 | 205 | @Test 206 | fun `test directory content copy`() { 207 | val dirName = "myDir" 208 | val fileName = "some.txt" 209 | val payload = "payload" 210 | 211 | val srcFile = myProject.layout.projectDirectory 212 | .dir(dirName) 213 | .file(fileName).asFile 214 | 215 | srcFile.parentFile.mkdir() 216 | srcFile.writeText(payload) 217 | 218 | ideArtifact.artifact.directoryContent(dirName) 219 | ideArtifact.createArtifact() 220 | 221 | val target = myProject.layout.buildDirectory 222 | .dir(DEFAULT_DESTINATION).get() 223 | .dir(artifactName) 224 | .file(fileName).asFile 225 | 226 | assertThat(target).exists() 227 | .hasContent(payload) 228 | } 229 | 230 | @Test 231 | fun `test archive unpacking`() { 232 | val payload = "Payload ;)" 233 | val temp = createTempDir() 234 | val fileName = "some.text" 235 | 236 | try { 237 | val testFile = File(temp, fileName) 238 | testFile.writeText(payload) 239 | val testArchive = myProject.layout.projectDirectory.file("archive.zip").asFile 240 | createTestArchive(temp, testArchive) 241 | 242 | ideArtifact.artifact.extractedDirectory(testArchive) 243 | ideArtifact.createArtifact() 244 | 245 | val target = myProject.layout.buildDirectory 246 | .dir(DEFAULT_DESTINATION).get() 247 | .dir(artifactName) 248 | .file(fileName).asFile 249 | 250 | assertThat(target).exists() 251 | .hasContent(payload) 252 | 253 | } finally { 254 | temp.deleteRecursively() 255 | } 256 | } 257 | 258 | fun createTestArchive(sourceDir: File, destinationFile: File) { 259 | var out = ZipOutputStream(BufferedOutputStream(FileOutputStream(destinationFile))) 260 | out.use { usedZipStream -> 261 | sourceDir.walkBottomUp().forEach { file -> 262 | if (!file.isDirectory()) { 263 | var entry = ZipEntry(file.absolutePath.removePrefix(sourceDir.absolutePath + File.separator)) 264 | usedZipStream.putNextEntry(entry) 265 | Files.copy(file.toPath(), usedZipStream) 266 | } 267 | } 268 | } 269 | } 270 | 271 | @Test 272 | fun `test copy module sources`() { 273 | applyPluginIdea() 274 | createJavaSourceSetWithOutput() 275 | 276 | ideArtifact.artifact.moduleSrc(myProject.ideaModuleName) 277 | ideArtifact.createArtifact() 278 | 279 | val allSourceFiles = collectRelativePathsOfSourceFiles(myProject.sourceSets) 280 | 281 | val target = myProject.layout.buildDirectory 282 | .dir(DEFAULT_DESTINATION).get() 283 | .dir(artifactName).asFile 284 | 285 | assertThat(collectRelativeChildrenPaths(target).toList()) 286 | .containsAll(allSourceFiles) 287 | } 288 | 289 | @Test 290 | fun `test copy module production classes`() { 291 | applyPluginIdea() 292 | createJavaSourceSetWithOutput() 293 | 294 | ideArtifact.artifact.moduleOutput(myProject.ideaModuleName) 295 | ideArtifact.createArtifact() 296 | 297 | val mainOutput = myProject.sourceSets.getByName("main").output 298 | val productionOutputRoots = mainOutput.classesDirs.files + mainOutput.dirs.files + mainOutput.resourcesDir 299 | val productionClassesPaths = productionOutputRoots.flatMap { collectRelativeChildrenPaths(it!!) } 300 | 301 | val target = myProject.layout.buildDirectory 302 | .dir(DEFAULT_DESTINATION).get() 303 | .dir(artifactName).asFile 304 | 305 | assertThat(collectRelativeChildrenPaths(target).toList()) 306 | .hasSameSizeAs(productionClassesPaths) 307 | .containsAll(productionClassesPaths) 308 | } 309 | 310 | @Test 311 | fun `test copy module test classes`() { 312 | applyPluginIdea() 313 | createJavaSourceSetWithOutput() 314 | 315 | ideArtifact.artifact.moduleTestOutput(myProject.ideaModuleName) 316 | ideArtifact.createArtifact() 317 | 318 | val testOutput = myProject.sourceSets.getByName("test").output 319 | val testClassesRoots = testOutput.classesDirs.files + testOutput.dirs.files + testOutput.resourcesDir 320 | val testClassesPaths = testClassesRoots.flatMap { collectRelativeChildrenPaths(it!!) } 321 | 322 | val target = myProject.layout.buildDirectory 323 | .dir(DEFAULT_DESTINATION).get() 324 | .dir(artifactName).asFile 325 | 326 | assertThat(collectRelativeChildrenPaths(target).toList()) 327 | .hasSameSizeAs(testClassesPaths) 328 | .containsAll(testClassesPaths) 329 | } 330 | 331 | private fun collectRelativePathsOfSourceFiles(sourceSets: NamedDomainObjectCollection): List { 332 | return sourceSets.flatMap { sourceSet -> 333 | sourceSet.allSource.srcDirs.flatMap { srcDir -> 334 | collectRelativeChildrenPaths(srcDir) 335 | } 336 | }.toList() 337 | } 338 | 339 | private fun collectRelativeChildrenPaths(srcDir: File) = srcDir.walk() 340 | .filter { it.isFile } 341 | .map { sourceFile -> 342 | sourceFile.toRelativeString(srcDir) 343 | }.asIterable() 344 | 345 | fun applyPluginIdea() { 346 | val param: MutableMap = HashMap() 347 | param["plugin"] = "idea" 348 | myProject.apply(param) 349 | } 350 | 351 | fun createJavaSourceSetWithOutput() { 352 | val param: MutableMap = HashMap() 353 | param["plugin"] = "java" 354 | myProject.apply(param) 355 | val sourceFile = myProject.layout.projectDirectory 356 | .dir("src") 357 | .dir("main") 358 | .dir("java") 359 | .dir("testRoot") 360 | .file("AClass.java") 361 | .asFile 362 | 363 | sourceFile.parentFile.mkdirs() 364 | sourceFile.writeText("""package testRoot; 365 | 366 | public class AClass { 367 | public static void main(String[] args) { 368 | System.out.println("Hello, World!"); 369 | } 370 | } 371 | """) 372 | 373 | val resourceFile = myProject.layout.projectDirectory 374 | .dir("src") 375 | .dir("main") 376 | .dir("resources") 377 | .file("file.text") 378 | .asFile 379 | 380 | resourceFile.parentFile.mkdirs() 381 | resourceFile.writeText("payload") 382 | 383 | val testFile = myProject.layout.projectDirectory 384 | .dir("src") 385 | .dir("test") 386 | .dir("java") 387 | .dir("testRoot") 388 | .file("BClass.java") 389 | .asFile 390 | 391 | testFile.parentFile.mkdirs() 392 | testFile.writeText("""package testRoot; 393 | public class BClass { 394 | public static void main(String[] args) { 395 | System.out.println("Hello, Test World!"); 396 | } 397 | } 398 | """) 399 | } 400 | } 401 | 402 | private val Project.ideaModuleName: String? 403 | get() { 404 | val ideaModel = this.extensions.getByName("idea") as IdeaModel 405 | return ideaModel.module.name 406 | } 407 | 408 | private val Project.sourceSets: NamedDomainObjectCollection 409 | get() { 410 | return (this.extensions as DefaultConvention) 411 | .extensionsAsDynamicObject.getProperty("sourceSets") as? NamedDomainObjectCollection 412 | ?: throw IllegalStateException("Source sets are not available for this project. Check that 'java' plugin is applied") 413 | } 414 | -------------------------------------------------------------------------------- /src/test/kotlin/org/jetbrains/gradle/ext/IdeaFilesProcessorTest.kt: -------------------------------------------------------------------------------- 1 | package org.jetbrains.gradle.ext 2 | 3 | import org.assertj.core.api.Assertions 4 | import org.assertj.core.api.Assertions.* 5 | import org.gradle.api.NamedDomainObjectCollection 6 | import org.gradle.api.Project 7 | import org.gradle.api.internal.tasks.DefaultSourceSet 8 | import org.gradle.api.tasks.SourceSet 9 | import org.gradle.internal.extensibility.DefaultConvention 10 | import org.gradle.testfixtures.ProjectBuilder 11 | import org.junit.Before 12 | import org.junit.Test 13 | 14 | class IdeaFilesProcessorTest { 15 | lateinit var myProject: Project 16 | 17 | @Before 18 | fun setUp() { 19 | myProject = ProjectBuilder.builder().build() 20 | } 21 | 22 | @Test 23 | fun `test callback presence is detected`() { 24 | myProject.apply(mapOf("plugin" to "java")) 25 | val sourceSet = myProject.sourceSets.findByName("main") 26 | 27 | var processor = IdeaFilesProcessor(myProject) 28 | processor.withIDEAFileXml("file.xml") { println(it) } 29 | assertThat(processor.hasPostprocessors()).isTrue() 30 | 31 | processor = IdeaFilesProcessor(myProject) 32 | processor.withIDEAdir({ println(it)} ) 33 | assertThat(processor.hasPostprocessors()).isTrue() 34 | 35 | processor = IdeaFilesProcessor(myProject) 36 | processor.withModuleFile(sourceSet) { println(it) } 37 | assertThat(processor.hasPostprocessors()).isTrue() 38 | 39 | processor = IdeaFilesProcessor(myProject) 40 | processor.withModuleXml(sourceSet) { println(it) } 41 | assertThat(processor.hasPostprocessors()).isTrue() 42 | } 43 | } 44 | 45 | private val Project.sourceSets: NamedDomainObjectCollection 46 | get() { 47 | return (this.extensions as DefaultConvention) 48 | .extensionsAsDynamicObject.getProperty("sourceSets") as? NamedDomainObjectCollection 49 | ?: throw IllegalStateException("Source sets are not available for this project. Check that 'java' plugin is applied") 50 | } -------------------------------------------------------------------------------- /src/test/kotlin/org/jetbrains/gradle/ext/SerializationTests.kt: -------------------------------------------------------------------------------- 1 | package org.jetbrains.gradle.ext 2 | 3 | import org.gradle.api.NamedDomainObjectCollection 4 | import org.gradle.api.Project 5 | import org.gradle.api.internal.provider.DefaultProvider 6 | import org.gradle.api.tasks.SourceSet 7 | import org.gradle.internal.extensibility.DefaultConvention 8 | import org.gradle.testfixtures.ProjectBuilder 9 | import org.intellij.lang.annotations.Language 10 | import org.jetbrains.gradle.ext.SerializationUtil.prettyPrintJSON 11 | import org.junit.Assert.assertEquals 12 | import org.junit.Assert.assertNotNull 13 | import org.junit.Before 14 | import org.junit.Test 15 | import java.io.File 16 | 17 | class SerializationTests { 18 | 19 | lateinit var myProject: Project 20 | 21 | @Before fun setup() { 22 | myProject = ProjectBuilder.builder().build() 23 | } 24 | 25 | data class Holder(val value: String) 26 | 27 | @Test fun `test json special character handling`() { 28 | assertEquals(""" 29 | |{ 30 | | "value": "symbols: =ł" 31 | |} 32 | """.trimMargin(), prettyPrintJSON(Holder("symbols: =ł"))) 33 | } 34 | 35 | @Test fun `test application json output`() { 36 | val application = Application("test", myProject).apply { 37 | beforeRun.create("make", Make::class.java).apply { 38 | enabled = false 39 | } 40 | beforeRun.create("myArtifact", BuildArtifact::class.java).apply { 41 | artifactName = "myName" 42 | } 43 | moduleRef(myProject) 44 | shortenCommandLine = ShortenCommandLine.MANIFEST 45 | } 46 | 47 | assertEquals(""" 48 | |{ 49 | | "defaults": false, 50 | | "type": "application", 51 | | "name": "test", 52 | | "moduleName": "test", 53 | | "envs": null, 54 | | "workingDirectory": null, 55 | | "beforeRun": [ 56 | | { 57 | | "type": "make", 58 | | "enabled": false 59 | | }, 60 | | { 61 | | "type": "buildArtifact", 62 | | "artifactName": "myName" 63 | | } 64 | | ], 65 | | "jvmArgs": null, 66 | | "programParameters": null, 67 | | "mainClass": null, 68 | | "shortenCommandLine": "MANIFEST", 69 | | "includeProvidedDependencies": false 70 | |} 71 | """.trimMargin(), 72 | prettyPrintJSON(application.toMap())) 73 | } 74 | @Test fun `test JarApplication json output`() { 75 | val jarApplication = JarApplication("test", myProject).apply { 76 | beforeRun.create("make", Make::class.java).apply { 77 | enabled = false 78 | } 79 | beforeRun.create("myArtifact", BuildArtifact::class.java).apply { 80 | artifactName = "myName" 81 | } 82 | jarPath = "myJarPath" 83 | } 84 | 85 | assertEquals(""" 86 | |{ 87 | | "defaults": false, 88 | | "type": "jarApplication", 89 | | "name": "test", 90 | | "moduleName": null, 91 | | "envs": null, 92 | | "workingDirectory": null, 93 | | "beforeRun": [ 94 | | { 95 | | "type": "make", 96 | | "enabled": false 97 | | }, 98 | | { 99 | | "type": "buildArtifact", 100 | | "artifactName": "myName" 101 | | } 102 | | ], 103 | | "jvmArgs": null, 104 | | "programParameters": null, 105 | | "jarPath": "myJarPath" 106 | |} 107 | """.trimMargin(), 108 | prettyPrintJSON(jarApplication.toMap())) 109 | } 110 | 111 | @Test 112 | fun `test remote json output`() { 113 | val remote = Remote("remote debug").apply { 114 | host = "hostname" 115 | port = 1234 116 | sharedMemoryAddress = "jvmdebug" 117 | autoRestart = true 118 | } 119 | 120 | @Language("JSON") 121 | val expected = """ 122 | { 123 | "defaults": false, 124 | "type": "remote", 125 | "name": "remote debug", 126 | "mode": "ATTACH", 127 | "port": 1234, 128 | "transport": "SOCKET", 129 | "host": "hostname", 130 | "sharedMemoryAddress": "jvmdebug", 131 | "autoRestart": true 132 | } 133 | """.trimIndent() 134 | assertEquals(expected, prettyPrintJSON(remote.toMap())) 135 | } 136 | 137 | @Test 138 | fun `test JUnit config output`() { 139 | val config = JUnit("myName").apply { 140 | className = "my.TestClass" 141 | repeat = "untilFailure" 142 | workingDirectory = "myWorkDir" 143 | vmParameters = "-Dkey=Value" 144 | passParentEnvs = true 145 | moduleName = "myModule" 146 | envs = mapOf("env1" to "envVal1", "env2" to "envVal2") 147 | defaults = true 148 | shortenCommandLine = ShortenCommandLine.CLASSPATH_FILE 149 | } 150 | 151 | @Language("JSON") 152 | val expected = """ 153 | { 154 | "defaults": true, 155 | "type": "junit", 156 | "name": "myName", 157 | "moduleName": "myModule", 158 | "directory": null, 159 | "repeat": "untilFailure", 160 | "envs": { 161 | "env1": "envVal1", 162 | "env2": "envVal2" 163 | }, 164 | "vmParameters": "-Dkey=Value", 165 | "category": null, 166 | "workingDirectory": "myWorkDir", 167 | "className": "my.TestClass", 168 | "passParentEnvs": true, 169 | "packageName": null, 170 | "pattern": null, 171 | "method": null, 172 | "shortenCommandLine": "CLASSPATH_FILE" 173 | } 174 | """.trimIndent() 175 | assertEquals(expected, prettyPrintJSON(config.toMap())) 176 | } 177 | 178 | @Test fun `test TestNG config output`() { 179 | val config = TestNG("myName").apply { 180 | className = "my.TestClass" 181 | 182 | workingDirectory = "myWorkDir" 183 | vmParameters = "-Dkey=Value" 184 | passParentEnvs = true 185 | moduleName = "myModule" 186 | envs = mapOf("env1" to "envVal1", "env2" to "envVal2") 187 | defaults = true 188 | shortenCommandLine = ShortenCommandLine.ARGS_FILE 189 | } 190 | 191 | @Language("JSON") 192 | val expected = """ 193 | { 194 | "defaults": true, 195 | "type": "testng", 196 | "name": "myName", 197 | "moduleName": "myModule", 198 | "package": null, 199 | "class": "my.TestClass", 200 | "method": null, 201 | "group": null, 202 | "suite": null, 203 | "pattern": null, 204 | "workingDirectory": "myWorkDir", 205 | "vmParameters": "-Dkey=Value", 206 | "passParentEnvs": true, 207 | "envs": { 208 | "env1": "envVal1", 209 | "env2": "envVal2" 210 | }, 211 | "shortenCommandLine": "ARGS_FILE" 212 | } 213 | """.trimIndent() 214 | assertEquals(expected, prettyPrintJSON(config.toMap())) 215 | } 216 | 217 | @Test 218 | fun `test Gradle run configuration output`() { 219 | val absolutePath = File("").absolutePath.replace("\\", "/") 220 | val config = Gradle("gradleName").apply { 221 | projectPath = absolutePath 222 | taskNames = listOf(":cleanTest", ":test") 223 | jvmArgs = "-Dkey=val" 224 | scriptParameters = "-PscriptParam" 225 | envs = mapOf("env1" to "envVal1", "env2" to "envVal2") 226 | defaults = true 227 | } 228 | 229 | @Language("JSON") 230 | val expected = """ 231 | { 232 | "defaults": true, 233 | "type": "gradle", 234 | "name": "gradleName", 235 | "projectPath": "$absolutePath", 236 | "taskNames": [ 237 | ":cleanTest", 238 | ":test" 239 | ], 240 | "envs": { 241 | "env1": "envVal1", 242 | "env2": "envVal2" 243 | }, 244 | "jvmArgs": "-Dkey=val", 245 | "scriptParameters": "-PscriptParam" 246 | } 247 | """.trimIndent() 248 | assertEquals(expected, prettyPrintJSON(config.toMap())) 249 | } 250 | 251 | @Test fun `test Groovy config output`() { 252 | val config = GroovyCompilerConfiguration() 253 | config.excludes { 254 | it.file("C:/myFile.ext") 255 | it.dir("C:/myDir") 256 | it.dir("C:/recursiveDir", true) 257 | } 258 | 259 | assertEquals(""" 260 | |{ 261 | | "excludes": [ 262 | | { 263 | | "url": "C:/myFile.ext", 264 | | "includeSubdirectories": false, 265 | | "isFile": true 266 | | }, 267 | | { 268 | | "url": "C:/myDir", 269 | | "includeSubdirectories": false, 270 | | "isFile": false 271 | | }, 272 | | { 273 | | "url": "C:/recursiveDir", 274 | | "includeSubdirectories": true, 275 | | "isFile": false 276 | | } 277 | | ] 278 | |} 279 | """.trimMargin(), 280 | prettyPrintJSON(config.toMap())) 281 | } 282 | 283 | @Test fun `test code style output`() { 284 | val config = CodeStyleConfig() 285 | 286 | config.java { 287 | it.ifForceBraces = ForceBraces.FORCE_BRACES_IF_MULTILINE 288 | it.classCountToUseImportOnDemand = 42 289 | it.forForceBraces = ForceBraces.FORCE_BRACES_ALWAYS 290 | } 291 | 292 | config.groovy { 293 | it.alignMultilineNamedArguments = true 294 | it.hardWrapAt = 99 295 | } 296 | 297 | assertEquals(""" 298 | |{ 299 | | "USE_SAME_INDENTS": null, 300 | | "RIGHT_MARGIN": null, 301 | | "KEEP_CONTROL_STATEMENT_IN_ONE_LINE": null, 302 | | "languages": { 303 | | "java": { 304 | | "RIGHT_MARGIN": null, 305 | | "WRAP_COMMENTS": null, 306 | | "IF_BRACE_FORCE": "FORCE_BRACES_IF_MULTILINE", 307 | | "DOWHILE_BRACE_FORCE": null, 308 | | "WHILE_BRACE_FORCE": null, 309 | | "FOR_BRACE_FORCE": "FORCE_BRACES_ALWAYS", 310 | | "KEEP_CONTROL_STATEMENT_IN_ONE_LINE": null, 311 | | "CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND": 42, 312 | | "JD_ALIGN_PARAM_COMMENTS": null, 313 | | "JD_ALIGN_EXCEPTION_COMMENTS": null, 314 | | "JD_P_AT_EMPTY_LINES": null, 315 | | "JD_KEEP_EMPTY_PARAMETER": null, 316 | | "JD_KEEP_EMPTY_EXCEPTION": null, 317 | | "JD_KEEP_EMPTY_RETURN": null 318 | | }, 319 | | "groovy": { 320 | | "RIGHT_MARGIN": 99, 321 | | "WRAP_COMMENTS": null, 322 | | "IF_BRACE_FORCE": null, 323 | | "DOWHILE_BRACE_FORCE": null, 324 | | "WHILE_BRACE_FORCE": null, 325 | | "FOR_BRACE_FORCE": null, 326 | | "KEEP_CONTROL_STATEMENT_IN_ONE_LINE": null, 327 | | "CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND": null, 328 | | "ALIGN_NAMED_ARGS_IN_MAP": true 329 | | } 330 | | } 331 | |} 332 | """.trimMargin(), 333 | prettyPrintJSON(config.toMap())) 334 | 335 | } 336 | 337 | @Test fun `test compiler options output`() { 338 | val config = IdeaCompilerConfiguration(myProject) 339 | 340 | config.apply { 341 | processHeapSize = 234 342 | addNotNullAssertions = true 343 | javac { 344 | it.preferTargetJDKCompiler = false 345 | it.javacAdditionalOptions = "-Xmaxwarns 999" 346 | it.moduleJavacAdditionalOptions = mapOf("some.test" to "-Xmaxwarns 899", "some.core" to "-Xmaxwarns 799") 347 | } 348 | additionalVmOptions = "-Xms120" 349 | useReleaseOption = false 350 | } 351 | 352 | assertEquals(""" 353 | |{ 354 | | "processHeapSize": 234, 355 | | "addNotNullAssertions": true, 356 | | "additionalVmOptions": "-Xms120", 357 | | "useReleaseOption": false, 358 | | "javacOptions": { 359 | | "preferTargetJDKCompiler": false, 360 | | "javacAdditionalOptions": "-Xmaxwarns 999", 361 | | "moduleJavacAdditionalOptions": { 362 | | "some.test": "-Xmaxwarns 899", 363 | | "some.core": "-Xmaxwarns 799" 364 | | } 365 | | } 366 | |} 367 | """.trimMargin(), 368 | prettyPrintJSON(config.toMap()) 369 | ) 370 | } 371 | 372 | @Test fun `test task triggers output`() { 373 | val config = TaskTriggersConfig(myProject) 374 | val subProject = ProjectBuilder.builder() 375 | .withProjectDir(File(myProject.projectDir, "subProject")) 376 | .withParent(myProject) 377 | .build() 378 | 379 | val tasks = myProject.tasks 380 | tasks.apply { 381 | create("task1") 382 | create("task2") 383 | create("task3") 384 | create("task4") 385 | create("task5") 386 | } 387 | 388 | val subTasks = subProject.tasks 389 | subTasks.apply { 390 | create("subtask") 391 | } 392 | 393 | config.apply { 394 | beforeBuild(tasks.getByName("task1"), tasks.getByName("task2")) 395 | beforeBuild(tasks.matching { it.name.endsWith("3")}) 396 | afterSync(tasks.getByName("task3")) 397 | beforeRebuild(tasks.getByName("task1"), tasks.named("task4"), DefaultProvider { "task5" } ) 398 | afterRebuild(subTasks.getByName("subtask")) 399 | } 400 | 401 | val escapedRootProjectPath = myProject.projectDir.path.replace("\\", "/") 402 | val subProjectPath = subProject.projectDir.path.replace("\\", "/") 403 | 404 | assertEquals(""" 405 | |{ 406 | | "beforeBuild": [ 407 | | { 408 | | "taskPath": "task1", 409 | | "projectPath": "$escapedRootProjectPath" 410 | | }, 411 | | { 412 | | "taskPath": "task2", 413 | | "projectPath": "$escapedRootProjectPath" 414 | | }, 415 | | { 416 | | "taskPath": "task3", 417 | | "projectPath": "$escapedRootProjectPath" 418 | | } 419 | | ], 420 | | "afterSync": [ 421 | | { 422 | | "taskPath": "task3", 423 | | "projectPath": "$escapedRootProjectPath" 424 | | } 425 | | ], 426 | | "beforeRebuild": [ 427 | | { 428 | | "taskPath": "task1", 429 | | "projectPath": "$escapedRootProjectPath" 430 | | }, 431 | | { 432 | | "taskPath": "task4", 433 | | "projectPath": "$escapedRootProjectPath" 434 | | }, 435 | | { 436 | | "taskPath": "task5", 437 | | "projectPath": "$escapedRootProjectPath" 438 | | } 439 | | ], 440 | | "afterRebuild": [ 441 | | { 442 | | "taskPath": "subtask", 443 | | "projectPath": "$subProjectPath" 444 | | } 445 | | ] 446 | |} 447 | """.trimMargin(), 448 | prettyPrintJSON(config.toMap()) 449 | ) 450 | } 451 | 452 | @Test fun `test artifacts tree`() { 453 | val artifacts = myProject.container(TopLevelArtifact::class.java, TopLevelArtifactFactory(myProject)) 454 | 455 | val rootDir = myProject.rootDir 456 | 457 | val filePath = File(rootDir, "file.txt").apply { 458 | createNewFile() 459 | writeText("Some text") 460 | }.path.replace('\\', '/') 461 | 462 | val dirPath = File(rootDir, "dir").apply { 463 | mkdirs() 464 | File(this, "dir_file.txt").apply { 465 | createNewFile() 466 | writeText("Some text in subdirectory") 467 | } 468 | }.path.replace('\\', '/') 469 | 470 | val archivePath = File(rootDir, "my.zip") 471 | .apply { createNewFile() } 472 | .path.replace('\\', '/') 473 | 474 | val configurationName = "testCfg" 475 | val testCfg = myProject.configurations.create(configurationName) 476 | 477 | // as long as this junit is used for build itself, it can be used safely 478 | myProject.repositories.mavenLocal() 479 | myProject.repositories.jcenter() 480 | myProject.dependencies.add(configurationName, "junit:junit:4.12") 481 | 482 | 483 | artifacts.apply { 484 | create("art1") { 485 | it.directory("dir1") { 486 | it.file(File("file.txt")) 487 | it.archive("arch1") { 488 | it.directoryContent("dir") 489 | it.libraryFiles(testCfg) 490 | } 491 | it.moduleOutput("moduleName") 492 | } 493 | } 494 | create("art2") { 495 | it.artifact("art1") 496 | it.extractedDirectory("my.zip") 497 | } 498 | } 499 | 500 | assertEquals(""" 501 | |{ 502 | | "artifacts": [ 503 | | { 504 | | "type": "ARTIFACT", 505 | | "name": "art1", 506 | | "children": [ 507 | | { 508 | | "type": "DIR", 509 | | "name": "dir1", 510 | | "children": [ 511 | | { 512 | | "type": "FILE", 513 | | "sourceFiles": [ 514 | | "$filePath" 515 | | ] 516 | | }, 517 | | { 518 | | "type": "ARCHIVE", 519 | | "name": "arch1", 520 | | "children": [ 521 | | { 522 | | "type": "DIR_CONTENT", 523 | | "sourceFiles": [ 524 | | "$dirPath" 525 | | ] 526 | | }, 527 | | { 528 | | "type": "LIBRARY_FILES", 529 | | "libraries": [ 530 | | { 531 | | "group": "junit", 532 | | "artifact": "junit", 533 | | "version": "4.12" 534 | | }, 535 | | { 536 | | "group": "org.hamcrest", 537 | | "artifact": "hamcrest-core", 538 | | "version": "1.3" 539 | | } 540 | | ] 541 | | } 542 | | ] 543 | | }, 544 | | { 545 | | "type": "MODULE_OUTPUT", 546 | | "moduleName": "moduleName" 547 | | } 548 | | ] 549 | | } 550 | | ] 551 | | }, 552 | | { 553 | | "type": "ARTIFACT", 554 | | "name": "art2", 555 | | "children": [ 556 | | { 557 | | "type": "ARTIFACT_REF", 558 | | "artifactName": "art1" 559 | | }, 560 | | { 561 | | "type": "EXTRACTED_DIR", 562 | | "sourceFiles": [ 563 | | "$archivePath" 564 | | ] 565 | | } 566 | | ] 567 | | } 568 | | ] 569 | |} 570 | """.trimMargin(), 571 | prettyPrintJSON(mapOf("artifacts" to artifacts.map { it.toMap() })) 572 | ) 573 | 574 | } 575 | 576 | @Test fun `test Make BeforeRunTask json output`() { 577 | val beforeRun = JavaRunConfiguration.createBeforeRun(myProject) 578 | beforeRun.create("make1", Make::class.java) { 579 | it.enabled = true 580 | } 581 | beforeRun.create("make2", Make::class.java) { 582 | it.enabled = false 583 | } 584 | assertNotNull(beforeRun.findByName("make1")) 585 | assertNotNull(beforeRun.findByName("make2")) 586 | assertEquals(""" 587 | |[ 588 | | { 589 | | "type": "make", 590 | | "enabled": true 591 | | }, 592 | | { 593 | | "type": "make", 594 | | "enabled": false 595 | | } 596 | |] 597 | """.trimMargin(), 598 | prettyPrintJSON(beforeRun.map { it.toMap() })) 599 | } 600 | 601 | @Test fun `test GradleTask BeforeRunTask json output`() { 602 | val taskA = myProject.tasks.create("beforeRunTestA") 603 | val taskB = myProject.tasks.create("beforeRunTestB") 604 | val escapedRootProjectPath = myProject.projectDir.path.replace("\\", "/") 605 | val beforeRun = JavaRunConfiguration.createBeforeRun(myProject) 606 | beforeRun.create("gradleTask1", GradleTask::class.java) { 607 | it.task = taskA 608 | } 609 | beforeRun.create("gradleTask2", GradleTask::class.java) { 610 | it.task = taskB 611 | } 612 | assertNotNull(beforeRun.findByName("gradleTask1")) 613 | assertNotNull(beforeRun.findByName("gradleTask2")) 614 | assertEquals(""" 615 | |[ 616 | | { 617 | | "type": "gradleTask", 618 | | "projectPath": "$escapedRootProjectPath", 619 | | "taskName": "${taskA.name}" 620 | | }, 621 | | { 622 | | "type": "gradleTask", 623 | | "projectPath": "$escapedRootProjectPath", 624 | | "taskName": "${taskB.name}" 625 | | } 626 | |] 627 | """.trimMargin(), 628 | prettyPrintJSON(beforeRun.map { it.toMap() })) 629 | } 630 | 631 | @Test fun `test BuildArtifact BeforeRunTask json output`() { 632 | val beforeRun = JavaRunConfiguration.createBeforeRun(myProject) 633 | beforeRun.create("buildArtifact1", BuildArtifact::class.java) { 634 | it.artifactName = "myName1" 635 | } 636 | beforeRun.create("buildArtifact2", BuildArtifact::class.java) { 637 | it.artifactName = "myName2" 638 | } 639 | assertNotNull(beforeRun.findByName("buildArtifact1")) 640 | assertNotNull(beforeRun.findByName("buildArtifact2")) 641 | assertEquals(""" 642 | |[ 643 | | { 644 | | "type": "buildArtifact", 645 | | "artifactName": "myName1" 646 | | }, 647 | | { 648 | | "type": "buildArtifact", 649 | | "artifactName": "myName2" 650 | | } 651 | |] 652 | """.trimMargin(), 653 | prettyPrintJSON(beforeRun.map { it.toMap() })) 654 | } 655 | 656 | @Test fun `test module types json output`() { 657 | val moduleTypes = ModuleTypesConfig(myProject, 658 | myProject.convention) 659 | 660 | myProject.apply(mapOf("plugin" to "java")) 661 | moduleTypes.putAt(myProject.sourceSets.findByName("main"), "PYTHON_MODULE"); 662 | moduleTypes.putAt(myProject.sourceSets.findByName("test"), "JAVA_MODULE"); 663 | 664 | assertEquals(""" 665 | |{ 666 | | "main": "PYTHON_MODULE", 667 | | "test": "JAVA_MODULE" 668 | |} 669 | """.trimMargin(), 670 | prettyPrintJSON(moduleTypes.toMap())) 671 | 672 | } 673 | } 674 | 675 | private val Project.sourceSets: NamedDomainObjectCollection 676 | get() { 677 | return (this.extensions as DefaultConvention) 678 | .extensionsAsDynamicObject.getProperty("sourceSets") as? NamedDomainObjectCollection 679 | ?: throw IllegalStateException("Source sets are not available for this project. Check that 'java' plugin is applied") 680 | } 681 | 682 | --------------------------------------------------------------------------------