使用 NAnt 任务进行文件同步






4.37/5 (9投票s)
提供了可在 NAnt 或控制台下运行的文件同步代码,用于在本地和本地/远程目标目录树之间进行文件同步。
图示:文件同步任务的图形表示。
引言
许多开发人员,包括我自己,拥有大量需要管理的文件,手动处理这些文件至少是耗时的。市面上有许多工具可以使用,有些是免费的,有些则需要付费。它们满足不同的需求。我将在这里介绍其中一个,它源自我们开发的一个构建系统,该系统是为了满足我们在不断发展(开发!)的环境中保持灵活性和可扩展性的需求而开发的。在时间和预算有限的情况下,从头开始开发它并不是首选。经过一番搜索,我们找到了以下可以集成到我们 Said 系统中的开源工具:
- 构建程序 NAnt(0.85 Release Candidate 1),它使用 XML(脚本)来控制构建过程。它允许您通过树状结构可视化任务(请参阅上图)。
- Uwe Keim 的 上传修改文件到 FTP 服务器的应用程序,它使用本地数据库来跟踪目标文件的更新状态。
当使用 _STANDALONE
编译标志进行编译时,它提供了一个独立的控制台入口点。同步过程使用与 Uwe Keim 提供的相同的配置文件进行控制;<fileSettings>
节点中添加的“versionDate
”属性是使用的版本控制,下面将进行讨论。代码中还可以看到一些附加的配置节点,但它们并未被当前功能单元使用。源包中包含了一个示例配置文件。一些读者,特别是那些知道如何运行 NAnt 的读者,在需要将其集成到他们的自动构建系统时,可能会觉得在 NAnt 下运行该应用程序更方便。
对于那些不知道如何操作 NAnt 并且发现学习 NAnt 的脚本编写规则很困难的人来说,考虑到他们可用的时间限制,有一些工具可以提供帮助。一个例子是 NAntPad。我们自然更喜欢我们自己的 :-),即 CryptoGateway 的 X-Script Generator。它曾被用于处理简单任务,如文件同步,也用于复杂任务,如 C++ 多层状态机生成,以及基于数据关系的网站框架生成,例如那些镜像 cryptogateway.com 的。简单来说,它可以作为一个基于语法的微程序可视化集成器,这是传统 UI 程序无法比拟的,尽管它看起来不像那些 fancy 程序。提供的代码是一个实际的示例程序集,它包含了对任务中元标签的足够完整的用法,可以扩展 NAnt 内置语法(我们称之为)由 X-script Generator 使用。后者感兴趣的用户应该查阅包含在二进制包中的开发者手册以获取更多详细信息。它可能对 Said generator 的用户有用,这些用户希望进行一些元编程来释放其全部潜力,或者那些仅仅喜欢玩自己的语言构造来获得乐趣,或者通过快速接受他们的构造而无需先写一篇文档让同行阅读,当它变得有点复杂时。
工作原理和代码使用方法
代码中有五个主要功能单元和三个层。这五个单元是:
- IO 组件 - 如果使用控制台构建,输入和输出参数在配置文件中指定,包中提供了一个示例。如果它在 NAnt 下运行,输入和输出参数在 NAnt 脚本文件中指定。
- 要执行的操作,在顶层
<fileSync>
节点的verb
属性中指定。有三种操作:- “
scan
”指令程序扫描目录树,从<group>
节点的<source>
子节点指定的文件夹开始。如果<group>
节点的<settings>
子节点的 “recurs
” 属性未设置或设置为true
,则扫描在目录树内递归进行。否则,仅扫描起始目录。 - “
listen
”指令程序监视文件<group>
集合中的文件更改,并在必要时执行相应的更新。它使用 .NET 类库提供的FileSystemWatcher
实现。当前程序集中此代码分支尚未经过充分测试,因为它未在我们当前的构建系统中使用。发现FileSystemWatcher
并不总是能正确报告文件更改。尚未投入实际精力去查找原因。我们邀请用户使用它并在适合他们需求时进行改进。例如,使用作业队列异步处理事件,因为当前版本在目标目录不在本地机器上时预计无法正常工作,并且两个最近文件更改事件触发之间的平均时间小于完成典型文件更新所需的平均时间。 - “
dbsync
”指令程序将源文件<group>
的记录插入数据库,而不对目标目录执行实际更新。如果用户知道更新已通过其他方式或工具完成,并且实际更新过程需要大量时间才能完成,则此功能可能很有用。
- “
- 数据库组件 - 数据库用于记录每个称为
<group>
的目标文件存储目录树中每个文件的最后修改时间。在每次扫描源目录树时,都会将每个源文件的最后修改日期与数据库记录进行比较。如果源文件较新,则会将其复制到目标目录。扫描前,每个对应特定文件<group>
的数据库记录都会被标记为“已签出”。扫描期间,在源目录树中找到的文件将被插入数据库(如果不存在)或标记为“已签入”。扫描完成后,程序将删除所有未“签入”的目标文件及其对应的记录。因此,为每个文件<group>
指定一个 *唯一名称* 非常重要。当前版本的程序 **无法处理重叠的文件组**,即一个文件组的根目录是另一个文件组的子目录。 - FTP 和文件系统组件 - 原始组件 由 Alex Kwok 编写,Uwe Keim 的代码版本在此进行了一些修改,以使其能与更多 FTP 服务器配合使用(此处不保证能与所有服务器配合使用)。不同 FTP 服务器之间的主要区别集中在以下几个方面:
- 响应代码 - 不同服务器在返回代码方面可能响应不同。
- 响应消息 - 某些服务器包含的响应(信息)行不以任何数字 FTP 响应代码开头。
- 目录列表格式 - 不同服务器具有不同的格式(UNIX、Windows 和两者的混合),必须通过实现其处理程序来正确解析。
该组件必须在机器对机器的时间尺度上工作,其中请求和响应是流水线式的,延迟很小或没有延迟。在这样的条件下,原始组件往往会失去同步。目标文件夹下的目录树(应预先存在)镜像源文件夹下“
folder
”属性指定的文件夹下的目录树。如果任何子目录树不存在,它们将被自动创建。因此,创建目录必须授权给接收目标文件的帐户,并且必须注意正确指定目标目录树。三个元素组合起来形成 FTP 服务器上的完整(本地)路径。它们是:<ftp>
节点下的<account>
节点的“baseFolder
”属性指定 FTP 帐户的根目录。<sink>
节点下的<ftp>
节点的“folder
”属性指定根目标目录,源“*folder*”目录树将在远程 FTP 帐户(或其他本地文件夹)下镜像在该根目标目录下方。它始终相对于“*baseFolder*”,因此,如果存在,则会忽略前导的“/”或“\”字符。- 以及源文件夹(目录树)下每个源文件的相对路径,该路径将在根目标目录中镜像,它们由文件扫描程序自动生成(镜像)。
- 版本机制 - 该应用程序的 NAnt 任务适配器使用
<sink>
节点的“version
”属性来开启/关闭版本控制;如果设置为空,则版本控制开启,其值用作版本。通过应用程序的控制台入口,配置文件中<settings>
节点的“incrementalBackup
”属性用于开启/关闭版本控制,<fileSettings>
节点的“versionDate
”属性用于设置版本值。当版本控制开启时,目标目录将包含版本信息。可以使用任何版本 **标识符**。在 NAnt 下运行时,可以从<sink>
节点的“versionPath
”属性的有效值列表中选择两种标记文件版本的方法:- “
subpath
”指令程序在目标根目录下方为每个版本创建一个子目录,该子目录镜像源目录树并将目标文件放在那里。因此,版本标识符应选择为与目标根目录下的任何子文件夹名称不同。在下面的示例构建脚本中,使用了当前时间戳,因为它被认为是最具信息量的。事实上,只要不与现有文件夹名称冲突,就可以使用任何标识符。 - “
append
”指令程序将版本字符串附加到目标根目录名称。在这种情况下,用户无需担心目录树中的现有文件夹名称,但目标根目录名称会被修改。
该应用程序的控制台版本始终将版本信息附加到目标(根)目录。
- “
- 还有一点,实际上有一个未完成且测试不充分的代码分支,用于执行差异分析和差量更新。如果有人有兴趣在一个开放项目中进一步开发它,请与我联系。
三个层是:
- C# 代码,上面已简要介绍。
- NAnt 使用的 NAnt 属性,用于识别其节点和属性。对 NAnt 不熟悉的用户应该访问其托管 网站 查找更多信息。
- X-Script Generator 令牌化属性,用于标记 X-script 中的“词法”和“上下文”令牌及其关系。如果读者有兴趣使用 X-Script Generator 并开发自己的“构建语言”版本,请阅读此处或 X-Script Generator 程序包内的开发者手册。文档在二进制包中。它也可以从提供的代码生成。用户手册 在线。
使用任务
当在纯 NAnt XML 脚本中使用时,该任务可以包含在 NAnt 的任何 target 节点下,前提是该任务程序集已使用内置的 <loadtasks>
任务预加载。主要的任务元素及其属性已在上面介绍,其他的要么不言自明,要么是 NAnt 内置的。如果在 _Samples_ 目录中的 X-script 项目文件中使用 X-Script Generator 加载它,每个属性都会有一个工具提示弹出窗口,其中包含该属性的描述。用户也可以阅读代码来查找这些描述。它们包含在每个任务元素(类)的“Description
”(元)属性中,该属性具有“LexicToken
”(元)属性(代码)属性(元)属性。这些任务类中的每一个都包含一个“LexicToken
”(元)属性。
以下 NAnt 脚本对应于文章开头图示的一个实例(该图实际上是从 X-Script Generator 的节点编辑面板复制的)
<?xml version="1.0" encoding="utf-8"?>
<project name="demo project"
default="File Upload" basedir="C:\Build"
output_script="C:\Build\scripts\file-sync.build">
<description>
Generated By: X-Script Generator 0.2.0.0
Project File: FileSync.nant.proj
Working Path: C:\Build\projects
Other Info: This is a sample project.
Generated At:
Tue, 25 Oct 2005 22:24:57 GMT
(Local Time: 2005-10-25 15:24:57Z)
</description>
<target name="File Upload">
<fileGroup name="File Group C"
id="6ec3a262-8768-4066-841a-9ad7cde926f8">
<settings dbType="MSSQL">
<dbConn server="10.0.0.12"
database="test-db"
user="user" pwd="password" />
</settings>
<source folder="C:\SourceDir" />
<sink versionPath="subpath">
<ftp folder="/RelativePath/DestinationDir">
<account domain="www.somewhere.net"
baseFolder="/" port="21"
user="ftp-user"
password="ftp-password" />
</ftp>
</sink>
</fileGroup>
<fileSync verb="scan" verbose="false">
<group name="File Group A">
<settings dbType="MSSQL">
<dbConn server="10.0.0.12"
database="test-db"
user="user"
pwd="password" />
</settings>
<source folder="C:\SourceDir" />
<sink version="2005-10-26 00:10:46"
versionPath="subpath">
<ftp folder="/RelativePath/DestinationDir">
<account domain="www.somewhere.net"
baseFolder="/"
port="21" user="ftp-user"
password="ftp-password" />
</ftp>
</sink>
<includes pattern=".txt" />
<excludes pattern=".exe" />
</group>
<group name="File Group B">
<settings dbType="ACCESS">
<dbConn database="test-db" />
</settings>
<source folder="C:\SourceDir" />
<sink versionPath="subpath">
<local folder="C:\LocalTargetDir" />
</sink>
<includes pattern=".jpg" />
<includes pattern=".gif" />
<excludes pattern=".png" />
</group>
<group name=""
refid="6ec3a262-8768-4066-841a-9ad7cde926f8" />
</fileSync>
</target>
<target name="File Download">
<downloader outdir="C:\FtpDownloadDir" verbose="false">
<source path="/RemoteItem1RelativePath">
<ftp folder="/RemoteItemFolder">
<account domain="www.somewhere.net"
baseFolder="/"
port="21" user="ftp-user"
password="ftp-password" />
</ftp>
</source>
<source path="/RemoteItem2RelativePath">
<ftp folder="/RemoteItemFolder">
<account domain="www.somewhere.net"
baseFolder="/"
port="21" user="ftp-user"
password="ftp-password" />
</ftp>
</source>
</downloader>
</target>
</project>
要在 X-Script Generator 下使用它,程序集必须被 预加载,以便它能够识别程序集中定义的语法扩展。
还有一个 fTP 文件下载器,如果包含在自动构建过程中,可能会很有用。它的当前版本相当简单。它适用于一次只下载少量项目的情况,并且在因意外事件中断下载时无法恢复任务。
包含内容
- 数据库和表创建脚本(名为“*fileinfo_table.sql*”和“*fileinfo_table_firebird.sql*”)位于主目录中,用于创建程序在一个用户选择的数据库上使用的单个表(和存储过程)。它从 Microsoft SQL Server 数据库和 Firebird 数据库(版本 1.5.2.4731)生成。对于其他数据库,可能需要进行一些小的修改。
- 位于主目录下的“*FileSync*”子目录中的任务的代码/二进制文件。
- 位于 _TokenAttrib_ 子目录下的 _CodeTokenizers.dll_ 的代码/二进制文件,它主要是 .NET 反射属性的定义,是 X-Script Generator 使用的程序集。它包含开发者手册,可以进行处理以生成 HTML 格式以供阅读,例如使用提供的 NAnt 内置脚本。
- 名为“*file-sync.build*”的示例 NAnt 模板项目文件,显示在上面,位于 _Samples_ 子目录中,并用虚构参数进行了初始化。
- 一个名为“*sample.syncjob*”的模板配置文件,可以根据应用程序进行修改,然后由应用程序的控制台版本读取。它位于 _Samples_ 子目录中。
- 名为“*FileSync.nant.proj*”的 X-Script Generator 项目文件,用于生成上述 NAnt 脚本。它也位于 _Samples_ 子目录中。
- 位于 _Build\nant-scripts_ 子目录中的程序集和开发者手册的 NAnt 构建文件。构建者应首先阅读“*Readme.txt*”文件。如果他/她更喜欢在 IDE 中构建,他/她可以始终将所有 C# 文件包含在一个引用 NAnt 发行版中包含的“*NAntCore.dll*”和本软件包中包含的 _CodeTokenizers.dll_ 程序集中的项目中,并引用标准的 .NET 库(包括 _System.Xml.dll_)。用于生成构建文件的 X-Script Generator 项目文件位于 _Build\XScriptProjs_ 子目录中。
修订历史
- 版本 1.0:2005 年 11 月 1 日。初始发布。
- 版本 1.1:2006 年 5 月 2 日。增加了对 Firebird 数据库的支持。它使用存储过程,因为 Firebird 数据库似乎不支持其存储过程之外的过程 SQL (PSQL)(MS Access 也不支持 PSQL,但我们使用了一种效率较低的方式来实现所需功能)。构建脚本也进行了一些小的更改。
待办事项
- 有兴趣的读者可能会发现,在提供的节点元素和 NAnt 内置元素(如
<fileset>
,它允许用户以更精细的粒度定义包含的文件集)以及任务当前的<includes>
和<excludes>
节点之间进行进一步集成是可能的。目前尚未尝试,因为我们的许多构建脚本依赖于当前版本。但这并不难做到。 - Oracle 数据库选项未经过测试,因为我们没有 Oracle 数据库。用户可能会发现将其扩展到包括 MySQL 等其他数据库很容易。有兴趣的程序员/用户可能会发现将其统一到一个 OLE DB 提供商 下很有用。这里没有这样做,仅仅是因为我们使用了一些现有的代码/经验。
- 一个标准的 NAnt 文档,用于不使用 X-Script Generator 的任务。