ilscipio/scipio-erp

View on GitHub
macros.xml

Summary

Maintainability
Test Coverage
<?xml version="1.0" encoding="UTF-8"?>
<!--
This file is subject to the terms and conditions defined in the
files 'LICENSE' and 'NOTICE', which are part of this source
code package.
-->
<project name="Ant - Macros"
    xmlns:ivy="antlib:org.apache.ivy.ant">

    <!-- SCIPIO: NOTE: Despite the filename, this is used for all types of definitions
        common to both main build.xml, component build.xml, and limited support for
        individual component build.xml invocation in some cases. -->

    <dirname property="ofbiz.home.dir" file="${ant.file.Ant - Macros}"/>

    <!-- ================================================================== -->
    <!-- Default compilation settings -->
    <!-- ================================================================== -->

    <!-- ant -->
    <property name="minimumantversion" value="1.9.1"/>

    <!-- javac (run ant clean after changing) -->
    <property name="scipio.build.javac.release" value="11"/><!-- 11 -->
    <property name="scipio.build.javac.compiler" value="javac10+"/><!-- javac10+ -->

    <!-- deprecated: source/target: These are unset and have no effect here anymore -
            if you need them, re-add them back to the javac-std presetdef further below -->
    <property name="scipio.build.javac.source" value=""/><!-- 11 -->
    <property name="scipio.build.javac.target" value=""/><!-- 11 -->

    <!-- ================================================================== -->
    <!-- Common/Generic definitions for all builds/buildfiles -->
    <!-- ================================================================== -->

    <!-- paths for component build.xml buildfiles -->
    <path id="local.class.path"/>
    <patternset id="src.inc.set">
        <include name="**/*.java"/>
    </patternset>
    <patternset id="src.exc.set"/>

    <!-- ================================================================== -->
    <!-- Ant extensions (taskdefs) -->
    <!-- ================================================================== -->

    <!-- ant-contrib -->
    <path id="ant-contrib.class.path">
        <fileset dir="${ofbiz.home.dir}/framework/base/lib/ant/" includes="ant-contrib-*.jar"/>
    </path>
    <taskdef resource="net/sf/antcontrib/antlib.xml" classpathref="ant-contrib.class.path"/>

    <!-- ivy (SCIPIO) (see ivy-init for taskdef) -->
    <property name="ivy.settings.file" value="${ofbiz.home.dir}/ivy/ivysettings.xml"/>
    <property name="lib.general.ivyfile" value="${ofbiz.home.dir}/ivy/ivy.xml"/>
    <property name="lib.comp.base.ivyfile" value="${ofbiz.home.dir}/framework/base/ivy.xml"/>
    <path id="lib.ivy.class.path">
        <fileset dir="${ofbiz.home.dir}/framework/base/lib/ant/" includes="ivy-*.jar"/>
        <fileset dir="${ofbiz.home.dir}/framework/base/lib/" includes="oro-*.jar"/>
    </path>

    <!-- groovy (SCIPIO: NOTE: 2018-03-20: see groovy-init for taskdef) -->
    <path id="groovy.class.path">
        <fileset dir="${ofbiz.home.dir}/framework/base/lib/" includes="groovy-*.jar"/>
    </path>

    <property name="lib.ant.js.engine.name" value="nashorn"/>
    <path id="lib.ant.js.class.path">
        <fileset dir="${ofbiz.home.dir}/framework/base/lib/ant-ext/js/" includes="*.jar"/>
    </path>

    <!-- junit -->
    <path id="junit.class.path">
        <!-- SCIPIO: 2017-02-02: The junit JAR itself should now be explicitly specified, as may miss from classpath or wrong one on classpath -->
        <fileset dir="${ofbiz.home.dir}/framework/base/lib" includes="junit-*.jar"/>
        <fileset dir="${ofbiz.home.dir}/framework/base/lib/ant/" includes="ant-apache-bsf-*.jar"/>
        <!-- scripting (previously under base/lib/scripting) -->
        <fileset dir="${ofbiz.home.dir}/framework/base/lib/" includes="antlr-*.jar"/>
        <!-- SCIPIO: 2018-07-06: junit should not need this
        <fileset dir="${ofbiz.home.dir}/framework/base/lib/ant-ext" includes="asm-*.jar"/>-->
        <fileset dir="${ofbiz.home.dir}/framework/base/lib/" includes="bsf-*.jar"/>
        <fileset dir="${ofbiz.home.dir}/framework/base/lib/" includes="bsh-*.jar"/>
        <fileset dir="${ofbiz.home.dir}/framework/base/lib/" includes="groovy-*.jar"/>
        <!-- SCIPIO: removed
        <fileset dir="${ofbiz.home.dir}/framework/base/lib/" includes="jython-*.jar"/>-->
        <fileset dir="${ofbiz.home.dir}/framework/base/lib/" includes="oro-*.jar"/>
        <fileset dir="${ofbiz.home.dir}/framework/base/lib/" includes="hamcrest-*.jar"/><!-- SCIPIO: 2018-03-27: hamcrest-core dep for junit -->
    </path>

    <!-- cobertura -->
    <path id="cobertura.class.path">
        <fileset dir="${ofbiz.home.dir}/framework/base/lib/ant-opt/cobertura" includes="*.jar"/>
        <!-- SCIPIO: 2018-07-06: for now, included under ant-opt/cobertura (what else would use asm?)
        <fileset dir="${ofbiz.home.dir}/framework/base/lib/ant-ext" includes="asm-*.jar"/>-->
        <fileset dir="${ofbiz.home.dir}/framework/base/lib/" includes="junit-*.jar"/>
        <fileset dir="${ofbiz.home.dir}/framework/base/lib/" includes="log4j-*.jar"/>
        <!-- SCIPIO: version compability problems
        <fileset dir="${ofbiz.home.dir}/framework/base/lib/" includes="slf4j-api-*.jar"/>-->
        <fileset dir="${ofbiz.home.dir}/framework/base/lib/" includes="oro-*.jar"/>
    </path>
    
    <!-- jacoco -->
    <path id="jacoco.class.path">
        <fileset dir="${ofbiz.home.dir}/framework/base/lib/ant-opt/jacoco" includes="*.jar"/>
        <fileset dir="${ofbiz.home.dir}/framework/base/lib/" includes="junit-*.jar"/>
        <fileset dir="${ofbiz.home.dir}/framework/base/lib/" includes="log4j-*.jar"/>
    </path>
    
    <path id="jacoco.agent.class.path">
        <fileset dir="${ofbiz.home.dir}/framework/base/lib/ant-opt/jacoco" includes="*agent*.jar"/>
    </path>

    <!-- ================================================================== -->
    <!-- Common javac compilation macros/presets -->
    <!-- ================================================================== -->

    <presetdef name="default-javac">
        <javac debug="on" deprecation="on" destdir="${build.dir}/classes" srcdir="${src.dir}" classpathref="local.class.path">
            <patternset refid="src.inc.set"/>
            <patternset refid="src.exc.set"/>
        </javac>
    </presetdef>

    <!-- SCIPIO: 3.0.0: javac-std replaces javac18 and targets minimum JDK 11  -->
    <presetdef name="javac-std">
        <!-- NOTE: JDK9+: The javac source/target options are deprecated and now removed; if you still try to compile
                using JDK8, re-add these: source="${scipio.build.javac.source}" target="${scipio.build.javac.target}" -->
        <default-javac compiler="${scipio.build.javac.compiler}" release="${scipio.build.javac.release}" encoding="UTF-8" includeantruntime="false">
            <compilerarg value="-Xlint:-path"/>
            <!-- Please leave this line here.  It makes it easier to enable/disable it. -->
            <!--<compilerarg value="-Xlint:unchecked"/>-->
        </default-javac>
    </presetdef>

    <!-- SCIPIO: 3.0.0: DEPRECATED: Delegates to javac-std -->
    <presetdef name="javac18">
        <javac-std/>
    </presetdef>

    <!-- ================================================================== -->
    <!-- Build requirement checks -->
    <!-- ================================================================== -->

    <!-- FOR DEVELOPERS:
        If you are a developer using an IDE-provided Ant version and certain there are no problems,
        you can eliminate the provided ant version warning by uncommenting the following line or setting this property: -->
    <!--<property name="antlib.check.ok" value="true"/>-->

    <!-- SCIPIO: 2018-04-04: The ant and JDK version checks are now only done once with include guard
        NOTE: the macro call is further below due to ant-contrib use -->
    <macrodef name="check-reqs">
        <sequential>
            <if>
                <not><isset property="checkreqs.ran"/></not>
                <then>
                    <antversion property="antversion" atleast="${minimumantversion}"/>
                    <fail unless="antversion">ANT VERSION: Scipio build requires minimum ant version: ${minimumantversion}
