我在之前的博客中有教大家如何编写 NuGet 工具包,其中就有编写 .targets 文件。

我在实际的使用中,发现 Visual Studio 2017 带来的含 Sdk 的新 csproj 格式基本上没有多少坑;然而旧的 csproj 文件却总是不能完美的运行,总是出错。关键是,不是每台电脑都出错,不是每个时机都出错。

本文将讲一些坑。


本文的前置知识

你可能需要了解 csproj 文件的格式和编译过程,才可能读懂本文,所以需要先阅读:

问题

下面的代码来自 SourceFusion 项目的早期版本。

这是一个 .targets 文件,项目安装此 NuGet 包之后就会自动 Import 这个 targets 文件。

<Project>
<PropertyGroup>
<_DefaultSourceFusionWorkingFolder Condition="'$(_DefaultSourceFusionWorkingFolder)' == ''">obj\$(Configuration)\</_DefaultSourceFusionWorkingFolder>
<SourceFusionWorkingFolder Condition="'$(SourceFusionWorkingFolder)' == ''">$(_DefaultSourceFusionWorkingFolder)</SourceFusionWorkingFolder>
<SourceFusionToolsFolder>$(SourceFusionWorkingFolder)SourceFusion.Tools\</SourceFusionToolsFolder>
<SourceFusionGeneratedCodeFolder>$(SourceFusionWorkingFolder)SourceFusion.GeneratedCodes\</SourceFusionGeneratedCodeFolder>
</PropertyGroup> <Target Name="_SourceFusionCreateDirectories" BeforeTargets="_SourceFusionWriteCompilingArgs;_SourceFusionWriteFilterArgs">
<ItemGroup>
<SourceFusionDirectory Include="$(SourceFusionWorkingFolder)" />
<SourceFusionDirectory Include="$(SourceFusionToolsFolder)" />
<SourceFusionDirectory Include="$(SourceFusionGeneratedCodeFolder)" />
</ItemGroup>
<MakeDir Directories="@(SourceFusionDirectory)" ContinueOnError="false" />
</Target>
</Project>

代码的解读如下:

  1. 创建了一个私有属性 _DefaultSourceFusionWorkingFolder,三个公有属性 SourceFusionWorkingFolderSourceFusionToolsFolderSourceFusionGeneratedCodeFolder
  2. 在编译期间,执行一个私有的 Target,收集所有收集到的文件夹,形成一个 SourceFusionDirectory 集合。然后将集合中的所有字符串视为文件夹,创建这几个文件夹。

在新的有 Sdk 的 csproj 中,这个 targets 文件的执行没有问题。但是,对于旧的 csproj 来说,就经常出现这几个属性为空或者部分为空的情况。额外的,就算修改这个文件,上面的属性也不会生效。

不过,如果使用命令行进行编译,这个却又是生效的。

原因

究其原因,这是 MSBuild 对项目文件(csproj)的解析和 Visual Studio 对项目文件的解析是不同的。命令行使用的是 MSBuild 解析 csproj,而 Visual Studio 使用的是 VSProjectSystem

对于 VSProjectSystem 来说,Project 根节点下的 PropertyGroupItemGroup 对不会更新。有时清除 Visual Studio 的项目缓存可以解决这个问题,但有时清除也不能解决。

真实的原因我并没有调查出来。但以上代码在大多数开发者的 Visual Studio 中是可以正常使用的,但有少数开发者使用这个会出现错误(没有创建任何文件夹)。

解决办法

既然问题出在 MSBuildVSProjectSystem 对属性和集合处理的不同,那么我就不要创建动态的集合,而是在 Target 内部编写属性和集合。

在 Target 内部的属性和集合将在编译期间进行计算,而不是在 Visual Studio 打开的时候就计算好。于是我们每次编译的时候都可以获得最新的属性和集合的值。

衍生知识

旧格式的 csproj 是不会自动计算属性和集合的变更的,这也是为什么项目文件改变的时候,Visual Studio 需要重新加载项目才可以正常显示和编译项目。同时,如果编辑旧格式的 csproj 文件,也需要先卸载掉项目才可以。而新格式的 csproj 是可以直接编辑而不需要卸载项目的,同时如果被外部改变,也不需要重新加载项目,而是可以直接计算出来新的属性和集合。

