MSBuild: re-use method from one CodeTaskFactory target in another one

Solution 1:

Not sure how to resue code across Tasks using CodeTaskFactory; it might be possible depedning on how those are implemented exactly. E.g. if they'd create a dll from the code supplied and you can figure out which dll you could reference it and use it's functions. But instead of figuring out whether that is the case, we can also just write that exact principle ourselves:

  • create a file with the common code, Common.cs:

    namespace Foo
    {
      public static class Bar
      {
        public static bool CopyFileIfDifferent(string A, string B)
        {
          return true;
        }
      }
    }
    
  • build that file on the fly if needed (should only build if the source file changes or the dll does not exist because of using Inputs and Outputs):

    <PropertyGroup>
      <CommonDll>$(Temp)\Common.dll</CommonDll>
    </PropertyGroup>
    
    <Target Name="BuildCommonDll" Inputs="$(MSBuildThisFileDirectory)Common.cs" Outputs="$(CommonDll)">
      <Csc Sources="$(MSBuildThisFileDirectory)Common.cs"
           References="System.dll;mscorlib.dll"
           TargetType="Library" OutputAssembly="$(CommonDll)"/>
    </Target>
    
  • in your Task(s) reference the built dll:

    <UsingTask
      TaskName="WriteIntoFileIfDifferent"
      TaskFactory="CodeTaskFactory"
      AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll" >
      <ParameterGroup>
        <FileName ParameterType="System.String" Required="True" Output="False"/>
        <Text     ParameterType="System.String" Required="True" Output="False"/>
      </ParameterGroup>
      <Task>
        <Reference Include="$(CommonDll)" />
        <Code Type="Class" Language="cs">
          <![CDATA[
            using Foo;
    
            public class WriteIntoFileIfDifferent : Microsoft.Build.Utilities.Task
            {
              public string FileName{ get; set;}
    
              public string Text{ get; set;}
    
              public override bool Execute ()
              {
                var result = Foo.Bar.CopyFileIfDifferent(FileName, Text);
                Log.LogMessage("result = " + result.ToString());
                return true;
              }
            }
          ]]>
        </Code>
      </Task>
    </UsingTask>
    
  • in the Targets invoking this make sure the dll building is a dependency:

    <Target Name="Build" DependsOnTargets="BuildCommonDll">
      <WriteIntoFileIfDifferent FileName="A" Text="B"/>
    </Target>
    

Output of first run:

Project "c:\temp\foo.targets" on node 1 (Build target(s)).
BuildCommonDll:
  C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\bin\Roslyn\csc.exe /reference:System
  .dll /reference:mscorlib.dll /out:c:\temp\Common.dll /target:library c:\temp\Common.cs
Build:
  result = True
Done Building Project "c:\temp\foo.targets" (Build target(s)).

Output of consecutive runs:

Project "c:\temp\foo.targets" on node 1 (Build target(s)).
BuildCommonDll:
Skipping target "BuildCommonDll" because all output files are up-to-date with respect to the input files.
Build:
  result = True
Done Building Project "c:\temp\foo.targets" (Build target(s)).