You are running: ${ant.version} (${ant.core.lib})

If you are running from command line, an ant jar is provided with Scipio (framework/base/lib/ant/);
using a system ant installation is not recommended, as such configurations may not be tested.

From command line, Scipio build should be invoked using the provided ant launch scripts in project root:
* Linux, Mac, Cygwin, Windows Git Bash:    ./ant [target]
* Windows (no Bash, through ant.bat):      ant [target]</fail>

                    <condition property="antlib.check.ok">
                        <contains substring="framework${file.separator}base${file.separator}lib${file.separator}ant${file.separator}" string="${ant.core.lib}"/>
                    </condition>
                    <if>
                        <not><isset property="antlib.check.ok"/></not>
                        <then>
                            <path id="antlib.provided.jar.full">
                                <fileset dir="${ofbiz.home.dir}/framework/base/lib/ant/" includes="ant-*-ant.jar"/>
                            </path>
                            <property name="antlib.provided.jar.rel" value="${toString:antlib.provided.jar.full}" relative="true" basedir="${ofbiz.home.dir}"/>
                            <!--<antversion property="providedantversion.ok" exactly="${providedantversion}"/>-->
                            <echo level="warning">WARNING: The ant jar executing is not the one provided with Scipio (${antlib.provided.jar.rel})
