我已经尽力创建一个自定义的while循环,但最终失败了。 有没有人在NANT中成功创建自定义的while循环?
我已经尽力创建一个自定义的while循环,但最终失败了。 有没有人在NANT中成功创建自定义的while循环?
<target name="sample">
<property name="foo.value" value="0"/>
<while property="foo.value" equals="0">
<do>
<echo message="${foo.value}"/>
<property name="foo.value" value="${int::parse(foo.value) + 1}"/>
</do>
</while>
</target>
<script language="C#" prefix="directory">
<code>
<![CDATA[
[TaskName("while")]
public class WhileTask : TaskContainer
{
private TaskContainer _doStuff;
private string _propertyName;
private string _equals;
private string _notEquals;
[BuildElement("do")]
public TaskContainer StuffToDo
{
get
{
return this._doStuff;
}
set
{
this._doStuff = value;
}
}
[TaskAttribute("property")]
public string PropertyName
{
get
{
return this._propertyName;
}
set
{
this._propertyName = value;
}
}
[TaskAttribute("equals")]
public string Equals
{
get
{
return this._equals;
}
set
{
this._equals = value;
}
}
[TaskAttribute("notequals")]
public string NotEquals
{
get
{
return this._notEquals;
}
set
{
this._notEquals = value;
}
}
protected override void ExecuteTask()
{
while (this.IsTrue())
{
this._doStuff.Execute();
}
}
private bool IsTrue()
{
if (!string.IsNullOrEmpty(this.Equals))
{
return this.Properties[this.PropertyName] == this.Equals;
}
return this.Properties[this.PropertyName] != this.NotEquals;
}
}
]]>
</code>
</script>
看着NAnt当前可用任务列表,似乎不再支持while(http://nant.sourceforge.net/release/latest/help/tasks/)
因此,我认为实现自定义while循环最简单和最有效的方法是递归。
例如,像这样:
<property name="count" value="120" />
<target name="wait">
<if test="${int::parse(count) > 0}" >
<property name="count" value="${int::parse(count) - 1}" />
<call target="wait"/>
</if>
</target>
Regards,
Marek
<?xml version="1.0"?>
<project name="whiletask" xmlns="http://tempuri.org/nant-donotuse.xsd">
<script language="C#" prefix="loop">
<code>
<![CDATA[
/// <summary>
/// A while loop task. Will continuelly execute the task while the <c>test</c> is <c>empty</c>
/// or evalutes to <c>true</c>.
/// </summary>
[TaskName("while")]
public class WhileTask : TaskContainer
{
private string _test;
private TaskContainer _childTasks;
/// <summary>
/// The expression to test each iteration. If empty, then always evalutes to true (i.e. infinite loop.)
/// </summary>
[TaskAttribute("test", ExpandProperties = false)]
public string Test
{
get { return _test; }
set { _test = NAnt.Core.Util.StringUtils.ConvertEmptyToNull(value); }
}
/// <summary>
/// Superficial to ensure the XML schema is rendered correctly for this task. It will get executed
/// if tasks exist within it.
/// </summary>
[BuildElement("do")]
public TaskContainer ChildTasks
{
get { return _childTasks; }
set { _childTasks = value; }
}
/// <summary>
/// Executes the while loop while the <c>test</c> evalutes to true or <c>test</c> is empty.
/// </summary>
protected override void ExecuteTask()
{
while (this.Test == null
|| bool.Parse(Project.ExpandProperties(this.Test, this.Location)))
{
if (this._childTasks != null)
{
this._childTasks.Execute();
}
else
{
base.ExecuteTask();
}
}
}
}
]]>
</code>
</script>
<property name="i" value="0" />
<while test="${int::parse(i) <= 10}">
<echo message="${i}" />
<property name="i" value="${int::parse(i)+1}" />
</while>
</project>
有很多方法可以做到这一点。我写了类似于Cao的东西,当属性为true时触发,所以条件可以像你喜欢的那样复杂,并且如果它是动态的,每次循环都会计算该值,这在调用函数(例如检查文件是否存在)时非常方便。我还添加了简单的break和continue控件。它也可以作为一个无属性的无限循环运行,当您想要许多条件退出时(在这种情况下使用'if'与break/continue或-在我的情况下-我想运行一个任务直到它出现异常,然后使用failonerror或trycatch块进行处理。
这是一些Nant脚本的示例,展示了两种从10倒数的方法:
<property name="greaterthanzero" value="${int::parse(count) > 0}" dynamic="true"/>
<property name="count" value="10" />
<while propertytrue="greaterthanzero" >
<echo>CountDown = ${count}</echo>
<property name="count" value="${int::parse(count) - 1}" />
</while>
<property name="count" value="10" />
<while>
<if test="${int::parse(count) > 0}" >
<echo>CountDown = ${count}</echo>
<property name="count" value="${int::parse(count) - 1}" />
<continue/>
</if>
<break/>
</while>
这里有一个与实际应用相关的例子,我使用它来等待锁文件被删除:
<property name="count" value="0" />
<property name="lockfileexists" value="${file::exists(lockfile)}" dynamic="true"/>
<while propertytrue="lockfileexists" >
<sleep seconds="1" />
<property name="count" value="${int::parse(count) + 1}" />
<if test="${count == '15'}" >
<echo>Timed out after 15 seconds</echo>
<break/>
</if>
</while>
这是任务的代码:
<script language="C#" prefix="loops">
<code>
<![CDATA[
public class LoopBreakException : Exception {}
public class LoopContinueException : Exception {}
[TaskName("break")]
public class BreakTask : Task
{
protected override void ExecuteTask()
{
throw new LoopBreakException();
}
}
[TaskName("continue")]
public class ContinueTask : Task
{
protected override void ExecuteTask()
{
throw new LoopContinueException();
}
}
[TaskName("while")]
public class WhileTask : TaskContainer
{
[TaskAttribute("propertytrue")]
public string PropertyName { get; set; }
protected bool CheckCondition()
{
if (!string.IsNullOrEmpty(PropertyName))
{
try
{
return bool.Parse(Properties[PropertyName]);
}
catch (Exception ex)
{
throw new BuildException(string.Format("While Property '{0}' not found", PropertyName), Location);
}
}
//for infinite loops
return true;
}
protected override void ExecuteTask()
{
while (CheckCondition())
{
try
{
ExecuteChildTasks();
}
catch (LoopContinueException)
{
continue;
}
catch (LoopBreakException)
{
break;
}
}
}
}
]]>
</code>
以下是一种在nant中编写WHILE循环的方式,无需自定义任务或script
元素,并利用foreach
循环的failonerror="false"
属性。
<property name="n" value="10000" /><!-- this would be inefficient if "n" is very large -->
<property name="i" value="0" />
<foreach item="String" in="${string::pad-right(' ', int::parse(n), ',')}" delim="," property="val" failonerror="false" >
<if test="${int::parse(i) > 3}"><!-- put our exit condition here -->
<fail message="condition met, exit loop early" />
</if>
<echo message="i: ${i}" />
<property name="i" value="${int::parse(i) + 1}" />
</foreach>
failonerror="false"
,fail
调用不会终止脚本: [echo] i: 0
[echo] i: 1
[echo] i: 2
[echo] i: 3
[foreach] myscript.nant(24,18):
[foreach] condition met, exit loop early
BUILD SUCCEEDED - 1 non-fatal error(s), 0 warning(s)
<property name="n" value="5" />
<property name="i" value="0" />
<foreach item="String" in="${string::pad-right(' ', int::parse(n), ',')}" delim="," property="val" >
<echo message="i: ${i}" />
<property name="i" value="${int::parse(i) + 1}" /> <!-- increment "i" -->
</foreach>
FOR循环的输出如下:
[echo] i: 0
[echo] i: 1
[echo] i: 2
[echo] i: 3
[echo] i: 4
BUILD SUCCEEDED
如果没有其他信息,可以在 这里 找到有关创建自定义 NAnt 任务的教程。
文章中提到的一个好处是,作者建议两种方法来 调试 您的自定义任务:
将程序集(及pdb文件)复制到NAnt bin目录中。在Visual Studio中打开包含您任务源代码的解决方案。设置断点。进入项目属性并打开调试页面。将Debug Mode更改为Program,将Start Application更改为指向NAnt可执行文件的路径(例如C:\ Program Files \ NAnt \ bin \ NAnt.exe)。然后设置工作目录和/或命令行参数,以便NAnt会使用您的构建文件。单击运行即可开始。
在希望中断的代码行之前放置System.Diagnostics.Debbugger.Break();重新编译项目并将程序集(及pdb文件)复制到NAnt bin目录中。运行NAnt脚本时,应该会弹出一个框让你选择一个调试器。
这里有另一个教程在这里。
或者,你能用foreach的术语来表达你的问题吗?
我自己创建了自定义任务。但是在使用NANT中的嵌套循环时似乎存在一些问题。
基本上,我正在尝试使用嵌套循环。一个while循环位于foreach内部,或者一个foreach位于另一个foreach内部。但在这两种情况下,循环都会执行当前目标和调用当前目标的目标,而不是第二个循环内的语句。
谢谢
Sarathy