2

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="// &lt;auto-generated/&gt;" /> </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

3
  • 3
    Unless sombody knows all build steps by heart the solution is to run the build with diagnostics cranked up to verbose (can be set in VS somewhere, or pass /v:d to the commandline), then go through the output - which will list all targets - and figure out which is the one generating the file. Then add AfterTargets="<TargetGeneratingFile>" to your MarkGeneratedFiles target (no need for BeforeCompile etc anymore0 Commented Dec 25, 2016 at 9:14
  • @stijn Thank you so much! I was able to track down the target that was generating this (it was called GenerateProgramFile) and put AfterTargets="GenerateProgramFile" in my target. Everything works as expected now. Commented Dec 25, 2016 at 20:31
  • @ColeWu-MSFT Sure. I've posted an answer to my question, however I can't accept my own answer for 6 more hours. Commented Dec 26, 2016 at 21:33

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.