Gradle refresh时产生的classpath错误

现象

选中项目⇒Gradle⇒refresh之后, ディフォルト出力フォルダ从“APIBot/bin/main”变成了“APIBot/bin/default”

Git上有一些内容可以参考

1. #3825(Closed)

Gradle从4.4版本开始,为每一个源文件目录使用一个单独的出力目录,
缺省情况下,这些出力目录都在bin目录下。
例子:

eclipse {
    classpath.defaultOutputDir = file('build-eclipse')
}

虽然通过上述命令指定了缺省目录,但生成的classpath如下,编译的目标目录还是在bin下面

<classpathentry path="build-eclipse" kind="output"/>

<classpathentry output="bin/main" kind="src" path="src/main/java">
    <attributes>
        <attribute name="gradle_scope" value="main"/>
        <attribute name="gradle_used_by_scope" value="main,test,integrationTest"/>
    </attributes>
</classpathentry>
<classpathentry output="bin/main" kind="src" path="src/main/resources">
    <attributes>
        <attribute name="gradle_scope" value="main"/>
        <attribute name="gradle_used_by_scope" value="main,test,integrationTest"/>
    </attributes>
</classpathentry>
<classpathentry output="bin/test" kind="src" path="src/test/java">
    <attributes>
        <attribute name="gradle_scope" value="test"/>
        <attribute name="gradle_used_by_scope" value="test"/>
    </attributes>
</classpathentry>
<classpathentry output="bin/test" kind="src" path="src/test/resources">
    <attributes>
        <attribute name="gradle_scope" value="test"/>
        <attribute name="gradle_used_by_scope" value="test"/>
    </attributes>
</classpathentry>

defaultOutputDir正如其字面意思一样,是为那些没有指定目标目录的源文件准备的。
Gradle目前给每个目标目录指定了目标目录,因此defaultOutputDir应该没有什么机会被用到

解决方案

classpath {
    defaultOutputDir = file('build/classes/java/main')
    file.whenMerged {
        entries.each {
            source ->
                // This seems kludgy.  If the second test is omitted, it fails processing a 'Project Dependency' entry
                if (source.kind == 'src' && source.toString().contains('output')) {
                    source.output = 'build/classes/java/main'
                }
        }
    }
}

其实想给所有源文件指定目标目录,更像是应该用outputBaseDir
outputBaseDir这个功能在Gradle的#3829票里面准备提供。

2. #4563(Closed)

内容和3825一样。因此被关闭

3. #3839(Open)

oehme提供了一个临时解决方案

classpath {
   file.whenMerged {
       entries.each { entry ->
            if (entry instanceof org.gradle.plugins.ide.eclipse.model.Output) {
               entry.path = entry.path.replace('bin', 'build/eclipse')
            }
       }
   }
}

今后可能会提供一个永久解决方案

classpath {
   outputBaseDir = 'build/eclipse'
}
apply plugin: 'java'
apply plugin: 'eclipse'

eclipse {
  classpath {
    file {
      //if you want to mess with the resulting XML in whatever way you fancy
      withXml {
        def node = it.asNode()
        node.appendNode('xml', 'is what I love')
      }

      //closure executed after .classpath content is loaded from existing file
      //从既存文件中载入.classpath的内容之后,这个闭包内容会被执行
      //but before gradle build information is merged
      //但是gradle build information被merge之前。
      //
      beforeMerged { classpath ->
        //you can tinker with the Classpath here
      }

      //closure executed after .classpath content is loaded from existing file
      //从既存文件中载入.classpath的内容之后,这个闭包内容会被执行(跟beforeMerged相同)
      //and after gradle build information is merged
      //但是gradle build information被merge之后。
      whenMerged { classpath ->
        //you can tinker with the Classpath here
      }
    }
  }
}

关键还是gradle build information的merge时间点,以及什么information。
从跑出来的一些例子看,这个information应该是指所有依赖关系

Gradle官方文档中关于defaultOutputDir的描述
缺省就是bin/default
所以一定要指定。