Search code examples
multithreadingapacheantimagemagickant-contrib

ImageMagick identify.exe returns nothing in parallel Apache Ant project


I use Apache Ant project to gather some information about textures. Here you can see a test project that does only reading without any further actions. This is a minimal set that reproduces one nasty bug. I have found that sometimes ImageMagick's identify.exe does not return anything – I've added a code that forces build to fail if so. If I run this project multiple times I will get unstable behavior. Sometimes project build successfully, sometimes it fails with several fail-messages. Developers of ImageMagick say that their tools are thread safe. But if identify.exe is not the case then what can be? I really need help of someone with advance knowledge about Apache Ant and ImageMagick.

<project default="default">

    <taskdef resource="net/sf/antcontrib/antlib.xml"/>

    <property name="image_magick_path" location="c:\Program Files\ImageMagick-6.8.9-Q8\"/>
    <property name="images_path" location="path\to\folder\with\png\images"/>

    <target name="default">
        <for param="item" parallel="true">
            <path>
                <fileset dir="${images_path}">
                    <patternset id="pattern_images">
                        <include name="**\*.png"/>
                        <include name="**\*.jpg"/>
                        <include name="**\*.gif"/>
                        <include name="**\*.bmp"/>
                    </patternset>
                </fileset>
            </path>
            <sequential>
                <local name="image_width"/>
                <tex_width file="@{item}" property="image_width"/>
                <local name="image_height"/>
                <tex_width file="@{item}" property="image_height"/>
                <if>
                    <or>
                        <equals arg1="${image_width}" arg2=""/>
                        <equals arg1="${image_height}" arg2=""/>
                    </or>
                    <then>
                        <fail message="Got nothing. But why? Image: @{item}"/>
                    </then>
                </if>
            </sequential>
        </for>
    </target>

    <macrodef name="tex_width">
        <attribute name="file"/>
        <attribute name="property"/>
        <sequential>
            <exec executable="${image_magick_path}\identify.exe" outputproperty="@{property}">
                <arg value="-format"/>
                <arg value="%w"/>
                <arg value="@{file}"/>
            </exec>
        </sequential>
    </macrodef>

    <macrodef name="tex_height">
        <attribute name="file"/>
        <attribute name="property"/>
        <sequential>
            <exec executable="${image_magick_path}\identify.exe" outputproperty="@{property}">
                <arg value="-format"/>
                <arg value="%h"/>
                <arg value="@{file}"/>
            </exec>
        </sequential>
    </macrodef>

</project>

Solution

  • Ok, I will write here how I managed to solve my problem. I hope it will help someone someday.

    First thing I found is that PHP method 'getimagesize' is much faster so I decided to switch to it thus killing the main problem. I wrote following macrodef to get both image width and height:

    <macrodef name="getimagesize">
        <attribute name="file"/>
        <attribute name="propertywidth"/>
        <attribute name="propertyheight"/>
        <sequential>
            <local name="output"/>
            <exec executable="php" outputproperty="output">
                <arg value="-r"/>
                <arg value=
                    "&quot;$size=getimagesize('@{file}');
                    echo($size[0].' '.$size[1]);&quot;"
                />
            </exec>
            <propertyregex 
                property="@{propertywidth}" 
                input="${output}" 
                regexp="(\d*) (\d*)" 
                replace="\1"
            />
            <propertyregex 
                property="@{propertyheight}" 
                input="${output}" 
                regexp="(\d*) (\d*)" 
                replace="\2"
            />
        </sequential>
    </macrodef>
    

    Unfortunately this macrodef has abosutely same bug. Sometimes during parallel run some exec-tasks returned nothing in output. I was very upset so I decided to write another macrodef which I use now and finally it works fine. What I did was avoid reading anything from exec-task's 'stdout' and use tempfile-task instead. Here's final macrodef:

    <macrodef name="getimagesize">
        <attribute name="file"/>
        <attribute name="propertywidth"/>
        <attribute name="propertyheight"/>
        <sequential>
            <local name="file_dirname"/>
            <dirname property="file_dirname" file="@{file}"/>
    
            <local name="file_temp"/>
            <tempfile property="file_temp" destdir="${file_dirname}" createfile="true"/>
    
            <exec executable="php">
                <arg value="-r"/>
                <arg value="&quot;$size=getimagesize('@{file}');
                    file_put_contents('${file_temp}', $size[0].' '.$size[1]);&quot;"/>
            </exec>
    
            <local name="file_temp_content"/>
            <loadfile property="file_temp_content" srcfile="${file_temp}"/>
            <delete file="${file_temp}"/>
    
            <propertyregex 
                property="@{propertywidth}" 
                input="${file_temp_content}" 
                regexp="(\d*) (\d*)" 
                replace="\1"
            />
    
            <propertyregex 
                property="@{propertyheight}" 
                input="${file_temp_content}" 
                regexp="(\d*) (\d*)" 
                replace="\2"
            />
        </sequential>
    </macrodef>