Gradle:使用项目类路径执行Groovy交互式shell

Gio*_*tta 9 java shell groovy gradle

我有一个由几个子项目组成的Gradle项目.我刚刚创建了一个新的,以添加对我想要运行的交互式Groovy shell的支持:

gradle console
Run Code Online (Sandbox Code Playgroud)

要么

gradle console:run
Run Code Online (Sandbox Code Playgroud)

所以我的新console模块的build.gradle文件如下:

apply plugin: 'groovy'
apply plugin:'application'

mainClassName = 'org.codehaus.groovy.tools.shell.Main'

dependencies {
  compile 'org.codehaus.groovy:groovy-all:2.2.2'
  compile 'org.fusesource.jansi:jansi:1.11'
  compile 'commons-cli:commons-cli:1.2'
  compile 'jline:jline:2.11'
  compile project(':my-module')
}

task(console, dependsOn: 'classes', type: JavaExec) {
  main = 'org.codehaus.groovy.tools.shell.Main'
  classpath = sourceSets.main.runtimeClasspath
}
Run Code Online (Sandbox Code Playgroud)

但是,当我跑步gradle :console:run或者gradle console得到类似的东西时:

:console:run
Groovy Shell (2.2.2, JVM: 1.6.0_45)
Type 'help' or '\h' for help.
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
groovy:000> 
BUILD SUCCESSFUL

Total time: 4.529 secs
giovanni@mylaptop:~/Projects/my-project$
Run Code Online (Sandbox Code Playgroud)

因此,交互式shell似乎已经开始,但它会立即退出.

难道我做错了什么?

编辑:在build.gradle文件中添加以下内容:

run.standardInput = System.in
Run Code Online (Sandbox Code Playgroud)

现在,从输入流中读取标准输入(由于注释).

但是,Gradle似乎陷入了困境:

Groovy Shell (2.2.2, JVM: 1.6.0_45)
Type 'help' or '\h' for help.
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
groovy:000> 
> Building 88% > :console:run
Run Code Online (Sandbox Code Playgroud)

并且没有接受任何输入.即使这导致了同样的事情:

gradle --no-daemon console:run
Run Code Online (Sandbox Code Playgroud)

2018年更新:

Dylons接受的答案似乎不再起作用,./gradlew console立即退出:

$ ./gradlew console
Run Code Online (Sandbox Code Playgroud)

配置项目:不推荐使用Task.leftShift(Closure)方法,并计划在Gradle 5.0中删除它.请改用Task.doLast(Action).在build_8qb2gvs00xed46ejq1p63fo92.run(/home/jhe052/eclipse-workspace/QuinCe/build.gradle:118)(使用--stacktrace运行以获取此弃用警告的完整堆栈跟踪.)

在3个3个可操作的任务中建立成功:1个执行,2个最新

用doLast替换leftShift(<<)会删除不推荐使用的消息,但结果相同.版本信息:

$ ./gradlew  --version
Run Code Online (Sandbox Code Playgroud)

Gradle 4.4.1

制作时间:2017-12-20 15:45:23 UTC修订:10ed9dc355dc39f6307cc98fbd8cea314bdd381c

Groovy:2.4.12 Ant:2017年2月2日编译的Apache Ant(TM)版本1.9.9 JVM:1.8.0_151(Oracle Corporation 25.151-b12)操作系统:Linux 4.13.0-32-generic amd64

Dyl*_*lon 6

这适用于JDK 7+(对于JDK 6,请参见下图):

configurations {
    console
}

dependencies {
    // ... compile dependencies, runtime dependencies, etc.
    console 'commons-cli:commons-cli:1.2'
    console('jline:jline:2.11') {
        exclude(group: 'junit', module: 'junit')
    }
    console 'org.codehaus.groovy:groovy-groovysh:2.2.+'
}

