MathematicalOrchid
MathematicalOrchid

Reputation: 62858

Make VisualStudio run a command to generate C# code

I'm trying to do something extremely simple, yet despite hours of scouring the Internet and desperately typing in every tiny example fragment I can find, I literally cannot make VS do what I want. Seriously frustrated here!

This is what I want:

I am damn-well convinced it must be possible to achieve this trivially simple task. I just need to figure out how.


So that's the problem. Let me explain what I've researched so far.

It seems that you can write a VS extension by implementing some COM interface. But that requires you to edit the Registry to tell VS where the plugin is. Obviously that's completely unacceptable; I should not have to reconfigure the OS just to run a command-line tool!

It appears that MSBuild is supposed to be this ultra-configurable build management tool where you can define an arbitrary set of build steps and their interdependencies. It seems like it should be trivial to add an extra task before the build C# compilation step that runs fubar.exe to generate the source code. And yet, after hours of trying, I cannot get a single C# file to appear.

Some documents talk about special pre-build and post-build steps. But it seems silly to need a special hook when the number and order of build steps is supposed to be arbitrary to begin with. Regardless, I tried it, and it still didn't work.

To be clear: Playing around with the project file makes VS display the item properties slightly differently, and doesn't make the build fail. But it also doesn't ever make any C# files appear. (At least, I can't find them anywhere on the disk. I can't tell if my tool is actually being run or not — I suspect "not".)

Can anybody tell me how to make this trivial, trivial thing work? There must be a way!

Upvotes: 4

Views: 1952

Answers (2)

jessehouwing
jessehouwing

Reputation: 115037

I agree that T4 may be a prettier solution, but the below MsBuild code works.

You can create a custom targets file that does the calling, I created one that simply copies files, but the process should be similar for invoking your executable.

Make sure you set your FuBar files to BuildAction "Fubars". The Build action has been defined in the targets file using the "AvailableItemName" property.

You may need to extend the below script with a couple of Conditions and Exists checks to make sure it runs when needed. I called it convert.targets and places it in the project directory.

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <!-- Ensure Visual Studio puts the FuBars item in the Build Action dropdown -->
  <ItemGroup>
    <AvailableItemName Include="FuBars" />
  </ItemGroup>

  <!-- Execute action on all items matching FuBar (only if output is missing or input has changed) -->
  <Target Name="GenerateCodeFubar" Inputs="@(Fubars)" Outputs="$(MSBuildProjectDirectory)/obj/$(Configuration)/Fubars/%(Fubars.Identity).g.cs" >
    <MakeDir Directories="$(MSBuildProjectDirectory)/obj/$(Configuration)/Fubars/%(Fubars.RelativeDir)" />

    <Exec Command="cmd /c copy &quot;%(FuBars.FullPath)&quot; &quot;$(MSBuildProjectDirectory)/obj/$(Configuration)/Fubars/%(FuBars.Identity).g.cs&quot;"/>
  </Target>

  <!-- Pick up generated code in the Compile group. Separated from the execute action so that the previous action only runs when files have changes -->
  <Target Name="IncludeCodeFubarInCompile">
    <ItemGroup>
      <GeneratedFubars Include="$(MSBuildProjectDirectory)/obj/$(Configuration)/Fubars/%(FuBars.Identity).g.cs"/>
    </ItemGroup>

    <CreateItem Include="%(GeneratedFubars.FullPath)">
      <Output TaskParameter="Include" ItemName="Compile" />
    </CreateItem>
  </Target>

  <!-- Clean up for Rebuild or Clean -->
  <Target Name="DeleteCodeFubar">
   <RemoveDir Directories="$(MSBuildProjectDirectory)/obj/$(Configuration)/Fubars/" />
     <Delete Files="$(MSBuildProjectDirectory)/obj/$(Configuration)/fubars/**/*.g.cs" />
  </Target>

    <!-- Instruct MsBuild when to call target on Build-->
    <PropertyGroup>
        <BuildDependsOn>
            GenerateCodeFubar;
            IncludeCodeFubarInCompile;
            $(BuildDependsOn);
        </BuildDependsOn>
    </PropertyGroup>

    <!-- Instruct MsBuild when to call target on Clean-->    
    <PropertyGroup>
        <CleanDependsOn>
            DeleteCodeFubar;
            $(CleanDependsOn);
        </CleanDependsOn>
    </PropertyGroup>
</Project>

Include it in your .csproj after the import the last targets file:

  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

  <!-- IMPORT HERE -->
  <Import Project="convert.targets" />
  <!-- END: IMPORT HERE -->

  <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
       Other similar extension points exist, see Microsoft.Common.targets.
  <Target Name="BeforeBuild">
  </Target>
  <Target Name="AfterBuild">
  </Target>
  -->
</Project>

You may have to tell Visual Studio to turn off the Host Compiler, as Visual Studio caches the contents of the <Compile> group for performance. Though a quick test shows this may not be needed.

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <!-- The first property group should not have any condition on it -->
  <PropertyGroup> 

    <!-- Include below item in the first PropertyGroup item:
    <UseHostCompilerIfAvailable>FALSE</UseHostCompilerIfAvailable>
  </PropertyGroup>

Upvotes: 4

Sam Axe
Sam Axe

Reputation: 33738

This is extremely easy.

In your project properties select the Build Events tab and enter a correct Pre-build event command line.

Without knowing what footer.exe is nor its command line args, I can't give you any specific help for that app. But there is a "Macros" button on the pre-build event dialog that can help you with various substitution variables.


Additionally, VS comes with the T4 templating system which might be a better fit than whatever footer.exe is.

Upvotes: -1

Related Questions