2
votes

I have a custom MSBuild Task that generates a file based on files that have no build action. The generated files need to be embedded into the final assembly. The task looks something like this:

public class MyTask : Task
{
    public string OutputDirectory { get; set; }

    public string[] NoneIncluded { get; set; }

    private IEnumerable<ITaskItem> _generatedFiles;
    [Output]
    public ITaskItem[] GeneratedFiles => _generatedFiles.ToArray();

    public override bool Execute()
    {
        _generatedCssFiles = new List<ITaskItem>();
        foreach(var item in NoneIncluded)
        {
            if(someCondition)
            {
                var contents = DoFoo(item);
                var outputPath = Path.Combine(OutputDirectory, $"{item}.txt");
                File.WriteAllText(outputPath, contents);
                _generatedFiles.Add(new TaskItem(ProjectCollection.Escape(outputFile)));
            }
        }
    }
}

In my targets file, I then have a target defined like the following:

<PropertyGroup>
    <CoreCompileDependsOn>MyTarget;$(CoreCompileDependsOn);</CoreCompileDependsOn>
</PropertyGroup>

<Target Name="MyTarget"
        BeforeTargets="CoreCompile;Build">

  <MyTask OutputDirectory="$(IntermediateOutputPath)"
          NoneIncluded="@(None)">
    <Output ItemName="FilesWrite"
            TaskParameter="GeneratedFiles"/>
    <Output ItemName="EmbeddedResource" 
            TaskParameter="GeneratedFiles" />
  </MyTask>

</Target>

I ultimately end up with two issues that I can't seem to resolve:

  1. Although the files are generated prior to the compile task, it isn't embedded into the assembly, unless I rebuild the project without cleaning the outputs. On the subsequent build the file is embedded.
  2. If I generate the files in the IntermediateOutputPath, the embedded resource id includes that path. So instead of MyProject.SomeResource.txt I get MyProject.obj.netstandard2._0.SomeResource.txt

Note: - If I replace the Path.Combine and simply generate the output file in the project, it fixes the issue of the resource id, but not the first issue with it not being embedded on the first compile.

How can I ensure the my generated files are embedded on the first compilation, and that I can generate them in the IntermediateOutputPath rather than the Project directory with a resource id as if it were in the project.

2

2 Answers

0
votes

You need to run your target earlier in the pipeline. CoreCompile is too late.

You can add the following to your .csproj file. This target will run before ResolveReferences and add Image.png as an embedded resource.

<Target Name="IncludeDynamicResources" BeforeTargets="ResolveReferences">
    <ItemGroup>
      <EmbeddedResource Include="Image.png">
        <Type>Non-Resx</Type>
      </EmbeddedResource>
    </ItemGroup>
</Target>
0
votes

Your second issue can be solved by simply setting LogicalName metadata to the created task item.

TaskItem taskItem = new TaskItem(ProjectCollection.Escape(outputFile));   
taskItem.SetMetadata("LogicalName", $"{AssemblyName}.{item}.txt");
_generatedFiles.Add(taskItem);

Now to access the assembly name inside your task just add AssemblyName string property to MyTask and AssemblyName="$(AssemblyName)" to your targets. This should produce the desired result.