You are running: ${ant.version} (${ant.core.lib})

From command line, Scipio build should be invoked using the provided ant launch scripts in project root:
* Linux, Mac, Cygwin, Windows Git Bash:    ./ant [target]
* Windows (no Bash, through ant.bat):      ant [target]
(Developers can eliminate this message for IDEs by setting the antlib.check.ok property to true)</echo>
                        </then>
                    </if>

                    <echo message="Current Java version (ant.java.version): ${ant.java.version} (target release: ${scipio.build.javac.release})"/>
                    <!-- SCIPIO: 3.0.0: JDK version minimum requirement is now 11 -->
                    <condition property="javaatleast11">
                        <not>
                            <matches pattern="^1\.[0-8]($|\..*)" string="${ant.java.version}"/>
                        </not>
                    </condition>
                    <fail unless="javaatleast11" message="Please upgrade Java JDK to 11 or higher."/>

                    <property name="checkreqs.ran" value="true"/>
                </then>
            </if>
        </sequential>
    </macrodef>

    <target name="check-reqs">
        <check-reqs/>
    </target>

    <!-- trigger the requirements check immediately (requires ant-contrib) -->
    <check-reqs/>

    <!-- ================================================================== -->
    <!-- Generic Ant macro library (SCIPIO) -->
    <!-- ================================================================== -->

    <macrodef name="condition-ext" description="Custom emulation of ant condition task for ant-contrib var assignments, with unset support">
        <attribute name="var" default=""/>
        <attribute name="property" default=""/>
        <attribute name="noneValue" default="__NONE__"/><!-- Ant macrodef workaround -->
        <attribute name="value" default="@{noneValue}"/>
        <attribute name="unset" default="false"/>
        <attribute name="else" default="@{noneValue}"/>
        <attribute name="else-unset" default="false"/>
        <element name="if-condition" optional="false" implicit="true"/>
        <sequential>
            <if><!-- SCIPIO: 2018-05-08: backward compat for old scripts -->
                <if-condition/>
                <then>
                    <if>
                        <equals arg1="@{unset}" arg2="true"/>
                        <then>
                            <if>
                                <not><equals arg1="@{var}" arg2=""/></not>
                                <then>
                                    <!-- NOTE: must unset first, in case using this to override a property (see ant-contrib docs) -->
                                    <var name="@{var}" unset="true"/>
                                </then>
                                <else>
                                    <var name="@{property}" unset="true"/>
                                </else>
                            </if>
                        </then>
                        <elseif>
                            <not><equals arg1="@{value}" arg2="__NONE__"/></not>
                            <then>
                                <if>
                                    <not><equals arg1="@{var}" arg2=""/></not>
                                    <then>
                                        <!-- NOTE: must unset first, in case using this to override a property (see ant-contrib docs) -->
                                        <var name="@{var}" unset="true"/>
                                        <var name="@{var}" value="@{value}"/>
                                    </then>
                                    <else>
                                        <var name="@{property}" unset="true"/>
                                        <property name="@{property}" value="@{value}"/>
                                    </else>
                                </if>
                            </then>
                        </elseif>
                    </if>
                </then>
                <else>
                    <if>
                        <equals arg1="@{else-unset}" arg2="true"/>
                        <then>
                            <if>
                                <not><equals arg1="@{var}" arg2=""/></not>
                                <then>
                                    <!-- NOTE: must unset first, in case using this to override a property (see ant-contrib docs) -->
                                    <var name="@{var}" unset="true"/>
                                </then>
                                <else>
                                    <var name="@{property}" unset="true"/>
                                </else>
                            </if>
                        </then>
                        <elseif>
                            <not><equals arg1="@{else}" arg2="__NONE__"/></not>
                            <then>
                                <if>
                                    <not><equals arg1="@{var}" arg2=""/></not>
                                    <then>
                                        <!-- NOTE: must unset first, in case using this to override a property (see ant-contrib docs) -->
                                        <var name="@{var}" unset="true"/>
                                        <var name="@{var}" value="@{else}"/>
                                    </then>
                                    <else>
                                        <var name="@{property}" unset="true"/>
                                        <property name="@{property}" value="@{else}"/>
                                    </else>
                                </if>
                            </then>
                        </elseif>
                    </if>
                </else>
            </if>
        </sequential>
    </macrodef>

    <!-- ================================================================== -->
    <!-- Common build macros/presets -->
    <!-- ================================================================== -->

    <presetdef name="default-javadoc">
        <javadoc classpathref="local.class.path"
            destdir="${build.dir}/javadocs"
            windowtitle="Apache OFBiz - ${desc}"
            useexternalfile="yes"
            maxmemory="512M"
            encoding="UTF-8"
            packagenames="org.ofbiz.*">
            <fileset dir="${basedir}" defaultexcludes="yes">
                <include name="**/*.java"/>
                <exclude name="**/ControlApplet.java"/>
                <exclude name="**/ShipmentScaleApplet.java"/>
                <exclude name="**/test/"/>
                <exclude name="**/cybersource/"/>
                <exclude name="**/PayPalServices.java"/>
                <exclude name="**/ideal/"/>
                <exclude name="**/orbital/"/>
                <exclude name="**/securepay/"/>
                <exclude name="**/verisign/"/>
                <exclude name="**/JREntityListIteratorDataSource.java"/>
                <exclude name="**/JRMapCollectionDataSource.java"/>
                <exclude name="**/openoffice/"/>
                <exclude name="**/JasperReportsXmlViewHandler.java"/>
                <exclude name="**/JasperReportsJXlsViewHandler.java"/>
                <exclude name="**/JasperReportsPoiXlsViewHandler.java"/>
            </fileset>
            <link href="http://java.sun.com/javase/7/docs/api/" offline="true" packagelistLoc="${ofbiz.home.dir}/tools/api-java17"/>
        </javadoc>
    </presetdef>

    <!-- SCIPIO: NOTE: 2018-03-20: This requires groovy-init task in main build -->
    <macrodef name="default-groovyc">
        <sequential>
            <groovyc destdir="${build.dir}/classes" srcdir="${src.dir}">
                <classpath>
                    <path refid="local.class.path"/>
                    <pathelement path="${build.dir}/classes"/>
                </classpath>
            </groovyc>
        </sequential>
    </macrodef>

    <macrodef name="main-jar">
        <attribute name="jarfile" default="${build.dir}/lib/${name}.jar"/>
        <element name="main-pattern" optional="true"/>
        <element name="main-elements" optional="true" />
        <sequential>
            <jar jarfile="@{jarfile}">
                <fileset dir="${build.dir}/classes">
                    <exclude name="**/test" />
                    <exclude name="**/test/*" />
                    <main-pattern />
                </fileset>
                <fileset dir="${src.extra.dir}">
                    <and>
                        <not>
                            <or>
                                <filename name="**/test"/>
                                <filename name="**/test/*"/>
                            </or>
                        </not>
                        <selector refid="src-extra-set"/>
                    </and>
                </fileset>
                <!-- now add the NOTICE and LICENSE files to allow the jar file to be distributed alone -->
                <zipfileset dir="${ofbiz.home.dir}" prefix="META-INF" includes="NOTICE,LICENSE"/>
                <main-elements/>
            </jar>
        </sequential>
    </macrodef>

    <macrodef name="test-jar">
        <attribute name="jarfile" default="${build.dir}/lib/${name}-test.jar"/>
        <element name="test-selector" optional="true"/>
        <element name="test-elements" optional="true"/>
        <sequential>
            <jar jarfile="@{jarfile}">
                <fileset dir="${build.dir}/classes">
                    <or>
                        <filename name="**/test"/>
                        <filename name="**/test/*"/>
                        <test-selector/>
                    </or>
                </fileset>
                <fileset dir="${src.dir}">
                    <and>
                        <filename name="**/test/*"/>
                        <or>
                            <test-selector/>
                            <selector refid="src-extra-set"/>
                        </or>
                    </and>
                </fileset>
                <zipfileset dir="${ofbiz.home.dir}" prefix="META-INF" includes="NOTICE,LICENSE"/>
                <test-elements/>
            </jar>
        </sequential>
    </macrodef>

    <!-- SCIPIO: NOTE: This macro is enhanced to supported nested implicit iterate-subant-args for the subant call -->
    <macrodef name="iterate">
        <attribute name="filelist" default="subdirs"/>
        <attribute name="target" default=""/>
        <attribute name="inheritall" default="false"/>
        <element name="iterate-subant-args" optional="yes" implicit="yes"/>
        <sequential>
            <subant target="@{target}" inheritall="@{inheritall}">
                <propertyset refid="ofbiz.subbuild.props.pass"/>
                <!-- SCIPIO: new 2017-02-01 -->
                <iterate-subant-args />
                <filelist refid="@{filelist}"/>
            </subant>
        </sequential>
    </macrodef>

    <presetdef name="externalsubant">
        <subant inheritall="false">
            <!-- SCIPIO: defined in build.xml
            <propertyset>
                <propertyref name="ofbiz.home.dir"/>
            </propertyset>-->
            <propertyset refid="ofbiz.subbuild.props.pass"/>
        </subant>
    </presetdef>

    <!-- ================================================================== -->
    <!-- SCIPIO: New common macros/presets -->
    <!-- ================================================================== -->

    <!-- SCIPIO: Build invoker with automatic dependency resolution.
        Fetches the component.build.dependencies filelist from each component within a given component directory
        and resolves the resulting dependency graph to determine a build order, mirroring the same
        algorithm now used for the scipio-component.xml/scipio-theme.xml/ofbiz-component.xml depends-on functionality (new 2017-01-17).
        See also the "get-component-build-deps" target (called by this macro). -->
    <macrodef name="dep-subant">
        <attribute name="dir" default=""/>
        <attribute name="target" default=""/>
        <attribute name="auto-build-tools" default="true"/>
        <sequential>
            <!-- Old stock code (arbitrary build order)
            <externalsubant target="@{target}">
              <fileset dir="@{dir}" casesensitive="no">
                <exclude name="disabled/**"/>
                <include name="*/build.xml"/>
              </fileset>
            </externalsubant>-->

            <if>
                <equals arg1="@{target}" arg2="" trim="true"/>
                <then>
                    <var name="depres.displaytarget" value="build"/>
                </then>
                <else>
                    <var name="depres.displaytarget" value="@{target}"/>
                </else>
            </if>

            <if>
                <equals arg1="@{auto-build-tools}" arg2="true"/>
                <then>
                    <var name="scipio.buildtools.present" unset="true"/>
                    <available file="${ofbiz.home.dir}/tools/misc/scipio-build-tools.jar" property="scipio.buildtools.present" />
                    <if>
                        <not><isset property="scipio.buildtools.present"/></not>
                        <then>
                            <echo message="scipio-build-tools.jar not found (required for sub-builds) - triggering build..."/>
                            <antcall target="build-scipio-build-tools"/>
                        </then>
                    </if>
                </then>
            </if>

            <var name="depres.depgraph" unset="true"/>
            <var name="depres.depgraph" value="" />
            <var name="depres.topdirname" unset="true"/>
            <basename file="@{dir}" property="depres.topdirname"/>

            <echo message=""/>
            <echo message="${depres.topdirname}: Starting automatic build dependency resolution for ${depres.displaytarget} task..." />

            <!-- Build depgraph: read the component.build.dependencies values out of each build files and append to depgraph -->
            <for param="depres.buildfile">
                <path>
                    <fileset dir="@{dir}" casesensitive="no">
                        <exclude name="disabled/**"/>
                        <include name="*/build.xml"/>
                    </fileset>
                </path>
                <sequential>
                    <var name="component.build.dependencies.prop" unset="true"/>
                    <var name="depres.buildfile.dir" unset="true"/>
                    <var name="depres.buildfile.name" unset="true"/>
                    <var name="depres.buildfile.relative" unset="true"/>

                    <dirname file="@{depres.buildfile}" property="depres.buildfile.dir"/>
                    <basename file="@{depres.buildfile}" property="depres.buildfile.name"/>
                    <property name="depres.buildfile.relative" value="@{depres.buildfile}" relative="true" />

                    <!-- Special call using ant-contrib to read dependency list stored in external build file -->
                    <var name="component.build.dependencies.prop" unset="true" />
                    <antcallback dir="${depres.buildfile.dir}" target="get-component-build-deps" antfile="${depres.buildfile.name}"
                        inheritall="false" return="component.build.dependencies.prop">
                        <!--<property name="basedir" value="${depres.buildfile.dir}"/>-->
                        <property name="component.build.dependencies.sep" value=","/>
                        <propertyset refid="ofbiz.subbuild.props.pass"/>
                    </antcallback>

                    <if>
                        <and>
                            <isset property="component.build.dependencies.prop" />
                            <not><equals arg1="${component.build.dependencies.prop}" arg2="" trim="true"/></not>
                        </and>
                        <then>
                            <var name="depres.depgraph" value="${depres.depgraph}#${depres.buildfile.relative}=${component.build.dependencies.prop}" />
                        </then>
                        <else>
                            <var name="depres.depgraph" value="${depres.depgraph}#${depres.buildfile.relative}" />
                        </else>
                    </if>
                </sequential>
            </for>

            <if>
                <or>
                    <not><isset property="depres.depgraph"/></not>
                    <equals arg1="${depres.depgraph}" arg2="" trim="true"/>
                </or>
                <then>
                    <echo message="${depres.topdirname}: No builds to invoke." />
                </then>
                <else>
                    <echo level="verbose" message="Dependency graph for ${depres.topdirname}:${line.separator}${depres.depgraph}" />

                    <!-- Invoke automatic dependency resolution algorithm -->
                    <var name="depres.builds" unset="true"/>
                        <var name="depres.stderr" unset="true"/>
                    <var name="depres.result" unset="true"/>
                    <java classname="com.ilscipio.scipio.ce.build.util.DependencyGraph" fork="true"
                        outputproperty="depres.builds" errorproperty="depres.stderr" resultproperty="depres.result" failonerror="false">
                        <arg value="-f=resolve-deps-dfs"/>
                        <!-- function to run -->
                        <!-- NOTE: using comma as file separator because fileset "includes" attributes uses this -->
                        <arg value="-do=,"/>
                        <!-- output delimiter -->
                        <arg value="-dn=#"/>
                        <!-- graph entry delimiter -->
                        <arg value="-de=="/>
                        <!-- graph element=deps pairs delimiter -->
                        <arg value="-dd=,"/>
                        <!-- graph deps delimiter -->
                        <arg value="-s=false"/>
                        <!-- strict mode -->
                        <arg value="-e=false"/>
                        <!-- use empty values -->
                        <arg value="-g=${depres.depgraph}"/>
                        <classpath>
                            <path location="${ofbiz.home.dir}/tools/misc/scipio-build-tools.jar"/>
                            <!-- FIXME: non-ideal location for this class -->
                        </classpath>
                    </java>
                    <if>
                        <not><equals arg1="${depres.result}" arg2="0"/></not>
                        <then>
                            <fail message="Error automatically resolving build dependencies for ${depres.topdirname} components:${line.separator}${line.separator}
${depres.stderr}${line.separator}${line.separator}
Please verify the ${depres.topdirname} component build.xml files
for possible errors in the component.build.dependencies build file references."/>
                        </then>
                    </if>
                    <echo message="${depres.topdirname}: Auto-determined build order from dependencies (component.build.dependencies):" />
                    <echo message="${depres.builds}" />

                    <!-- Sanity check -->
                    <if>
                        <or>
                            <not><isset property="depres.builds"/></not>
                            <equals arg1="${depres.builds}" arg2="" trim="true"/>
                        </or>
                        <then>
                            <fail message="Error automatically resolving build dependencies for ${depres.topdirname} components:${line.separator}${line.separator}
${depres.stderr}${line.separator}${line.separator}
Unexpectedly received empty list of builds to iterate."/>
                        </then>
                    </if>

                    <!-- Invoke build files with requested target, in the resolved order (after re-expand comma-separated deresbuilds names to file list) -->
                    <externalsubant target="@{target}">
                        <filelist dir="${ofbiz.home.dir}" files="${depres.builds}"/>
                    </externalsubant>
                </else>
            </if>
        </sequential>
    </macrodef>

    <!-- SCIPIO: Reads and partly validates the component's component.build.dependencies and returns them
        to caller (dep-subant) for automatic dependency resolution.
        A component build.xml file may define a build list of dependencies on other components, using the following format:
          <filelist dir="." id="component.build.dependencies"> <!- NOTE: MUST be filelist, not fileset ->
            <file name="../other1/build.xml" /> <!- explicit build file form: the referenced component build file must exist ->
            <file name="../other2" /> <!- abstracted component dependency: component may or may not have a build ->
          </filelist>
        The component.build.dependencies are file paths which must specify either a component directory
        or the project build file within it - you MUST use filelist and NOT fileset to express these.
        The second form requires the dependency to have a build, while the first is more abstracted
        and will work even if the component has no build project or it is removed (e.g., themes).
        Currently (2017-01-18), only dependencies on components within the same component directory (addons, hot-deploy, themes)
        have any tangible effect on dependency resolution; reordering is within given component directory only.
        Dependencies on components from other, lower-layer components directories which load first are
        allowed to appear and may be verified, but will have no functional effect.
        NOTE: These build-time dependencies are managed independently from the load-time
            dependencies (component loader), and are designated using filesystem paths as opposed
            to component names. However, it is recommended to keep the same order for
            build-time and load-time dependencies as much as possible.
            Currently (2017-01-18), Scipio supports this same directory-focused automatic
            dependency resolution for both build-time and load-time resolution; see the description
            for the "depends-on" xml element in the following file for the load-time mechanism:
              component://base/dtd/ofbiz-component.xsd
        FIXME?: get-component-build-deps must currently be defined in macros.xml due to theme patterns... -->
    <macrodef name="read-component-build-deps">
        <!--
            NOTE: Caller must specify:
            * component.build.dependencies.sep (context-dependent)
            * ofbiz.home.dir
        -->
        <sequential>
            <if>
                <isreference refid="component.build.dependencies"/>
                <then>
                    <var name="component.build.dependencies.prop" unset="true" />
                    <pathconvert property="component.build.dependencies.prop" refid="component.build.dependencies"
                        pathsep="${component.build.dependencies.sep}" setonempty="true">
                        <!-- DEV NOTE: see https://ant.apache.org/manual/Tasks/script.html for language details -->
                        <scriptmapper language="javascript" classpathref="lib.ant.js.class.path"><![CDATA[
                            var proj = self.getProject();

                            var doFail = function(msg) {
                                var failTask = proj.createTask("fail");
                                failTask.setMessage("Project " + proj.getName() + ": component.build.dependencies: " + msg);
                                failTask.perform();
                            };
                            var echoMsg = function(msg) {
                                var echoTask = proj.createTask("echo");
                                echoTask.setMessage("Project " + proj.getName() + ": component.build.dependencies: " + msg);
                                echoTask.perform();
                            };
                            var stringEndsWith = function(str, suffix) { // old JS support
                                return str.indexOf(suffix, str.length - str.length) !== -1;
                            };

                            // NOTE: here source is the full path to file
                            var sourceFile = new java.io.File(source);
                            if (!sourceFile.exists()) {
                                doFail("missing or invalid build dependency: path does not exist: " + source);
                            } else {
                                var isDepAlreadyHandled = function(targetDep) {
                                    // SPECIAL: 2017-01-18: if the dependency is from a different top component directory
                                    // than the current one, and it is from one already loaded, then discard the dependency
                                    // because it is for all practical purposes already built.
                                    // (we currently do not support global dependency ordering)
                                    var homeDirPath = java.nio.file.Paths.get(proj.getProperty("ofbiz.home.dir"), []);
                                    var getCompDir = function(path) {
                                        var pathPath = java.nio.file.Paths.get(path, []);
                                        return homeDirPath.relativize(pathPath).getName(0);
                                    }

                                    var projCompDir = getCompDir(proj.getProperty("ant.file")); // NOTE: not basedir
                                    var depCompDir = getCompDir(targetDep);
                                    if (depCompDir.compareTo(projCompDir) !== 0) {
                                        var compDirsProp = proj.getProperty("ofbiz.component.dirs");
                                        if (compDirsProp) {
                                            var compDirs = compDirsProp.split(",");
                                            for(var i=0; i < compDirs.length; i++) {
                                                var compDirPath = java.nio.file.Paths.get(compDirs[i], []);
                                                if (projCompDir.compareTo(compDirPath) === 0) {
                                                    echoMsg("WARN: build dependency refers to a higher-layer component directory; build may fail: " + targetDep);
                                                    return false;
                                                } else if (depCompDir.compareTo(compDirPath) === 0) {
                                                    ; //echoMsg("build dependency ignored; already handled: " + targetDep);
                                                    return true;
                                                }
                                            }
                                        }
                                    }
                                    return false;
                                };

                                var validateCommitDep = function(targetDep) {
                                    self.addMappedName(targetDep);
                                };

                                if (stringEndsWith(source.toLowerCase(), ".xml") && sourceFile.isFile()) {
                                    // If explicit build file referenced as dependency, interpret as explicit request and don't modify.
                                    // In other words, the component must have a build file, otherwise dependency will fail.
                                    if (!isDepAlreadyHandled(source, sourceFile)) {
                                        validateCommitDep(source, sourceFile);
                                    }
                                } else if (sourceFile.isDirectory()) {
                                    if (!isDepAlreadyHandled(source, sourceFile)) {
                                        // Here, the reference is to the directory of the component, and we only
                                        // check the dependency if the component has in fact a build file.
                                        // If the component has no build file, we can ignore the build dependency,
                                        // though the component itself must exist.
                                        var dirBuildFile = new java.io.File(sourceFile, "build.xml");
                                        if (dirBuildFile.exists() && dirBuildFile.isFile()) {
                                            validateCommitDep(dirBuildFile.getPath(), dirBuildFile);
                                        } else {
                                            ; //echoMsg("build dependency ignored; has no build file: " + sourceFile.getName());
                                        }
                                    }
                                } else {
                                    doFail("missing or invalid build dependency: invalid path (must be a build.xml file or component directory): " + source);
                                }
                            }
                        ]]></scriptmapper>
                        <!-- strip the full path, make relative to ofbiz base folder (most versatile)
                            NOTE: this runs after the mapper (even if you list the map before the mapper) -->
                        <!--<globmapper from="${ofbiz.home.dir}/*" to="*" />-->
                        <map from="${ofbiz.home.dir}/" to="" />
                        <!-- this one may be safer for Windows -->

                        <!--<compositemapper>
                            <regexpmapper from="^(.*)/?(?&lt;!\.xml)$" to="\1/build.xml" casesensitive="false" />
                            <regexpmapper from="^(.*\.xml)$" to="\1" casesensitive="false" />
                        </compositemapper>-->
                        <!--<filtermapper>
                          <scriptfilter language="beanshell">
                            self.setToken(self.getToken().toUpperCase());
                          </scriptfilter>
                        </filtermapper>-->
                    </pathconvert>
                    <!-- will be read using ant-contrib's antcallback/antfetch
                    <echo message="${component.build.dependencies.prop}"/>-->
                </then>
                <else>
                    <property name="component.build.dependencies.prop" value="" />
                </else>
            </if>
        </sequential>
    </macrodef>

    <!-- get-component-build-deps: basic form expected by dep-subant
        NOTE: We don't really want to define this here... but without it,
            a theme not using the new commontheme.xml would need to copy this into its build file
    <target name="get-component-build-deps"
        description="Reads this component's build dependencies from component.build.dependencies filelist (if any) and prepares for automatic dependency resolution (SCIPIO)">
        <read-component-build-deps/>
    </target>
    -->

    <!-- Removes all lib (jar) files from a component lib folder and its subfolders, without removing any subfolders,
        with the given exceptions as exclude-pattern -->
    <macrodef name="remove-lib-files">
        <attribute name="dir"/>
        <attribute name="excludes-refid" default=""/>
        <attribute name="include" default="**/*.jar"/>
        <!-- this takes a <selector> of <filename> elements -->
        <element name="patterns" optional="true"/>
        <sequential>
            <!-- FIXME: ugly duplication, ant kludge (but at least kind to caller) -->
            <if>
                <isreference refid="@{excludes-refid}"/>
                <then>
                    <!-- never delete empty dirs; never delete .git, .svn, .gitignore, ... -->
                    <delete includeemptydirs="false" defaultexcludes="true">
                        <fileset dir="@{dir}">
                            <include name="@{include}"/>
                            <patterns />
                            <!-- instead of taking excludes, take an include list and negate it
                            <patternset refid="@{excludes-refid}"/>-->
                            <not><selector refid="@{excludes-refid}"/></not>
                        </fileset>
                    </delete>
                </then>
                <else>
                    <delete includeemptydirs="false" defaultexcludes="true">
                        <fileset dir="@{dir}">
                            <include name="@{include}"/>
                            <patterns />
                        </fileset>
                    </delete>
                </else>
            </if>
        </sequential>
    </macrodef>

    <!-- ================================================================== -->
    <!-- SCIPIO: tasks common to main build and standalone component builds -->
    <!-- ================================================================== -->

    <target name="groovy-init" depends="ivy-init"><!-- SCIPIO: 2018-03-20: new -->
        <if>
            <resourcecount when="equal" count="0">
                <fileset file="${ofbiz.home.dir}/framework/base/lib/groovy-*.jar"/>
            </resourcecount>
            <then>
                <antcall target="download-groovy"/>
            </then>
        </if>
        <taskdef name="groovyc" classname="org.codehaus.groovy.ant.Groovyc" classpathref="groovy.class.path"/>
    </target>

    <target name="download-groovy" depends="ivy-init"><!-- SCIPIO: 2018-03-20: new -->
        <ivy:retrieve file="${lib.comp.base.ivyfile}" pattern="framework/base/lib/[artifact]-[revision].[ext]" conf="groovy"/>
    </target>

    <target name="ivy-init"><!-- SCIPIO: 2017-01-30: new -->
        <!-- DEV NOTE: WARN: for performance reasons, this code has to be COPIED into
            common.xml#lib-update; keep in sync -->
        <taskdef uri="antlib:org.apache.ivy.ant" resource="org/apache/ivy/ant/antlib.xml" classpathref="lib.ivy.class.path"/>
    </target>

    <target name="get-scipio-java-version" unless="scipio.java.version.major">
        <java classname="com.ilscipio.scipio.ce.build.util.BuildUtil" outputproperty="scipio.java.version.major">
            <arg value="-majorver"/>
            <classpath>
                <path location="${ofbiz.home.dir}/tools/misc/scipio-build-tools.jar"/>
            </classpath>
        </java>
        <echo level="info">scipio.java.version.major: ${scipio.java.version.major}</echo>
    </target>

    <macrodef name="get-ivy-versioned-confs">
        <attribute name="conf"/>
        <attribute name="ivyfile"/>
        <attribute name="outputproperty"/>
        <sequential>
            <var name="@{outputproperty}" unset="true"/>
            <java classname="com.ilscipio.scipio.ce.build.util.BuildUtil" outputproperty="@{outputproperty}">
                <arg value="-ivyverconf"/>
                <arg value="@{conf}"/>
                <arg value="@{ivyfile}"/>
                <classpath>
                    <path location="${ofbiz.home.dir}/tools/misc/scipio-build-tools.jar"/>
                </classpath>
            </java>
        </sequential>
    </macrodef>

</project>