Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Groovy scripts no longer work under Cygwin?

Tags:

cygwin

groovy

In older versions of Groovy, I could run Groovy as a shell script under Cygwin, following their own instructions for doing so:

$ cat ~/bin/hiworld
#!/usr/bin/env groovy
println("Hello world")

This worked. However, under (at least) Groovy 2.3.2 and 2.3.3, I'm seeing this instead:

$ hiworld
Caught: java.net.MalformedURLException: unknown protocol: c
java.net.MalformedURLException: unknown protocol: c

My best stab in the dark: "env" launches scripts via an absolute path (e.g. "groovy /home/myacct/bin/hiworld"), and newer versions of Groovy have been, un, 'improved' so that Groovy no longer understands how to handle this.

Indeed, I can produce the same error by doing that:

$ groovy ~/bin/hiworld
Caught: java.net.MalformedURLException: unknown protocol: c
java.net.MalformedURLException: unknown protocol: c

So I'm not sure how Groovy is (a) resolving that to a windows-style path, and then (b) failing to understand it's a windows-style path.

I can "fix" this, then, by running it thusly:

$ groovy $(cygpath -w ~/bin/hiworld)
Hello world

... but, c'mon, that's a completely insane way of having users launch a utility script. (Or I could write a "front" script, with just that line, to launch another script, of course. But then, for what I'm trying to ultimately accomplish, I might as well just give up on Groovy and distribute a runnable JAR with an associated launching script.)

So has Groovy simply dropped support for Cygwin? Or is it really possible that they've gone at least two releases without testing their own recommended way of running a script under one of the most popular environments?

If not, what am I missing or doing wrong?


Update: I thought it would be helpful to back up some of the things I'm suggesting here.

First, I want note Cygwin clearly is (or was) at least somewhat supported: groovyStart, for example, has considerable code in it supporting the Cygwin platform (seemingly as much as, say Mac OSX). As noted, it apparently worked fine under previous versions.

Currently, the last example resolves, under groovyStart, to this:

'/cygdrive/c/Program Files/Java/jdk1.7.0_51/bin/java' -classpath C:/cygwin64/home/myacct/opt/groovy-2.3.3/lib/groovy-2.3.3.jar -Dscript.name=/home/myacct/opt/groovy-2.3.3/bin/groovy -Dprogram.name=groovy -Dgroovy.starter.conf=C:/cygwin64/home/myacct/opt/groovy-2.3.3/conf/groovy-starter.conf -Dgroovy.home=C:/cygwin64/home/myacct/opt/groovy-2.3.3 '-Dtools.jar=C:/Program Files/Java/jdk1.7.0_51/lib/tools.jar' org.codehaus.groovy.tools.GroovyStarter --main groovy.ui.GroovyMain --conf C:/cygwin64/home/myacct/opt/groovy-2.3.3/conf/groovy-starter.conf --classpath . C:/cygwin64/home/myacct/bin/hiworld

Just to clarify, there's no problem invoking the JDK itself -- runs that part of the command runs just fine. The part which is broken is the very last argument: if I change

C:/cygwin64/home/myacct/bin/hiworld

to

file:///C:/cygwin64/home/myacct/bin/hiworld

... it works again. This agrees with my assertion above that "groovy" (the script) is indeed correctly converting from Cygwin/UNIX-style paths to a native Windows path, but the underlying process -- running in Windows Java -- is actually confused by having been a windows path! Apparently it was expecting a URL.


Update 2: Below, Warren makes the excellent suggestion of trying to use GVM. Sadly, this still produces the same error:

$ which groovy
/home/myacct/.gvm/groovy/current/bin/groovy

$ hiworld
Caught: java.net.MalformedURLException: unknown protocol: c
java.net.MalformedURLException: unknown protocol: c

$ sh $(which groovy) ~/bin/hiworld
Caught: java.net.MalformedURLException: unknown protocol: c
java.net.MalformedURLException: unknown protocol: c

Adding the "-x" flag to the previous command shows that Groovy is still using my Windows JVM (which isn't wrong, just noting) and is now referencing the GVM-installed libraries (shown here reformatted slightly for readability):

'/cygdrive/c/Program Files/Java/jdk1.7.0_51/bin/java' \
  -classpath C:/cygwin64/home/myacct/.gvm/groovy/2.3.3/lib/groovy-2.3.3.jar \
  -Dscript.name=/home/C400334/.gvm/groovy/current/bin/groovy \
  -Dprogram.name=groovy \
  -Dgroovy.starter.conf=C:/cygwin64/home/myacct/.gvm/groovy/2.3.3/conf/groovy-starter.conf \
  -Dgroovy.home=C:/cygwin64/home/myacct/.gvm/groovy/2.3.3 \
  '-Dtools.jar=C:/Program Files/Java/jdk1.7.0_51/lib/tools.jar' \
  org.codehaus.groovy.tools.GroovyStarter \
  --main groovy.ui.GroovyMain \
  --conf C:/cygwin64/home/myacct/.gvm/groovy/2.3.3/conf/groovy-starter.conf \
  --classpath . \
  C:/cygwin64/home/myacct/bin/hiworld

As before, adding a "file:///" before the final argument seems to resolve the problem.

So I'm wondering perhaps if we're using a different version of the JVM or something?


Update 3: Upgrading to jdk1.7.0_60 (tried both 64- and 32-bit versions) but that didn't seem to make a difference. Java 6 exhibits the same problem, but also adds a complaint about missing NioGroovyMethods.

like image 620
Tim W Avatar asked Oct 20 '22 06:10

Tim W


2 Answers

I'm not sure how Groovy is (a) resolving that to a windows-style path

The startGroovy script has specific code in it to detect Cygwin and use cygpath as necessary.

I discovered this by installing Groovy via GVM, the approved way of getting Groovy for Unixy platforms. (You do not want to use the native Windows distribution of Groovy in this case!)

The only tricky thing was figuring out how to set JAVA_HOME. Here, it needed to be:

 $ export JAVA_HOME='/cygdrive/c/Program Files (x86)/Java/jre7'

If you're on a 64-bit version of Windows and have a 32-bit JRE installed as I do, this should also work for you. Otherwise, you may have to adjust the path.

Once I got gvm install groovy to succeed, your hiworld example worked fine here if I ran it as ./hiworld. This was with the 32-bit version of Cygwin with Java 1.7.0_55.

However, when I put it in the PATH and ran it as hiworld, as you are doing, this passed a fully-qualified path to the groovy wrapper script (e.g. /home/wyoung/bin/hiworld) instead of a relative path, which caused the startGroovy script to run the path through cygpath -m, which turns that into something like C:/cygwin64/home/wyoung/bin/hiworld. Oracle's JRE can't cope with forward slashes in local paths. It blindly assumes that forward slashes on Windows means it's a URL of some kind, so C: gets treated as the URL scheme, or "protocol" as they put it.

I don't know if this is a regression in Java or in the startGroovy script. Although you could pass a file:// URL here as you discovered, you can also pass a "proper" Windows path with backslashes instead of forward slashes. You get that from cygpath with the -w switch instead of -m. You have to be a lot more careful about avoiding accidental backslash escaping in this case, which may explain the regression.

like image 140
Warren Young Avatar answered Oct 29 '22 00:10

Warren Young


Just hit the same problem and changed the exec call in the startGroovy script to the following (note the last two lines):

    exec "$JAVACMD" $JAVA_OPTS \
        -classpath "$STARTER_CLASSPATH" \
        -Dscript.name="$SCRIPT_PATH" \
        -Dprogram.name="$PROGNAME" \
        -Dgroovy.starter.conf="$GROOVY_CONF" \
        -Dgroovy.home="$GROOVY_HOME" \
        -Dtools.jar="$TOOLS_JAR" \
        $STARTER_MAIN_CLASS \
        --main $CLASS \
        --conf "$GROOVY_CONF" \
        --classpath "$CP" \
        "$(cygpath -w $1)" \
        "${@:2}"

... that way the script being called is altered to a windows path with backslashes as suggested above, and then not interpreted as a URL it seems. Nasty hack, but at least I was able to execute my script again :)

like image 34
Clive Jevons Avatar answered Oct 29 '22 01:10

Clive Jevons