通过Visual Studio自动化设置项目的OutputPath属性

13

我正在编写一个VSIX包,使用户能够批量编辑当前加载解决方案中所有活动配置的项目的OutputPath属性(请参见极其烦人的第4步这里)。

我遇到了一个非常具体的问题:当将属性设置为包含宏的值时(例如"$(SolutionDir)\bin\Debug"),写入.csproj的值会被转义如下:

<OutputPath>%24%28SolutionDir%29\bin\Debug\</OutputPath>

与其让MSBuild扩展宏,不如创建一个实际的物理文件夹,并将其命名为$(SolutionDir)。我想以某种方式绕过这个转义。

MSDN文档在这一领域显然缺乏。

我的初始代码如下:

private void MenuItemCallback(object sender, EventArgs e)
{
    SolutionWideOutputDialogWindow dialog = new SolutionWideOutputDialogWindow();
    dialog.ShowModal();
    if (!dialog.DialogResult.HasValue || !dialog.DialogResult.Value)
    {
        return;
    }

    string requestedOutputPath = dialog.outputPathTextBox.Text;
    Solution2 solution = _dte2.Solution as Solution2;
    if (solution == null)
    {
        return;
    }

    Projects projects = solution.Projects;
    foreach (Project project in projects)
    {
        Property outputPath = project.ConfigurationManager.ActiveConfiguration.Properties.Item("OutputPath");
        outputPath.Value = requestedOutputPath;
        project.Save();
    }
}

非常感谢任何人的帮助。


当您使用IDE进行操作时,会发生完全相同的事情。IDE也没有给出任何提示表明支持宏。因此可以安全地假设您无法使其工作。请改用相对路径,例如“..\bin\debug”。 - Hans Passant
好的,MSBuild确实支持它,但这会让我直接编辑底层项目文件。 - DoomMuffins
msbuild/VS 的“对所有项目执行 x 操作”的方法是使用一个在所有项目中导入的公共文件。您可以通过在每个项目中添加该文件来手动(或脚本化)完成此操作,也可以使用全局文件(例如在 $(VCTargetsPath)\Platforms\Win32\ImportBefore 中)。在全局文件中设置 OutputPath,然后就完成了。我真的建议使用一个通用文件来定义几乎所有内容(编译器/链接器/工具选项和所有路径),这毕竟是DRY的问题。 - stijn
这实际上是我已经做过的事情,但当我想在每个解决方案配置基础上使输出路径全局化时遇到了问题(即$(SolutionDir)bin$(SolutionConfigurationName)$(SolutionPlatformName))。由MSBuild导出的宏是按项目为基础的,因此如果您有不完全统一命名的项目平台,则无法使用它们。最终,我找到了一个Visual Studio插件,可以在IDE内部为我导出这些插件。我不得不稍微调整一下,让它更早地导出它们,并且不会破坏VS功能,但现在我很满意。 - DoomMuffins
2个回答

15

很不幸,在项目属性编辑中,Visual Studio会转义特殊字符。

为了解决这个问题,请在文本编辑器中直接编辑你的.csproj文件。

例如,将以下内容更改为:

<OutputPath>%24%28SolutionDir%29\bin\Debug\</OutputPath>

致:

<OutputPath>$(SolutionDir)\bin\Debug\</OutputPath>

5
请注意:如果您使用文本编辑器,请将<OutputPath>设置为$(SolutionDir)\bin\Debug,则在Visual Studio 2013中它将显示为..\..\..\bin\Debug。好消息是,宏将保持不变。如预期,如果您对Visual Studio中的输出路径进行任何更改,则该宏将被删除。FYI - Microsoft使用此网站来帮助优先处理功能请求: UserVoice.com - Pressacco

11

这是我最终所做的:

我尝试解决的问题是不重复自己(D.R.Y.)并指定一个解决方案通用的输出目录(在一个包含很多项目的解决方案中)——也就是说,编译解决方案时,所有项目的输出目录都会被设置为类似$(SolutionDir)bin\Debug或者$(SolutionDir)bin\Release这样的内容。值得一提的是,有些项目跨越仓库并且在一个以上的解决方案中被包含。

起初,我创建了一个MSBuild文件(一个名为MySolution.sln.targets<Project> XML文件)。在它里面,我定义了一个<PropertyGroup>,将<OutputPath>属性覆盖为:

$(SolutionDir)bin\$(Platform)\$(Configuration)

然后我在所有相关项目中,在构建目标导入之前添加了以下导入:

<Import Project="$(SolutionPath).targets" />

这样,每个解决方案都有一个相应的.targets文件,定义了我希望在整个解决方案中使用的内容。

这个方法很有效,但后来我遇到了以下问题:上述的$(Platform)$(Configuration)宏是指项目属性而不是解决方案级别的属性。如果我的解决方案的Debug / Any CPU配置仍然在其Release配置中构建某些非常特定的项目,会发生什么情况?据我所知,在彻底检查文档后,没有导出具有解决方案级别粒度的宏。

我找到了ceztko的Visual Studio扩展程序,它使Visual Studio正好导出我要找的宏 - 但经过一些试验和调整,我发现该扩展程序设置得太晚了 - 只有在构建解决方案时才会设置。这导致在Visual Studio的增量构建功能中出现问题 - 它一直认为项目已过期,因为它寻找的位置是错误的 - 它不知道变量,但MSBuild.exe知道。

我开始尝试调整IVsUpdateSolutionEvents interface,跟踪每个方法被调用的时间 - 然后发现在打开一个单项目解决方案或将解决方案的配置从Debug更改为Release时,IVsUpdateSolutionEvents.OnActiveProjectCfgChange会被调用两次。进一步调整表明,如果我将项目设置为在两个解决方案配置中都编译为Release,则在更改解决方案配置时,此方法现在只会被调用一次。
我分叉了扩展程序的存储库并通过将宏设置逻辑移动到上述方法来修复问题。您可以在here找到它。
免责声明:这可能与IDE中的批量构建操作不太兼容,并要求您在从MSBuild.exe的命令行构建时自己导出这些属性。
祝您旅途愉快。

真棒的工作(原始项目现在似乎可以运行)。 - satnhak

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接