Visual Studio 2017 以前的旧格式的 csproj Import 进来的 targets 文件有时不能正确计算属性(PropertyGroup)和集合(ItemGroup)的更多相关文章

  1. 用于Azure功能的Visual Studio 2017工具

    今天我们很高兴地宣布发布第一个预览的Visual Studio 2017工具的Azure功能.这个预览介绍了一些令人兴奋的变化,我们以前的版本.此外,除了支持Visual Studio 2017之外, ...

  2. Visual Studio 2017 怎么将自动生成属性设置为旧版格式

    工具:Visual Studio 2017 1.点击工具,进入选项 2.选项窗口左侧找到C#--代码样式,点击 3.找到表达式首选项中:使用属性的表达式主体.使用索引器的表达式主体和使用访问器的表达式 ...

  3. 最强 IDE Visual Studio 2017 正式版发布

    Visual Studio 2017 正式版发布,该版本不仅添加了实时单元测试.实时架构依赖关系验证等新特性,还对许多实用功能进行了改进,如代码导航.IntelliSense.重构.代码修复和调试等等 ...

  4. Visual Studio 2017正式版发布全纪录

    又是一年发布季,微软借着Visual Studio品牌20周年之际,于美国太平洋时间2017年3月7日9点召开发布会议,宣布正式发布新一代开发利器Visual Studio 2017.同时发布的还有 ...

  5. Visual Studio 2017开发环境的安装

    Visual Studio 2017是微软为了配合.NET战略推出的IDE开发环境,同时也是目前开发C#程序最新的工具,本节以Visual Studio 2017社区版的安装为例讲解具体的安装步骤. ...

  6. Visual Studio 2017 Enterprise (15.3)

    版本15.3更新在用户离线下载时更加人性化,包含了进度显示,下载出错可以输入R,进行下载的重新尝试,并在当前下载框下继续下载为完成的作业,结合 --layout 参数的离线文件的检查和修复,并且在下载 ...

  7. Visual Studio 2017 发布 15.5 版本,百度网盘离线安装包下载。

    Visual Studio 2017 15.5 版本已正式发布,同时发布的还有 Visual Studio for Mac 7.3 .此次更新包含主要性能改进,新特性以及 bug 修复.发行说明中文版 ...

  8. Visual Studio 2017 Enterprise 发布 15.4 版本,离线安装包百度网盘下载。

    Visual Studio 2017 于2017年10月13日发布 15.4 版本.该版本包含多项生产力改进,支持 .NET Standard 2.0 ,并且可以开启 Xamarin Live Pla ...

  9. Visual Studio 2017 Enterprise 发布 15.3.3 版,附离线安装包百度网盘下载。

    Visual Studio 2017 Enterprise 发布 15.3.3 版,附离线安装包百度网盘下载. Visual Studio 2017 Enterprise 更新至 15.3.3 ,本安 ...

随机推荐

  1. Qt与FFmpeg联合开发指南(四)——编码(2):完善功能和基础封装

    上一章我用一个demo函数演示了基于Qt的音视频采集到编码的完整流程,最后经过测试我们也发现了代码中存在的问题.本章我们就先处理几个遗留问题,再对代码进行完善,最后把编码功能做基础封装. 一.遗留问题 ...

  2. C#对config配置文件的管理

    应用程序配置文件,对于asp.net是 web.config,对于WINFORM程序是App.Config(ExeName.exe.config). 配置文件,对于程序本身来说,就是基础和依据,其本质 ...

  3. LeetCode--202--快乐数

    问题描述: 编写一个算法来判断一个数是不是“快乐数”. 一个“快乐数”定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数变为 1,也可能是无限循环但始终变 ...

  4. PHP函数总结 (一)

    <?php /** * 原理: * 函数不调用不执行,定义函数时,会将 * 函数放到内存中代码段,当调用函数时去内存 * 中函数名称所在位置中执行函数体,执行完后 * 将控制权移交回给调用函数的 ...

  5. CoderForce 140C-New Year Snowmen(贪心)

    题目大意:有n个已知半径的雪球.堆一个雪人需要三个尺寸不同的雪球,问用这些雪球最多能堆多少个雪人? 题目分析:先统计一下每种尺寸的球的个数,从三种最多的种类中各取出一个堆成雪人,这样贪心能保证的到的数 ...

  6. linux下常用的截图、录屏工具

    录屏: 在linux下常用的录屏工具有5种,可以baidu或者google下喔,我选用的是recordMydesktop,使用非常方便,用时注意先把每秒桢数调高,否则效果必然很差. 在ubuntu下可 ...

  7. OC Copy和Property

  8. java并发编程:线程安全管理类--原子操作类--AtomicReference<V>

    1.类 AtomicReference<V> public class AtomicReference<V>extends Objectimplements Serializa ...

  9. PHP:第一章——PHP中逻辑运算符的使用方法

    //逻辑运算符 $a=;$b=;$c=;$d=; //逻辑与(and 和 &&)他们两个的逻辑是一样的,如果两个值都为true,结果才为true,否则是false. //var_dum ...

  10. POJ 2752 KMP中next数组的理解

    感觉这里讲的挺好的.http://cavenkaka.iteye.com/blog/1569062 就是不断递归next数组.长度不断减小. 题意:给你一个串,如果这个串存在一个长度为n的前缀串,和长 ...