Link Additional Files in Visual Studio - visual-studio

I am trying to include stylecop.json in my C# project on Visual Studio 2017. I have figured out how to do this by modifying the .csproj in any text editor:
<AdditionalFiles Include="stylecop.json">
<Link>stylecop.json</Link>
</AdditionalFiles>
I am wondering if there is a way to do this without making any text edits. I understand I can always add it like any other files but that would make a copy of the file within project folder and not link it externally.

You can add a Directory.Build.targets file somewhere in your solution directory (will apply to all projects at or below that directory) with the following contents:
<Project>
<ItemGroup>
<AdditionalFiles Update="#(AdditionalFiles)">
<Link Condition="'%(Link)' == ''">%(Identity)</Link>
</AdditionalFiles>
</ItemGroup>
</Project>
Note that using %(Identity) here isn't optimal for items outside the "project cone" - e.g. when you have <AdditionalFiles Include="..\..\foo.bar" />. For this you can use a mechanism similar to what SDK-based projects will do in VS 2017 15.3 / .net core 2.0 tooling:
<Project>
<ItemGroup>
<AdditionalFiles Update="#(AdditionalFiles)">
<LinkBase Condition="'%(LinkBase)' != ''">$([MSBuild]::EnsureTrailingSlash(%(LinkBase)))</LinkBase>
<Link Condition="'%(Link)' == '' And !$([MSBuild]::ValueOrDefault('%(FullPath)', '').StartsWith($([MSBuild]::EnsureTrailingSlash($(MSBuildProjectDirectory)))))">%(LinkBase)%(RecursiveDir)%(Filename)%(Extension)</Link>
</AdditionalFiles>
</ItemGroup>
</Project>
This will even preserve directory hierarchies of items matched with e.g. ..\shared\**\*.json and the target folder could be set using the LinkBase metadata:
<AdditionalFiles Include="..\shared\**\*">
<LinkBase>SharedFiles</LinkBase>
</AdditionalFiles>
Note that you may need to close and re-open the solution after making changes to Directory.Build.targets. In the upcoming VS 2017 15.3 update, changes will be monitored by VS automatically. In previous versions, the file will be cached by VS until the solution is closed.

Related

Visual Studio constantly triggers MSBuild Targets

I want to execute a text template before my MSBuild project in Visual Studio. I have added the following to my project file:
<Target Name="TransformOnBuild" BeforeTargets="ResolveProjectReferences">
<PropertyGroup>
<_TransformExe>$(MSBuildExtensionsPath)\..\Common7\IDE\TextTransform.exe</_TransformExe>
<_TextTransform>$(ProjectDir)AssemblyInfo.tt</_TextTransform>
<_TextTransformResult>$(ProjectDir)AssemblyInfo.cs</_TextTransformResult>
</PropertyGroup>
<Exec Command="del "$(_TextTransformResult)"" />
<Exec Command=""$(_TransformExe)" "$(_TextTransform)" -out "$(_TextTransformResult)"" />
</Target>
This simply deletes my AssemblyInfo.cs and regenerates it from AssemblyInfo.tt.
I use BeforeTargets="ResolveProjectReferences" because I need this file regenerated before any of the referenced projects get built.
Basically, this already works but I have noticed something strange: When I have this in my project file while Visual Studio is open, the AssemblyInfo.cs file constantly dissappears and then reappears. To me it looks like VS repeatedly executes my build target in the background. Of course I don't want it to behave like this. I want it to regenerate the file only when I start a build.
Is there any way to achieve my goal without generating constant CPU load and annoying file-wobbling in the explorer? Maybe a different base target than ResolveProjectReferences?
I use Visual Studio Professional 2022, Version 17.2.6
Update based on latest comments.
You could also try Condition="'$(DesignTimeBuild)' != 'true'".
Details/Background.
If you can live withit never being run inside Visual Studio, you can add this condition to the target element:
Condition="'$(BuildingInsideVisualStudio)' != 'true'"
Otherwise you can try this:
<Target Name="TransformOnBuild" BeforeTargets="ResolveProjectReferences"
Inputs="$(MSBuildProjectDirectory)AssemblyInfo.tt"
Outputs="$(MSBuildProjectDirectory)AssemblyInfo.cs">
<PropertyGroup>
<!-- ... -->
</Target>
You can learn more about Inputs/Outputs here. Basically, in this case, it means that the target will only be run, when AssemblyInfo.tt is newer than AssemblyInfo.cs.
Note that VS (for intellisence, etc.) will run targets in the background.

Visual Studio / msbuild CopyToOutputDirectory - how does it work?

I am using Visual Studio Community:
Microsoft Visual Studio Community 2019
Version 16.4.5
VisualStudio.16.Release/16.4.5+29806.167
Microsoft .NET Framework
Version 4.8.03752
I have a very simple ASP.Net Core website / app project, with the following directory structure:
website
/bin
/www
/about
/images
NOTE: The webroot directory is NOT wwwroot, but instead just www
The project file (.csproj) contains the following (to copy all sub-directories/files to the output directory):
<ItemGroup>
<Content Update="www\**\*">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
Using Visual Studio (Config = Debug)
BUILD -> Rebuild Solution
Result:
/bin
/Debug
/netcoreapp3.0
/Properties
/runtimes
(missing /www and all sub-directories and files!) - WHY???
The same results are obtained if I use msbuild.exe from the developer command prompt; makes no difference with regard to configuration (Debug vs Release).
I assumed that the www directory (and all sub-directories) and files would be copied to the output directory -- but clearly, this is not happening. I do not understand why the CopyToOutputDirectory is being ignored; I am missing something, but a search of the documentation did not yield any answers.
Any ideas / advice would be appreciated.
Thanks in advance.
Update:
Just changed the following in the project (.csproj) file:
<ItemGroup>
<Content Update="www\**\*">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
To:
<ItemGroup>
<None Update="www\**\*">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
Using Visual Studio: BUILD -> Rebuild Solution (success)
Also tried with msbuild.exe (success)
Now the www directory and all sub-directories / files are present in the output -- why? I suspect because I am not using the default "wwwroot" as the content directory, using <Content> does not work whereas <None> does; but that is only a theory; all I know is it works.
Also found this SO link useful in describing the differences between <Content> and <None>, for those who are curious:
What are the various "Build action" settings in Visual Studio project properties and what do they do?

How to ALWAYS include new files in a folder in Visual Studio 2017

I have a project in visual studio 2017 for making a WinJS UWP windows 10 app. I'm using babel to compile some files from one folder and put them in another -- > jsx/src includes my .jsx files, and they get compiled into jsx/out.
I've set up a babel watcher to watch the jsx/src file and output a new file to jsx/out, but for now every new file I add, I have to manually add it in visual studio so that it shows in the folder. I'd like this to be automatic.
I have the answer for this now! Didn't realize this was left unanswered.
The answer is to modify the project.jsproj file directly. Open your project folder, find the file called {projectname}.jsproj and open it in your favorite text editor.
For my use case, I wanted to:
Fully include the 'bundle.js' file in /jsx/out
Include, but not export with the build, any .js / .jsx files in /jsx/src
So I added these lines:
<None Include="jsx\src\**\*.jsx" />
<None Include="jsx\src\**\*.js" />
<Content Include="jsx\out\bundle.js" />
"None" means the files are shown in Visual Studio, still remain debuggable, but don't end up getting put in with the files when you build it for the windows store.
Now any new files get included in when they're added to the appropriate folder (sometimes you might have to force reload the project to see them)

TransformXml task could not be loaded from Microsoft.Web.Publishing.Tasks.dll

Has anyone seen this error and know how to fix it?
The "TransformXml" task could not be loaded from the assembly C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v11.0\Web\Microsoft.Web.Publishing.Tasks.dll.
Could not load file or assembly 'file:///C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v11.0\Web\Microsoft.Web.Publishing.Tasks.dll' or one of its dependencies. The system cannot find the file specified.
Confirm that the declaration is correct, that the assembly and all its dependencies are available, and that the task contains a public class that implements Microsoft.Build.Framework.ITask.
I read elsewhere that the problem is when you don't have SQL Server installed. But I do have SQL Express 2012 x64 installed with SP1. I am also running VS 2013 Professional.
I have ran this exact same solution in VS 2012 express with no problems.
The answers provided by Dai Bok and emalamisura work fine as long as you use Visual Studio 2012.
For VS 2013 this fails as well. In order to make this work with all versions of Visual Studio you should:
Open the project file (.csproj) of the project failing to load
Search for <Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.targets" />
Change it to <Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.targets" />
Reload the project
That will set the correct version of Visual Studio dynamically and properly.
To get mine to work, I just copied my v10.0 folder and renamed it to v11.0, and things seems to work well from then on. That's the quick fix for now.
As this is probably not the best solution, and although it works, I was going to try installing the Microsoft Windows SDK for Windows 7 and .NET Framework 4 Windows SDK for Windows 7 and .NET Framework 4, but it is taking to long to download.
To fix the issue,
Find the Visual studio Installer in your computer
Click or tap to start the installer, and then select Modify.
From the Individual Components screen, select Asp.net and web development tools and then select Modify/Install.
This solved the issue as it creates the dll's in the mentioned path.
I've been combating this problem on our build server for several days, so I figured I'd document the resolution I came to. First, my build server has the web publishing extensions installed. I can use the TransformXml task to my heart's content inside of a web application project.
To use it outside of a web application project, I tried to add the UsingTask element to my project and point it to the right place using ms build properties (as Benjamin demonstrated). However, they weren't there on my build server (those with easy access to the file system of their build server can probably skip this and just install the relevant package to Visual Studio). I even went so far as to hard code visual studio versions, but it always dropped that error on me.
I finally gave up, pulled the DLLs from my local PC:
C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v12.0\Web\Microsoft.Web.Publishing.Tasks.dll
C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v12.0\Web\Microsoft.Web.XmlTransform.dll
I uploaded them to source control and added that folder to my build's workspace (Edit Build Definition -> Source Settings -> Source Control Folder). From there, I don't even need to reference the folder -- here's what my UsingTask looks like:
<UsingTask TaskName="TransformXml" AssemblyFile="Microsoft.Web.Publishing.Tasks.dll" />
Now I can use the TransformXml task to my heart's content from any project.
For VS2019
<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(MSBuildToolsVersion
I replaced MSBuildToolsVersion with VisualStudioVersion.
Because there are only v12.0, v14.0 and v15.0 in my VisualStudio folder, I edit my project file and change the reference path from v10.0 to v14.0. Then the project builds successfully.
Before:
<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll" />
After:
<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v14.0\Web\Microsoft.Web.Publishing.Tasks.dll" />
Solutions provided seem to work for using VS as an IDE, but if you use DotnetCore via CLI or on a unix based system this does not work.
I found that the following seem to work
<PropertyGroup>
<XmlTransformDllPath Condition="'$(XmlTransformDllPath)' == '' AND '$(MSBuildRuntimeType)' == 'core'">$(MSBuildSDKsPath)/Microsoft.NET.Sdk.Publish/tools/net5.0/Microsoft.NET.Sdk.Publish.Tasks.dll</XmlTransformDllPath>
<XmlTransformDllPath Condition="'$(XmlTransformDllPath)' == '' AND '$(MSBuildRuntimeType)' != 'core'">$(MSBuildSDKsPath)/Microsoft.NET.Sdk.Publish/tools/net472/Microsoft.NET.Sdk.Publish.Tasks.dll</XmlTransformDllPath>
<XmlTransformDllPath Condition="!Exists($(XmlTransformDllPath))">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll</XmlTransformDllPath>
</PropertyGroup>
<UsingTask TaskName="TransformXml" AssemblyFile="$(XmlTransformDllPath)" />
This solution takes into account netcore, full .net
For some reason MSBuildSDKsPath and MSBuildExtensionsPath32 are different on windows when using CLI vs VS2019
CLI:
MSBuildSDKsPath = C:\Program Files\dotnet\sdk\5.0.103\Sdks
MSBuildExtensionsPath32 = C:\Program Files\dotnet\sdk\5.0.103
Vs2019
MSBuildSDKsPath = C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Sdks
MSBuildExtensionsPath32 = C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild
Which on my Mac returns /usr/local/share/dotnet/sdk/5.0.201
Only problem I see is with the tools/net5.0 part of the name which changes ever release
Also created https://github.com/dotnet/sdk/issues/16469 and answers this on The "TransformXml" task was not found (error MSB4036) on TeamCity build
The correct answer to this is to unload the project in question and then edit the csproj file, look for an entry where they are referencing the 10.0 path and change it to point to 11.0 instead.
You need two things to make it work:
1) Install Visual Studio Build Tools (You don't need the whole Visual Studio, only the VS Build Tools) with selected "Web development build tools" option on your build server
https://www.visualstudio.com/pl/thank-you-downloading-visual-studio/?sku=BuildTools&rel=15
2) Ensure that path to Microsoft.Web.Publishing.Tasks.dll is correct
<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(MSBuildToolsVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />
For me it started working just by adding reference to the NuGet package MSBuild.Microsoft.VisualStudio.Web.targets v14.0.0.3
Even no need to add UsingTask element to the project file as it mentioned by the package author
https://github.com/pdonald/nuget-webtargets
Just install the NuGet package. The package automatically sets the
$(VSToolsPath) property to use the targets file in the tools folder.
And then I was able to use TransformXml and other tasks, defined in the package, for instance to transform app.config
<Target Name="app_config_AfterCompile" AfterTargets="AfterCompile" Condition="Exists('app.$(Configuration).config')">
<!--Generate transformed app config in the intermediate directory-->
<TransformXml Source="app.config" Destination="$(IntermediateOutputPath)$(TargetFileName).config" Transform="app.$(Configuration).config" />
<!--Force build process to use the transformed configuration file from now on.-->
<ItemGroup>
<AppConfigWithTargetPath Remove="App.config" />
<AppConfigWithTargetPath Include="$(IntermediateOutputPath)$(TargetFileName).config">
<TargetPath>$(TargetFileName).config</TargetPath>
</AppConfigWithTargetPath>
</ItemGroup>
</Target>
Just in case someone is using an SDK-style csproj, you can achieve this without having to install Visual Studio on the build server.
First you should install the SlowCheetah nuget package to your project. Once you install it, you'll see the following in your SDK-style project.
<PackageReference Include="Microsoft.VisualStudio.SlowCheetah" Version="3.2.20">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
Then make sure you add the GeneratePathProperty="true" attribute (see below). This is very important for the next part because it'll help you grab the path of where the nuget package is restored on your machine. George Dangl explains it in his article here.
<PackageReference Include="Microsoft.VisualStudio.SlowCheetah" Version="3.2.20" GeneratePathProperty="true">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
Import the SlowCheetah targets into your project:
<Import Project="$(PkgMicrosoft_VisualStudio_SlowCheetah)\build\Microsoft.VisualStudio.SlowCheetah.targets" />
You can now use an target command (in this case after publish) to apply some custom transformations. If you need to, you can always hard-code the file names below instead of using the variables in the below example.
<Target Name="AfterPublishs" AfterTargets="Publish">
<TransformTask Source="Web.config" Transform="Web.$(Configuration).MyCustomTransformFile.config" Destination="$(PublishDir)\Web.config" />
</Target>
If you haven't used SlowCheetah before, I recommend checking it out. They have a Visual Studio extension that will make it easier for you to preview transform files.

VS2010 custom tool - generated filename bug? (YUI Custom Tool and others)

I don't know if this is a bug of visual studio 2010 but when I run the YUI Custom Tool in some files I get the new file with a "1" added to the filename..
For example, I have a file named Flexigrid.js and when I run the custom tool I get a file called Flexigrid1.min.js instead of Flexigrid.min.js
I'm running Visual Studio 2010 Version 10.0.40219.1 SP1Rel over Windows 7 Proffesional 64bit...
Any ideas?
Thanks in advance!!!
Cheers from Argentina!
I found the solution:
1) Unload the .csproj open it for edit..
2) Find the files and remove the .. tag. Example:
<EmbeddedResource Include="JavaScript\flexigrid.js">
<Generator>YUICustomTool</Generator>
<LastGenOutput>flexigrid1.min.js</LastGenOutput> <!-- Remove this tag -->
</EmbeddedResource>
3) Remove the minified file reference (e.g: flexigrid1.min.js). Example:
<!-- Remove -->
<EmbeddedResource Include="JavaScript\flexigrid1.min.js">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>flexigrid.js</DependentUpon>
</EmbeddedResource>
<!-- Remove -->
4) Save and reload the project.
5) Run custom tool again... this time should generate flexigrid.min.js
I hope it helps...
Cheers!

Resources