Processing of SVG Files on Build in .NET
For some of my projects, I need bitmap images (PNG, to be precise) to be included in the final build. I often prefer to store these images in the form of SVG files, because they are easier to edit.
Earlier, I've been working on MSBuild tooling to process SVGs on build, but eventually I've moved on to a more simple way (and since it was pretty hard to lift that tooling onto modern .NET runtime).
The plan is:
- Declare your SVG files as MSBuild items (e.g.
<Svg Include="my-file.svg" />).
- Install the Svg.Skia.Converter as a .NET tool into your solution, commit the
.config/dotnet-tools.jsonfile (for the others to install it using the manifest).
- Add a build step that will call
dotnet tool restoreduring the build.
- Add a built step that will call
dotnet tool run Svg.Skia.Converter --inputFiles @(Svg) --outputDirectory $(TheOutputPathYouWant)during the build.
So, let's begin. First of all, go to the solution directory and execute the following command:
dotnet new tool-manifest
This command will generate a
.config/dotnet-tools.json file, and will enable you to install local .NET tools (to not have to deal with a global environment).
Then, install the Svg.Skia.Converter tool:
dotnet tool install Svg.Skia.Converter
Note it gets installed locally (no
--global flag), and should not mess with any other tools or projects you are working on.
Now, let's go write some MSBuild. Create a separate file
Svg.props somewhere in your project or solution folder:
<Project> <Target Name="DotNetToolRestore" BeforeTargets="ProcessSvgFiles" Inputs="..\.config\dotnet-tools.json" Outputs="$(IntermediateOutputPath)\dotnet-tool-restore.timestamp"> <Exec Command="dotnet tool restore" WorkingDirectory=".." /> <Touch Files="$(IntermediateOutputPath)\dotnet-tool-restore.timestamp" AlwaysCreate="true" /> </Target> <Target Name="ProcessSvgFiles" BeforeTargets="Build" DependsOnTargets="DotNetToolRestore" Inputs="@(Svg)" Outputs="@(Svg->'$(OutputPath)\Resources\%(FileName).png')"> <Exec Command="dotnet tool run Svg.Skia.Converter --inputFiles @(Svg) --outputDirectory $(OutputPath)\Resources" /> </Target> </Project>
This file declares two MSBuild targets.
DotNetToolRestoregets called first, and will just invoke
dotnet tool restorein a working directory of
..(the paths are relative to the parent directory of the file containing the target). It will also touch a file
obj/dotnet-tool-restore.timestampfor the purpose of caching (to not call the same build step again if there were no changes in the
ProcessSvgFilesgets called before the
Buildtarget, and will invoke
dotnet tool run Svg.Skia.Converterto convert all the SVG files into PNGs. It has properly (I hope!) declared the inputs and outputs of this target, so that MSBuild will be able to cache the results of this target and not call it again if there were no changes in the SVG files. The resulting files will go into
And the final step: in your project file, add the
Svg items and don't forget to import
<Project Sdk="Microsoft.NET.Sdk"> <ItemGroup> <Svg Include="submarine.svg" /> </ItemGroup> <Import Project="Svg.props" /> </Project>
And voilá, you have a working build step that will convert your SVG files into PNGs with proper caching, and working across all the supported platforms (well, across all the platforms Svg.Skia.Converter supports, at least).
Note: This section is outdated. This was already fixed in the latest version of Svg.Skia.Converter.
There's one current downside of this approach: the Svg.Skia.Converter got published in a way that it requires .NET Core 3.1 to work, and it won't load on newer runtimes. I've sent a PR fixing that, but a new version hasn't been published yet. So, for now, you'll have to install .NET Core 3.1 runtime on your build agents and developer machines for the whole scheme to work. Watch for the corresponding issue in case that's already fixed.
You can see the whole pipeline working in my project O21 (I am specifically linking a particular commit to make the reference future-proof).