Reputation: 34499
Background: StyleCop is complaining that an auto-generated file has poor formatting, leading to many warnings when I try to build my project. The auto-generated file is in the obj/
directory of my project, and I want to create an MSBuild task that prepends // <auto-generated/>
to this file before compilation (but after it is generated) so that StyleCop doesn't complain.
Problem: I have the following MSBuild code
<!-- StyleCop complains about a file that's auto-generated by the designer,
so we need to prepend 'auto-generated' to it beforehand. -->
<Target Name="BeforeCompile" DependsOnTargets="MarkGeneratedFiles" />
<Target Name="MarkGeneratedFiles">
<PropertyGroup>
<GeneratedFilePath>$(MSBuildThisFileDirectory)obj\$(Configuration)\$(TargetFramework)\$(MSBuildProjectName).Program.cs</GeneratedFilePath>
</PropertyGroup>
<InsertIntoFile FilePath="$(GeneratedFilePath)" LineNumber="1" Text="// <auto-generated/>" />
</Target>
<!-- Code taken from http://stackoverflow.com/a/21500030/4077294 -->
<UsingTask
TaskName="InsertIntoFile"
TaskFactory="CodeTaskFactory"
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<ParameterGroup>
<FilePath ParameterType="System.String" Required="true" />
<LineNumber ParameterType="System.Int32" Required="true" />
<Text ParameterType="System.String" Required="true" />
</ParameterGroup>
<Task>
<Using Namespace="System" />
<Using Namespace="System.IO" />
<Code Type="Fragment" Language="cs">
<![CDATA[
// By tradition, text file line numbering is 1-based
var lines = File.Exists(FilePath)
? File.ReadAllLines(FilePath).ToList()
: new List<String>(1);
lines.Insert(Math.Min(LineNumber - 1, lines.Count), Text);
File.WriteAllLines(FilePath, lines);
return true;
]]>
</Code>
</Task>
</UsingTask>
The file that I want to modify has the filename obj/Debug/netcoreapp1.0/BasicCompiler.Tests.Program.cs
. In the above snippet, I have a BeforeCompile
target that depends on MarkGeneratedFiles
, which goes ahead and tries to insert // <auto-generated/>
before the first line of that file.
I have tested, and this seems to work fine if the generated file is already present. However, if I remove the obj/
directory or I build from another machine, I get this error:
"C:\cygwin64\home\james\Code\cs\BasicCompiler\src\BasicCompiler.Tests\BasicCompiler.Tests.csproj" (default target) (1) ->
(MarkGeneratedFiles target) ->
C:\cygwin64\home\james\Code\cs\BasicCompiler\src\BasicCompiler.Tests\BasicCompiler.Tests.csproj(68,5): error MSB4018: The "InsertIntoFile" task failed unexpectedly.\r
C:\cygwin64\home\james\Code\cs\BasicCompiler\src\BasicCompiler.Tests\BasicCompiler.Tests.csproj(68,5): error MSB4018: System.IO.DirectoryNotFoundException: Could not find
a part of the path 'C:\cygwin64\home\james\Code\cs\BasicCompiler\src\BasicCompiler.Tests\obj\Debug\netcoreapp1.0\BasicCompiler.Tests.Program.cs'.\r
Basically it seems like the target is getting run before the file is getting generated, so there's nothing to prepend the text to. Is there a way to run it after this file gets generated, but before compilation?
Additional notes: So far, I have looked through all of the special target names here and tried using both BeforeBuild
and BeforeCompile
.
Also, since I am using the "new" StyleCop, I cannot put <ExcludeFromStyleCop>
in my project file. See https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/1145
Upvotes: 2
Views: 1627
Reputation: 34499
I managed to work around this; see @stijn's super-helpful comment here.
First, I ran msbuild /v:detailed
from the command line. This increases the verbosity of MSBuild so that it gives you a more detailed overview of what's going on, e.g. you can see the name of each target that's being run.
I searched through the log for the name of the target that was generating the file. In my case, it turned out to be GenerateProgramFiles
.
I marked my custom target with AfterTargets="GenerateProgramFiles"
so it ran after the file was generated.
Upvotes: 2