javaantslashant-contribplantuml

ant-contrib: issues with outofdate


I have a very similar situation as in the the outofdate example (see gengrammer, http://ant-contrib.sourceforge.net/tasks/tasks/outofdate.html): I have a folder with *.class and *.seq files that should be converted to *.pdf with the help of plantuml (a java program).

This is my current task:

<property name="diagram.path" value="../graphics/plantuml/src"/>

...

<target name="diagram-files" depends="update-classpath">
    <outofdate property="manual.outofdate" outputsources="diagram.sources">
        <sourcefiles>
            <fileset dir="${diagram.path}" includes="*.class"/>
            <fileset dir="${diagram.path}" includes="*.seq"/>
        </sourcefiles>
        <mapper type="glob" dir="${diagram.path}" from="*.class" to="graphics/*.pdf"/>
        <mapper type="glob" dir="${diagram.path}" from="*.seq" to="graphics/*.pdf"/>
        <sequential>
            <shellscript shell="bash">
                cd ${diagram.path}
                echo ${diagram.sources}
                #for diagram in ${diagram.sources}
                #do
                # java -jar plantuml ... $diagram
                #done
            </shellscript>
        </sequential>
    </outofdate>
</target>

I can't get this to run because inside ${diagram.sources} all (back)slashes were stripped off. So echoing this variable gives me something like this:

C:UsersMYUSERpath-to-folderANDsoONmyfile.class
C:UsersMYUSERpath-to-folderANDsoONmyfile2.class

Don't know if I made something wrong or this is a feature. Any help would be great!

btw. I'm on Windows 10, but ant is running inside a cygwin shell. I never had problems with this combination.


Solution

  • It's unclear if the backslashes were stripped by the Ant outofdate task before execution reached your bash script, or if the backslashes made it into the script and you're seeing the effects of shell escaping.

    If it's the latter, then you may be able to solve the problem by wrapping ${diagram.sources} in quotes, so that the shell interprets it as a string literal without escaping. For example:

    build.xml

    <project>
        <taskdef resource="net/sf/antcontrib/antcontrib.properties">
            <classpath>
                <pathelement location="/Users/naurc001/ant-contrib-0.6-bin/lib/ant-contrib-0.6.jar"/>
            </classpath>
        </taskdef>
        <property name="diagram.path" value="../graphics/plantuml/src"/>
        <property name="diagram.sources.property" value="C:\private\tmp\anttest\graphics\plantuml\src\myfile.class C:\private\tmp\anttest\graphics\plantuml\src\myfile2.class" />
        <target name="diagram-files">
            <outofdate property="manual.outofdate" outputsources="diagram.sources">
                <sourcefiles>
                    <fileset dir="${diagram.path}" includes="*.class"/>
                    <fileset dir="${diagram.path}" includes="*.seq"/>
                </sourcefiles>
                <mapper type="glob" dir="${diagram.path}" from="*.class" to="graphics/*.pdf"/>
                <mapper type="glob" dir="${diagram.path}" from="*.seq" to="graphics/*.pdf"/>
                <sequential>
                    <shellscript shell="bash">
                        echo Unquoted: ${diagram.sources.property}
                        echo Quoted: '${diagram.sources.property}'
                        for diagram in $(echo '${diagram.sources.property}')
                        do
                            # I don't have plantuml, so just stubbing it to print the command.
                            echo java -jar plantuml ... $diagram
                        done
                    </shellscript>
                </sequential>
            </outofdate>
        </target>
    </project>
    

    Output

    > ant diagram-files
    Buildfile: /private/tmp/anttest/proj/build.xml
    
    diagram-files:
    [shellscript] Unquoted: 
    C:privatetmpanttestgraphicsplantumlsrcmyfile.class 
    C:privatetmpanttestgraphicsplantumlsrcmyfile2.class
    [shellscript] Quoted: 
    C:\private\tmp\anttest\graphics\plantuml\src\myfile.class 
    C:\private\tmp\anttest\graphics\plantuml\src\myfile2.class
    [shellscript] java -jar plantuml ... 
    /private/tmp/anttest/graphics/plantuml/src/myfile.class
    [shellscript] java -jar plantuml ... 
    /private/tmp/anttest/graphics/plantuml/src/myfile2.class
    
    BUILD SUCCESSFUL
    Total time: 0 seconds
    

    Explanation

    I don't have access to a Windows machine for direct testing on that platform, so I have simulated the problem by hard-coding my diagram.sources.property to use Windows-style backslashes in the paths. The <shellscript> echoes the same property twice: once without single quotes and once with single quotes. For the unquoted form, the output looks like what you described due to shell escaping. After quoting it, we get the expected results.

    However, if we just passed '{diagram.sources.property}' on to other commands like jar ..., then we'd have an unintended side effect. Bash would pass the entire list as a single argument. To fix that, we can wrap the whole thing in $(echo ...) to turn it back into multiple arguments. The test output proves that we invoke the jar command once for each file.

    Depending on which tools you need to run, sometimes there is a need to convert Windows-style backslash path separators to Unix-style forward slashes. If that need arises, then I recommend using the cygpath utility inside the bash script to convert backslashes to slashes. In particular, the -u and -w options are helpful here.

    The -u and -w options indicate whether you want a conversion to UNIX (POSIX) format (-u) or to Windows format (-w). Use the -d to get DOS-style (8.3) file and path names. The -m option will output Windows-style format but with forward slashes instead of backslashes. This option is especially useful in shell scripts, which use backslashes as an escape character.

    You mentioned that you are using Cygwin, but perhaps you also have teammates running on a Mac or straight Linux and you need to interop with them. If so, then you can guard the calls to cygpath inside a check against the value of uname -a. In a Cygwin shell, the output of uname -a will contain some form of "Cygwin".

    Additional references on bash escaping: