【发布时间】:2010-11-30 21:06:02
【问题描述】:
我想从 Ant 构建文件中为目录中的每个文件执行一个命令。
我正在寻找一个独立于平台的解决方案。
我该怎么做?
当然,我可以用某种脚本语言编写脚本, 但这会给项目增加更多的依赖。
【问题讨论】:
标签: file-io ant build-process build-automation
我想从 Ant 构建文件中为目录中的每个文件执行一个命令。
我正在寻找一个独立于平台的解决方案。
我该怎么做?
当然,我可以用某种脚本语言编写脚本, 但这会给项目增加更多的依赖。
【问题讨论】:
标签: file-io ant build-process build-automation
将<foreach> 与嵌套的<FileSet> 一起使用
Foreach 需要 ant-contrib。
最近 ant-contrib 的更新示例:
<target name="foo">
<foreach target="bar" param="theFile">
<fileset dir="${server.src}" casesensitive="yes">
<include name="**/*.java"/>
<exclude name="**/*Test*"/>
</fileset>
</foreach>
</target>
<target name="bar">
<echo message="${theFile}"/>
</target>
这将使用 ${theFile} 调用目标“bar”,从而生成当前文件。
【讨论】:
ant-contrib 是邪恶的;编写一个自定义的 ant 任务。
ant-contrib 是邪恶的,因为它试图将 ant 从声明式风格转换为命令式风格。但是 xml 是一种垃圾编程语言。
相比之下,自定义 ant 任务允许您使用真正的 IDE 用真正的语言 (Java) 编写,您可以在其中编写单元测试以确保您拥有所需的行为,然后在您的构建关于你想要的行为的脚本。
只有当你关心编写可维护的 ant 脚本时,这种咆哮才有意义。如果您不关心可维护性,请尽一切努力。 :)
jtf
【讨论】:
Tassilo Horn(original target is here)建议了一种没有 ant-contrib 的方法
基本上,因为没有 <java> 的扩展名(还没有?)以相同的方式
<apply> 扩展了 <exec>,他建议使用
这里有一些例子:
<apply executable="java">
<arg value="-cp"/>
<arg pathref="classpath"/>
<arg value="-f"/>
<srcfile/>
<arg line="-o ${output.dir}"/>
<fileset dir="${input.dir}" includes="*.txt"/>
</apply>
【讨论】:
使用<apply> task。
它为每个文件执行一次命令。通过文件集或任何其他资源指定文件。
也可以只运行一次命令,一次性将所有文件作为参数附加。使用parallel属性切换行为。
对不起,迟到了一年。
【讨论】:
这是使用 javascript 和 ant scriptdef 任务执行此操作的方法,由于 scriptdef 是核心 ant 任务,因此您不需要 ant-contrib 来运行此代码。
<scriptdef name="bzip2-files" language="javascript">
<element name="fileset" type="fileset"/>
<![CDATA[
importClass(java.io.File);
filesets = elements.get("fileset");
for (i = 0; i < filesets.size(); ++i) {
fileset = filesets.get(i);
scanner = fileset.getDirectoryScanner(project);
scanner.scan();
files = scanner.getIncludedFiles();
for( j=0; j < files.length; j++) {
var basedir = fileset.getDir(project);
var filename = files[j];
var src = new File(basedir, filename);
var dest= new File(basedir, filename + ".bz2");
bzip2 = self.project.createTask("bzip2");
bzip2.setSrc( src);
bzip2.setDestfile(dest );
bzip2.execute();
}
}
]]>
</scriptdef>
<bzip2-files>
<fileset id="test" dir="upstream/classpath/jars/development">
<include name="**/*.jar" />
</fileset>
</bzip2-files>
【讨论】:
project 在上面的示例中被引用,但没有提供先前的定义。最好能说明或澄清这一点。编辑:n/m,发现project 是访问当前项目的预定义变量。我怀疑过,但不确定。
您可以使用 ant-contrib 任务“for”来迭代由任何分隔符分隔的文件列表,默认分隔符是“,”。
以下是显示此内容的示例文件:
<project name="modify-files" default="main" basedir=".">
<taskdef resource="net/sf/antcontrib/antlib.xml"/>
<target name="main">
<for list="FileA,FileB,FileC,FileD,FileE" param="file">
<sequential>
<echo>Updating file: @{file}</echo>
<!-- Do something with file here -->
</sequential>
</for>
</target>
</project>
【讨论】:
我知道这篇文章已经很老了,但是现在已经过了一段时间和 ant 版本,有一种方法可以用基本的 ant 功能做到这一点,我想我应该分享它。
这是通过调用嵌套任务的递归宏定义来完成的(甚至可以调用其他宏)。唯一的约定是使用固定的变量名(此处为元素)。
<project name="iteration-test" default="execute" xmlns="antlib:org.apache.tools.ant" xmlns:if="ant:if" xmlns:unless="ant:unless">
<macrodef name="iterate">
<attribute name="list" />
<element name="call" implicit="yes" />
<sequential>
<local name="element" />
<local name="tail" />
<local name="hasMoreElements" />
<!-- unless to not get a error on empty lists -->
<loadresource property="element" unless:blank="@{list}" >
<concat>@{list}</concat>
<filterchain>
<replaceregex pattern="([^;]*).*" replace="\1" />
</filterchain>
</loadresource>
<!-- call the tasks that handle the element -->
<call />
<!-- recursion -->
<condition property="hasMoreElements">
<contains string="@{list}" substring=";" />
</condition>
<loadresource property="tail" if:true="${hasMoreElements}">
<concat>@{list}</concat>
<filterchain>
<replaceregex pattern="[^;]*;(.*)" replace="\1" />
</filterchain>
</loadresource>
<iterate list="${tail}" if:true="${hasMoreElements}">
<call />
</iterate>
</sequential>
</macrodef>
<target name="execute">
<fileset id="artifacts.fs" dir="build/lib">
<include name="*.jar" />
<include name="*.war" />
</fileset>
<pathconvert refid="artifacts.fs" property="artifacts.str" />
<echo message="$${artifacts.str}: ${artifacts.str}" />
<!-- unless is required for empty lists to not call the enclosed tasks -->
<iterate list="${artifacts.str}" unless:blank="${artifacts.str}">
<echo message="I see:" />
<echo message="${element}" />
</iterate>
<!-- local variable is now empty -->
<echo message="${element}" />
</target>
</project>
需要的关键功能:
我没有设法使分隔符变量,但这可能不是主要的缺点。
【讨论】:
按照 blak3r 的建议做,并像这样定义你的目标类路径
<taskdef resource="net/sf/antcontrib/antlib.xml">
<classpath>
<fileset dir="lib">
<include name="**/*.jar"/>
</fileset>
</classpath>
</taskdef>
lib 是你存储 jar 的地方
【讨论】: