Background:
We're using a 3rd party tool in our .NET C# solution. This tool has it's own syntax and integrates with Visual Studio. When we use this tool we write its markup within Visual Studio and then when we build the solution the custom tool runs and generates a .cs file based on the markup we have written.
This generated source file contains a version number which is causing problems when we check these in to version control (Endless conflicts). Our understanding is that it's considered best practice not to check in generated source files.
So we excluded the generated .cs files from SVN and then the next issue we ran in to was that the Visual Studio solution referenced these files, so when TeamCity (Our continuous build/integration software) went to build the solution it would fail straight away as it couldn't find these files.
We then removed these from the solution as well as excluding them from SVN, this fixed the original issue, we're no longer checking in generated code and it builds fine in TeamCity (As the files are re-generated with every build).
We now have a new problem - As the generated files are no longer included in the solution, intellisense and code inspection fails as the generated classes cannot be found. The solution builds just fine (As again the code is re-generated during the build).
Question
Is there a way to tell ReSharper to include generated .cs files in its code inspection? These files are external to the solution but they are in the obj directory.
Cheers,
Tyler
We had a similar problem and couldn't come up with a good solution so I wrote a ReSharper extension to include external code:
https://resharper-plugins.jetbrains.com/packages/ReSharper.ExternalCode
As mentioned in my comment, one workaround is to keep the generated files in the solution (but not in source control), while adding a pre-build step to create empty .cs files (if the real generated file isn't present) so that the file is always available during a build.
In my projects, I use the following MSBuild targets to generate empty files by using the Touch task. You may need to make some modifications - in my case, the target files are actually defined within a project not at the solution level; and the build action for the files is set to "None" which is important to understand how these targets work.
<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<!--
Creates empty 'dummy' files for any files that are specified but do not exist.
To be processed, the following must be true:
1. The file is included in an ItemGroup called CanCreateDummy, e.g.
<ItemGroup>
<CanCreateDummy Include="SomeFile.cs" />
</ItemGroup>
If you want to specify a CanCreateDummy file in the .csproj file, you would
modify the above slightly as follows to prevent it appearing twice:
<ItemGroup>
<CanCreateDummy Include="SomeFile.cs">
<Visible>false</Visible>
</CanCreateDummy>
</ItemGroup>
2. The file is included in the ItemGroup called None. This is normally performed
by adding the file to the project in the usual way through Visual Studio, and
then setting the file's Build Action property to None.
-->
<Target
Name="CreateDummyFiles"
AfterTargets="BeforeBuild"
>
<!--
This voodoo creates the intersection of 2 lists - #(CanCreateDummy) and #(None)
(this latter item is defined in the project file). We want to create a filtered
list of all items that are in both these lists, which is called _ProjectDummyFiles.
See http://blogs.msdn.com/b/msbuild/archive/2006/05/30/610494.aspx for how the
Condition voodoo works.
-->
<CreateItem Include="#(CanCreateDummy)" Condition="'%(Identity)' != '' and '#(None)' != ''" >
<Output TaskParameter="Include" ItemName="_ProjectDummyFiles"/>
</CreateItem>
<Message
Text="Creating dummy settings file #(_ProjectDummyFiles)"
Condition=" !Exists('%(_ProjectDummyFiles.FullPath)')"
/>
<Touch
AlwaysCreate="true"
Files="#(_ProjectDummyFiles)"
Condition=" !Exists('%(_ProjectDummyFiles.FullPath)')"
/>
</Target>
</Project>
Hope this helps
Rich
Related
I've done a lot of MSBuild customization for my C++ projects in the past. The Input and Output attributes of a MSBuild target are used to determine whether the target has to be executed or not. Additionally Visual Studio uses the .tlog files (located in the intermediate directory) to determine whether MSBuild has to be invoked at all.
Now I'm working on a C# project. I wrote a simple MSBuild target which copies a file to the output directory:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="CopyMyFile" BeforeTargets="AfterBuild" Inputs="$(ProjectDir)File.dat" Outputs="$(TargetDir)FileRenamed.dat">
<Copy SourceFiles="$(ProjectDir)File.dat" DestinationFiles="$(TargetDir)FileRenamed.dat" OverwriteReadOnlyFiles="true">
</Copy>
</Target>
</Project>
The target works as expected if the build is invoked through MSBuild.exe. The file is copied if the target file does not exist or the source file has been modified.
If I invoke the build inside Visual Studio it does not work as expected. Visual Studio does not invoke MSBuild if I delete the file from the output directory. On the other hand MSBuild is invoked every time I build the project after modifiying the source file even if there are no other changes made.
It seems that Visual Studio just compares every file from a project to the output files (.exe, .dll or .pdb). If any file in the project is newer than the output files, MSBuild is invoked. In my case MSBuild does not update the .exe file, so MSBuild is invoked again and again.
In a C++ project this behaviour is controlled by the .tlog files. Is there anything similar in a C# project?
Thanks a lot!
The answer might be no, nothing similar to the tlog mechanism. I am not 100% sure though, also because it's strange you cannot do something quite basic as this as that would mean MS basically ditched the tracker stuff for C# (and similar) projects but didn't replace it with something which can be hooked into by users.
Using procmon you can see VS getting timestamps of output and input files, but nowhere I found a way to interfere with what it treats as input and output files. It looks like VS gets a list of everything included directly in the project file (i.e. Reference/Content/Compile/.. item groups of what is shown in VS), not what is listed in Taget's Inputs/Outputs, and at the start of a build compares timstamps for just those items. If everything (well, everything as far as VS is considered) is up to date no msbuild process is launched for the build.
There is a workaround, though not super nice: if you add a 'dummy' Content item (e.g. Right-click project->Add New Item->Text File) and set it to always be copied (Right-clik text file just added->Properties->Copy to Output Directory->Copy always) then VS will always start a build and hence check your target's Inputs vs the Outputs and run if if you deleted FileRenamed.dat.
It looks like this is just poorly documented. This site shows you can easily hook up a command line tool, while lifting on the incremental features of tlog files.
To make sure the information doesn't get lost, I'll just copy over their use case, but looking at that, I think it's easy to transform into your needs. Every occurrence of dcx can be replaced by e.g. data
1. Create a definition .xml file
Define an ItemType
Link a ContentType to the ItemType
Hook up a FileExtension
<?xml version="1.0" encoding="utf-8"?>
<ProjectSchemaDefinitions xmlns="http://schemas.microsoft.com/build/2009/properties">
<!-- Associate DXCShader item type with .hlsl files -->
<ItemType Name="DXCShader" DisplayName="DXC Shader" />
<ContentType Name="DXCShader" ItemType="DXCShader" DisplayName="DXC Shader" />
<FileExtension Name=".hlsl" ContentType="DXCShader" />
</ProjectSchemaDefinitions>
2. Create a .targets file
Include the .xml definitions file
Create a Target that depends on one of your build hooks (here: ClCompile)
Create an ItemGroup in your Target that will serve as the argument to your CustomBuild. Message, Command, AdditionalInputs and Output are meta-attributes that are relevant.
Invoke CustomBuild with MinimalRebuildFromTracking="true" and a TrackerLogDirectory to contain the tlog files. This part is the magic ingredient that makes MSBuild skip the build if your dependencies are up-to-date.
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<!-- Include definitions from dxc.xml, which defines the DXCShader item. -->
<PropertyPageSchema Include="$(MSBuildThisFileDirectory)dxc.xml" />
<!-- Hook up DXCShader items to be built by the DXC target. -->
<AvailableItemName Include="DXCShader">
<Targets>DXC</Targets>
</AvailableItemName>
</ItemGroup>
<Target
Name="DXC"
Condition="'#(DXCShader)' != ''"
BeforeTargets="ClCompile">
<Message Importance="High" Text="Building shaders!!!" />
<!-- Find all shader headers (.hlsli files) -->
<ItemGroup>
<ShaderHeader Include="*.hlsli" />
</ItemGroup>
<PropertyGroup>
<ShaderHeaders>#(ShaderHeader)</ShaderHeaders>
</PropertyGroup>
<!-- Setup metadata for custom build tool -->
<ItemGroup>
<DXCShader>
<Message>%(Filename)%(Extension)</Message>
<Command>
"$(WDKBinRoot)\x86\dxc.exe" -T vs_6_0 -E vs_main %(Identity) -Fh %(Filename).vs.h -Vn %(Filename)_vs
"$(WDKBinRoot)\x86\dxc.exe" -T ps_6_0 -E ps_main %(Identity) -Fh %(Filename).ps.h -Vn %(Filename)_ps
</Command>
<AdditionalInputs>$(ShaderHeaders)</AdditionalInputs>
<Outputs>%(Filename).vs.h;%(Filename).ps.h</Outputs>
</DXCShader>
</ItemGroup>
<!-- Compile by forwarding to the Custom Build Tool infrastructure,
so it will take care of .tlogs and error/warning parsing -->
<CustomBuild
Sources="#(DXCShader)"
MinimalRebuildFromTracking="true"
TrackerLogDirectory="$(TLogLocation)"
ErrorListRegex="(?'FILENAME'.+):(?'LINE'\d+):(?'COLUMN'\d+): (?'CATEGORY'error|warning): (?'TEXT'.*)" />
</Target>
</Project>
I have many C# script source files in the project tree marked as Build Action: None
The problem is: if the source file is marked as Build Action: None then Go To Declaration, Go To Implementation and other navigation and refactoring functions of Visual Studio and Resharper doesn't work anymore.
The script files are eventually compiled by the CSharpCodeProvider once the app is running and generally behave 100% the same as if they were in the actual code base (the original executable assembly).
The difference here is that the source files are not compiled by MSBuild, but by the app itself.
How can I force Visual Studio/build process to analyze the files (as if they were going to be compiled) without including them into final executable assembly?
If you edit the main project file (.csproj) and add an AfterBuild target, you can invoke any custom action on post build. The following example calls compile.exe for all Build Action=None items in the project with extension of .proj.
<Target Name="AfterBuild">
<ItemGroup>
<Scripts Include="#(None)" Condition="'%(Extension)' == '.proj'" />
</ItemGroup>
<Message Text="Scripts files: #(Scripts)" Importance="high" />
<Exec Command="Compile.exe %22%(Scripts.FullPath)%22"
WorkingDirectory="$(MSBuildProjectDirectory)"
Condition="'#(Scripts)' != ''" />
</Target>
One possible option is to have different build configurations, with different settings for the files in question.
You can have a development configuration, where the files are set to 'compile', and a deployment configuration, where they're set to 'none'.
That way, you'll get nice Intellisense when working on your code, and no compilation happening when you flip to 'deployment' configuration before you build.
How is
<None Include="C:\foo.bar" />
different from
<Content Include="C:\foo.bar" />
?
The MSDN article on the build action property explains the differences.
None - The file is not included in the project output group and is not compiled in the build process. An example is a text file that contains documentation, such as a Readme file.
Content - The file is not compiled, but is included in the Content output group. For example, this setting is the default value for an .htm or other kind of Web file.
One difference is how they get published; "None" items don't get included in a publish, "Content" items do; for example, on the "Application Files" dialog on the Publish tab.
I am not 100% sure (I read the MSDN description of Build Action property) but just copying that answer from MSDN to StackOverflow does not answer the question completely for me.
The difference of None and Content only has an effect on Web projects. For a command line project, WinForm project or UnitTest project (in my case) etc. None and Content have no different behavior.
MSDN: "project output group" or "Content output group" only terms used in a Web project, right?
In my situation, my MSBuild file had an ItemGroup for image resources that appeared as follows:
<ItemGroup>
<Content Include="Resources\image001.png" />
<Content Include="Resources\image002.png" />
<Content Include="Resources\image003.png" />
<Content Include="Resources\image004.png" />
<None Include="Resources\image005.png" />
<None Include="Resources\image006.png" />
<None Include="Resources\image007.png" />
</ItemGroup>
While my project was building fine, this left me wondering why I had a mix of Content and None item type elements in my ItemGroup. This MSDN article (for Visual Studio 2010) gave me the guidance I was looking for:
Note that when the resource editor adds an image, it sets Build
Action to None, because the .resx file references the image
file. At build time, the image is pulled into the .resources file
created out of the .resx file. The image can then easily be accessed
by way of the strongly-typed class auto-generated for the .resx file.
Therefore, you should not change this setting to Embedded
Resource, because doing this would include the image two times in
the assembly.
Resolution: With this guidance, using a text editor, I changed the Content item type elements to None.
Also, for an overview of MSBuild items, see this MSDN article.
Content files are not included in a build, but are included in a publish.
None files are not included in a build or publish, unless they are configured that way by you. For instance, a "Copy to Output Directory" setting of "Always" or "Newer", will cause them to be included in both a build and publish.
I have a project that contains no compilable items (it stores html and javascript for jasmine unit tests).
One day my solution (that contained said project) stopped compiling saying "The target "Build" does not exist in the project".
I added an import to bring in the compiler, which worked fine on my machine but failed using msbuild on the build server.
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
I then changed a line from
<None Include="SpecRunner.html" />
to
<Content Include="SpecRunner.html" />
and it worked on the build server as well.
You need None in a template project file to include files you define in the .vstemplate otherwise they are lost in the creation & translation process. They get left behind in the temp folder it uses to build everything and then deleted shortly after.
In my case .Pubxml is one of those files among None list. It's not meant for solution building or as a static file for web project. But to publish the site to Azure, the configurations are present in this.
As per Microsoft article these are the major types we see among .csproj file tags:
None - The file is not included in the project output group and is not
compiled in the build process. An example is a text file that contains
documentation, such as a Readme file.
Compile - The file is compiled into the build output. This setting is
used for code files.
Content - The file is not compiled, but is included in the Content
output group. For example, this setting is the default value for an
.htm or other kind of Web file.
Embedded Resource - This file is embedded in the main project build
output as a DLL or executable. It is typically used for resource
files.
I have a VS project that contains:
a pre-build action running TextTransform on a template.tt to generate generated.cs
generated.cs listed as one of the files to compile (i.e. in the list of project files)
When I build the project, the pre-build action is executed, generated.cs is re-created, but VS compiles the previous version of generated.cs (which I guess it loaded in memory when the build process started).
How to make the build use the new version of generated.cs (i.e. the one generated in the pre-build action)? How to force the build order?
Note that the text transformation input is dynamic and hence cannot be done in design time.
I don't think you need a custom pre-build action. Just add the ".tt" file to your project and set its "Custom Tool" property to "TextTemplatingFileGenerator". You might want to make sure that the *.generated.cs files are also added to the project, but I think that VS takes care of that.
There's now a solution to this problem!
Oleg Sych has a post on his blog detailing how to make transform-at-build-time work.
Here's the source: https://web.archive.org/web/20140116193428/http://www.olegsych.com/2010/04/understanding-t4-msbuild-integration/
Basically, you just include the T4 build targets in your project file and set the TransformOnBuild property to true.
Here's the relevant excerpt:
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<TransformOnBuild>true</TransformOnBuild>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\TextTemplating\v10.0\Microsoft.TextTemplating.targets" />
Note that the Microsoft.TextTemplating.targets file has to be included AFTER the Microsoft.CSharp.targets.
I just finished setting up an out-of-place build system for our existing C++ code using inherited property sheets, a feature that seems to be specific to the Visual C++ product. Building out-of-place requires that many of the project settings be changed, and the inherited property sheets allowed me to change all the necessary settings just by attaching a property sheet to the project. I am migrating our team from C++/MFC for UI to C# and WPF, but I need to provide the same out-of-place build functionality, hopefully with the same convenience. I cannot seem to find a way to do this with C# projects - I first looked to see if I could reference an MsBuild targets file, but could not find a way to do this. I know I could just use MsBuild for the whole thing, but that seems more complicated than necessary. Is there a way I can define a macro for a directory and use it in the output path, for example?
I'm not quite sure what an "out-of-place" build system is, but if you just need the ability to copy the compiled files (or other resources) to other directories you can do so by tying into the MSBuild build targets.
In our projects we move the compiled dlls into lib folders and put the files into the proper locations after a build is complete. To do this we've created a custom build .target file that creates the Target's, Property's, and ItemGroup's that we then use to populate our external output folder.
Our custom targets file looks a bit like this:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectName>TheProject</ProjectName>
<ProjectDepthPath>..\..\</ProjectDepthPath>
<ProjectsLibFolder>..\..\lib\</ProjectsLibFolder>
<LibFolder>$(ProjectsLibFolder)$(ProjectName)\$(Configuration)\</LibFolder>
</PropertyGroup>
<Target Name="DeleteLibFiles">
<Delete Files="#(LibFiles-> '$(ProjectDepthPath)$(LibFolder)%(filename)%(extension)')" TreatErrorsAsWarnings="true" />
</Target>
<Target Name="CopyLibFiles">
<Copy SourceFiles="#(LibFiles)" DestinationFolder="$(ProjectDepthPath)$(LibFolder)" SkipUnchangedFiles="True" />
</Target>
<ItemGroup>
<LibFiles Include=" ">
<Visible>false</Visible>
</LibFiles>
</ItemGroup>
</Project>
The .csproj file in Visual Studio then integrates with this custom target file:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" ... >
...
<Import Project="..\..\..\..\build\OurBuildTargets.targets" />
<ItemGroup>
<LibFiles Include="$(OutputPath)$(AssemblyName).dll">
<Visible>false</Visible>
</LibFiles>
</ItemGroup>
<Target Name="BeforeClean" DependsOnTargets="DeleteLibFiles" />
<Target Name="AfterBuild" DependsOnTargets="CopyLibFiles" />
</Project>
In a nutshell, this build script first tells MSBuild to load our custom build script, then adds the compiled file to the LibFiles ItemGroup, and lastly ties our custom build targets, DeleteLibFiles and CopyLibFiles, into the build process. We set this up for each project in our solution so only the files that are updated get deleted/copied and each project is responsible for it's own files (dlls, images, etc).
I hope this helps. I apologize if I misunderstood what you mean by out-of-place build system and this is completely useless to you!
Is there a way I can define a macro for a directory and use it in the output path
Have you looked at the pre-build and post-build events of a project?
Actually, pre-build and post-build events seem to be solely a place to add batch-file type commands. This would not help me to set up standard build directories for our projects, unfortunately. And having these events create batch files seems like a very 1980's approach for a modern language like C#, IMO.
After digging some more, and experimenting, I have found that you can add an <Import> directive into your .csproj file. When you do this, the IDE pops up a warning dialog that there is an unsafe entry point in your project - but you can ignore this, and you can make it not appear at all by editing a registry entry, evidently. So this would give me a way to get the variables containing the directory paths I need into the .csproj file.
Now to get the Output Path to refer to it - unfortunately when you add a string like "$(MySpecialPath)/Debug" to the Output Path field, and save the project, the $ and () chars are converted to hex, and your file get's put in a Debug directory under a directory named "$(MySpecialPath)". Arrgghh. If you edit the .csproj file in a text editor, you can set this correctly however, and it seems to work as long as the <Import> tag appears before the <PropertyGroup> containing the Output Path.
So I think the solution for me will be to create a standard OurTeam.targets MsBuild file in a standard location, add an installer for changing the registry so it doesn't flag warnings, and then create custom project templates that <Import> this file, and also set the Output Path to use the properties defined in the OurTeam.targets file. Sadly, this is more work and a less elegant solution than the property sheet inheritance mechanism in C++.