使用 MSBuild 批量重命名
Posted
技术标签:
【中文标题】使用 MSBuild 批量重命名【英文标题】:Batch rename with MSBuild 【发布时间】:2012-10-09 04:31:28 【问题描述】:我刚刚加入了一个没有 CI 流程(甚至没有一夜之间构建)和一些粗略的开发实践的团队。人们渴望改变这一点,所以我现在的任务是创建一个隔夜构建。我已经跟随this series 的文章:创建一个包含我们所有项目的主解决方案(一些 Web 应用程序、一个 Web 服务、一些 Windows 服务,以及编译成命令行可执行文件的耦合工具);创建了一个 MSBuild 脚本来自动构建、打包和部署我们的产品;并创建了一个 .cmd 文件以一键完成所有操作。这是我现在正在尝试完成的一项任务:
团队目前的做法是将 web.config 和 app.config 文件置于源代码管理之外,并将其放入名为 web.template.config 和 app.template.config 的源代码管理文件中。目的是开发人员将 .template.config 文件复制到 .config 以获得所有标准配置值,然后能够将 .config 文件中的值编辑为本地开发/测试所需的任何值.出于显而易见的原因,我想自动化将 .template.config 文件重命名为 .config 的过程。最好的方法是什么?
是否可以在构建脚本本身中执行此操作,而不必在脚本中规定需要重命名的每个单独文件(这将需要在任何时候将新项目添加到解决方案时对脚本进行维护)?或者我可能需要编写一些我只是从脚本中运行的批处理文件?
此外,有没有更好的开发解决方案,我可以建议让整个过程变得不必要?
【问题讨论】:
为什么没有检入配置文件?您能否提供一些特定于开发人员(且无法共享)的配置设置示例? 据我所知,没有使用无法共享的设置。但是开发人员可能想要更改连接字符串,以便他可以在本地数据库上进行所有测试。我可以告诉你,当前的过程很糟糕,因为项目文件仍然需要一个 web.config 文件,这增加了我为了构建所有东西而不得不做的手动工作量。 整个团队(在我加入之前只有 3 或 4 个开发人员)似乎对现状没有问题,因为似乎开发人员对特定项目几乎拥有所有权,并且可能甚至不建立其他人。因此,他们只处理单个项目文件,而不是像我创建的那样的主解决方案。 【参考方案1】:在阅读了大量有关项目组、目标和复制任务的内容后,我已经弄清楚了如何做我需要的事情。
<ItemGroup>
<FilesToCopy Include="..\**\app.template.config">
<NewFilename>app.config</NewFilename>
</FilesToCopy>
<FilesToCopy Include="..\**\web.template.config">
<NewFilename>web.config</NewFilename>
</FilesToCopy>
<FilesToCopy Include"..\Hibernate\hibernate.cfg.template.xml">
<NewFilename>hibernate.cfg.xml</NewFilename>
</FilesToCopy>
</ItemGroup>
<Target Name="CopyFiles"
Inputs="@(FilesToCopy)"
Outputs="@(FilesToCopy->'%(RootDir)%(Directory)%(NewFilename)')">
<Message Text="Copying *.template.config files to *.config"/>
<Copy SourceFiles="@(FilesToCopy)"
DestinationFiles="@(FilesToCopy->'%(RootDir)%(Directory)%(NewFilename)')"/>
我创建了一个项目组,其中包含我要复制的文件。 ** 运算符告诉它递归遍历整个目录树以查找具有指定名称的每个文件。然后,我将一段元数据添加到每个名为“NewFilename”的文件中。这就是我将每个文件重命名为的内容。
这个 sn-p 将每个文件添加到名为 app.template.config 的目录结构中,并指定我将命名新文件 app.config:
<FilesToCopy Include="..\**\app.template.config">
<NewFilename>app.config</NewFilename>
</FilesToCopy>
然后我创建一个目标来复制所有文件。这个目标最初非常简单,只调用 Copy 任务以便始终复制和覆盖文件。我将 FilesToCopy 项目组作为复制操作的源传递。我使用transforms 来指定输出文件名,以及我的NewFilename 元数据和众所周知的项目元数据。
下面的 sn-p 将例如将文件 c:\Project\Subdir\app.template.config 转换为 c:\Project\Subdir\app.config 并将前者复制到后者:
<Target Name="CopyFiles">
<Copy SourceFiles="@(FilesToCopy)"
DestinationFiles="@(FilesToCopy->'%(RootDir)%(Directory)%(NewFileName)')"/>
</Target>
但后来我注意到开发人员可能不喜欢在每次运行脚本时都覆盖他的自定义 web.config 文件。但是,如果存储库的 web.template.config 已被修改,开发人员可能应该覆盖他的本地文件,并且现在其中包含代码需要的新值。我尝试了多种不同的方法——使用“Exist()”函数将 Copy 属性“SkipUnchangedFiles”设置为 true——但无济于事。
解决方案是building incrementally。这可确保仅当 app.template.config 较新时才会覆盖文件。我将文件名作为目标输入传递,并将新文件名指定为目标输出:
<Target Name="CopyFiles"
Input="@(FilesToCopy)"
Output="@(FilesToCopy->'%(RootDir)%(Directory)%(NewFileName)')">
...
</Target>
这有目标检查以查看当前输出相对于输入是否是最新的。如果不是,即特定的 .template.config 文件比其对应的 .config 文件具有更多的最新更改,那么它将复制 web.template.config 覆盖现有的 web.config。否则,它将单独保留开发人员的 web.config 文件且未修改。如果不需要复制任何指定的文件,则完全跳过目标。干净的存储库克隆后,将立即复制每个文件。
以上结果证明是一个令人满意的解决方案,因为我才开始使用 MSBuild,我对它的强大功能感到惊讶。我唯一不喜欢的是我必须在两个地方重复完全相同的变换。我讨厌复制任何类型的代码,但我不知道如何避免这种情况。如果有人有小费,将不胜感激。此外,虽然我认为需要这样做的开发实践完全糟糕,但这确实有助于减轻这种糟糕的因素。
【讨论】:
会自动调用名为“CopyFiles”的目标?还是我必须做一些事情来包含该目标? 假设您正在运行不同的目标,例如“构建”,并且您希望此目标作为它的一部分运行,您需要使 CopyFiles 成为目标的依赖项,如下所示:简答: 是的,您可以(并且应该)将其自动化。您应该可以使用MSBuild Move task 重命名文件。
长答案: 人们希望从手动过程变为自动过程,这真是太好了。通常很少有真正的理由不进行自动化。您的构建脚本将充当构建和部署实际工作方式的活文档。在我看来,一个好的构建脚本比静态文档更有价值(尽管我并不是说你不应该有文档 - 毕竟它们不是互斥的)。让我们分别解决您的问题。
最好的方法是什么?
我不完全了解您在这些文件中存储的配置,但我怀疑很多配置可以在开发团队之间共享。
我建议提出以下问题:
哪些设置是特定于开发者的? 有没有办法标准化本地开发者机器以便共享设置?是否可以在构建脚本本身中执行此操作,而不必在脚本中规定需要重命名的每个单独文件?
是的,看看MSBuild Move task。您应该可以使用它来重命名文件。
...每当将新项目添加到解决方案时,都需要对脚本进行维护?
这是不可避免的 - 您的构建脚本必须与您的解决方案一起发展。接受这一事实,并在您估计的时间中包含对构建脚本进行更改的时间。
此外,有没有更好的开发解决方案,我可以建议让整个过程变得不必要?
我不了解所有要求,因此很难推荐非常具体的内容。我可以说建议:
为您的解决方案创建共享构建脚本 尽可能自动化手动任务(在合理范围内) 如果您正在努力实现自动化 - 这可能表明某个领域需要重新考虑/重新设计 确保您的团队成员了解构建的工作原理并能够自行对其进行更改 - 不要“拥有”构建并成为瓶颈请记住,从无构建脚本到完全自动化并非一朝一夕的过程。要有耐心,首先专注于自动化导致最痛苦的领域。
如果我误解了您的任何问题,请告诉我,我会更新答案。
【讨论】:
谢谢,阿诺德。需要明确的是,配置设置 存储在源代码管理中,它们只是以不同的文件名存储,因此需要上述过程。我想这允许开发人员从存储库中提取,而不必担心他的特定配置会被覆盖,从而迫使他用他的本地值重新编辑。似乎应该有更好的方法来做到这一点,例如也许可以导入一个本地文件来覆盖 web.config 中的设置,就像 MSBuild 可以传递 /p:TargetEnvPropsFile=xxx.proj 来配置特定环境一样。 另外,我希望,就像我可以将解决方案文件传递给 MSBuild(这不需要编辑脚本来构建另一个项目,只需将其添加到解决方案中),我会能够在脚本中编写代码以查找任何具有 .template.config 扩展名的文件并将扩展名重命名为 .config。如果我不能这样做,并且在添加新项目时需要编辑脚本,以便我们可以列出另一个要重命名的文件,那么 几乎 就违背了传递解决方案的目的全部,并且可能会主张单独列出脚本中的每个项目。 @MarcChu 是的,您可以将这样的逻辑添加到构建脚本中。我以前做过类似的事情(出于其他原因)。以上是关于使用 MSBuild 批量重命名的主要内容,如果未能解决你的问题,请参考以下文章