task(console, dependsOn: 'classes') << {
    def classpath = sourceSets.main.runtimeClasspath + configurations.console

    def command = [
        'java',
        '-cp', classpath.collect().join(System.getProperty('path.separator')),
        'org.codehaus.groovy.tools.shell.Main',
        '--color'
    ]

    def proc = new ProcessBuilder(command)
        .redirectOutput(ProcessBuilder.Redirect.INHERIT)
        .redirectInput(ProcessBuilder.Redirect.INHERIT)
        .redirectError(ProcessBuilder.Redirect.INHERIT)
        .start()

    proc.waitFor()

    if (0 != proc.exitValue()) {
        throw new RuntimeException("console exited with status: ${proc.exitValue()}")
    }
}
Run Code Online (Sandbox Code Playgroud)

为了使此功能适用于JDK 6,我从/sf/answers/299217481/修改了解决方案。我的解决方案是针对标准Linux终端量身定制的,因此,如果您运行的外壳使用换行符使用'\ n'以外的char序列,或者将退格编码为其他127以外的值,则可能需要对其进行一些修改。我不确定如何正确打印颜色,因此它的输出相当单调,但是可以完成工作:

configurations {
    console
}

dependencies {
    // ... compile dependencies, runtime dependencies, etc.
    console 'commons-cli:commons-cli:1.2'
    console('jline:jline:2.11') {
        exclude(group: 'junit', module: 'junit')
    }
    console 'org.codehaus.groovy:groovy-groovysh:2.2.+'
}

class StreamCopier implements Runnable {
    def istream
    def ostream
    StreamCopier(istream, ostream) {
        this.istream = istream
        this.ostream = ostream
    }
    void run() {
        int n
        def buffer = new byte[4096]
        while ((n = istream.read(buffer)) != -1) {
            ostream.write(buffer, 0, n)
            ostream.flush()
        }
    }
}

class InputCopier implements Runnable {
    def istream
    def ostream
    def stdout
    InputCopier(istream, ostream, stdout) {
        this.istream = istream
        this.ostream = ostream
        this.stdout = stdout
    }
    void run() {
        try {
            int n
            def buffer = java.nio.ByteBuffer.allocate(4096)
            while ((n = istream.read(buffer)) != -1) {
                ostream.write(buffer.array(), 0, n)
                ostream.flush()
                buffer.clear()
                if (127 == buffer.get(0)) {
                    stdout.print("\b \b")
                    stdout.flush()
                }
            }
        }
        catch (final java.nio.channels.AsynchronousCloseException exception) {
            // Ctrl+D pressed
        }
        finally {
            ostream.close()
        }
    }
}

def getChannel(istream) {
    def f = java.io.FilterInputStream.class.getDeclaredField("in")
    f.setAccessible(true)
    while (istream instanceof java.io.FilterInputStream) {
        istream = f.get(istream)
    }
    istream.getChannel()
}

task(console, dependsOn: 'classes') << {
    def classpath = sourceSets.main.runtimeClasspath + configurations.console

    def command = [
        'java',
        '-cp', classpath.collect().join(System.getProperty('path.separator')),
        'org.codehaus.groovy.tools.shell.Main'
    ]

    def proc = new ProcessBuilder(command).start()

    def stdout = new Thread(new StreamCopier(proc.getInputStream(), System.out))
    stdout.start()

    def stderr = new Thread(new StreamCopier(proc.getErrorStream(), System.err))
    stderr.start()

    def stdin  = new Thread(new InputCopier(
        getChannel(System.in),
        proc.getOutputStream(),
        System.out))
    stdin.start()

    proc.waitFor()
    System.in.close()
    stdout.join()
    stderr.join()
    stdin.join()

    if (0 != proc.exitValue()) {
        throw new RuntimeException("console exited with status: ${proc.exitValue()}")
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,通过以下方式执行:

gradle console
Run Code Online (Sandbox Code Playgroud)

或者,如果您从gradle中听到很多噪音:

gradle console -q
Run Code Online (Sandbox Code Playgroud)