Skip to content

A basic example of how to use MSBuild files in NuGet packages.

License

Notifications You must be signed in to change notification settings

yurkinh/msbuild-nuget-sample

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

.NET MAUI NuGet Package with MSBuild files sample

A basic example of how to use MSBuild files in NuGet packages. This sample is geared toward use by .NET MAUI developers specifically but the overall concepts are applicable to .NET in general.

The Problem

There are situations where, as a .NET MAUI NuGet package author, you may want to automate project configuration or perform custom build logic to simplify the setup process for users and reduce opportunity for errors and inconsistencies. For example, configuring some project settings, including package files with the requisite build actions, or performing other necessary operations through MSBuild tasks.

The Approach

The MSBuild system is highly customizable and extensible. It enables us to split build configuration across multiple standalone project files, typically with .props and .targets extensions, for use in multiple projects.

In MSBuild, .props and .targets files serve different purposes and are used at different stages of the build process. Typically, .props files are used to define properties for use throughout the build process and are imported early on. The .targets files, on the other hand, can contain item definitions, properties, targets, and tasks which get imported later in the build process.

NuGet packages can optionally include MSBuild .props and .targets files within its build folders. When projects consume packages containing build files in the form of <package_id>.<extension>, those files effectively get imported automatically. Files in the root build folders are considered suitable for all frameworks whereas framework specific build folders are used where applicable.

Packages like Microsoft.Maui.Controls.Build.Tasks use this technique to apply TFM (target framework moniker) specific settings. For example, the Microsoft.Maui.Controls.Build.Tasks.targets file (within the buildTransitive > net6.0-ios10.0 folder) defines a PropertyGroup that conditionally sets MtouchLink to None.

<Project>
  <PropertyGroup>
    <MtouchLink Condition="'$(MtouchLink)' == '' and '$(Configuration)' == 'Debug' and '$(UseInterpreter)' == 'true'">None</MtouchLink>
  </PropertyGroup>
</Project>

The Sample

This first-principles example shows how to add a .targets file to a package, created from a .NET MAUI class library project, alongside the resulting assembly and other sample content via csproj configuration. In this case, the example SamplePackage.targets file just includes some assets from the package with the MauiFont and MauiImage build actions as a means to demonstrate the concept.

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <ItemGroup>
        <MauiFont Include="$(MSBuildThisFileDirectory)..\fonts\Font Awesome 6 Free-Regular-400.otf" Visible="False">
            <Link>%(Filename)%(Extension)</Link>
        </MauiFont>
        <MauiImage Include="$(MSBuildThisFileDirectory)..\images\sample_bot.svg" Visible="False">
            <Link>%(Filename)%(Extension)</Link>
        </MauiImage>
    </ItemGroup>
</Project>

The package is then consumed by a .NET MAUI application within the same solution to demonstrate the .targets configuration has been imported successfully.

The MauiImage items can be used in MainPage.xaml with no additional setup.

<Image
    Source="sample_bot.png"
    HeightRequest="185"
    Aspect="AspectFit" />

The MauiFont must get registered in MauiProgram.cs as an additional step.

builder.ConfigureFonts(fonts =>
{
    fonts.AddFont("Font Awesome 6 Free-Regular-400.otf", "FontAwesome");
});

Once registered, the font alias and glyphs can be used by MainPage.xaml.

<Label
    Text="&#xf1c9;"
    FontFamily="FontAwesome" />

To embellish the sample a little, the SamplePackage project includes some additional components:

The UseSamplePackage() method from AppBuilderExtensions.cs gets called from MauiProgram.cs.

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
         builder
            .UseMauiApp<App>()
            .UseSamplePackage()
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
            });

        return builder.Build();
    }
}

The FontAwesomeGlyphs and PackageImages classes can be optionally used in MainPage.xaml instead of raw string values.

<Image
    Source="{x:Static sp:PackageImages.DotnetBot}"
    HeightRequest="185"
    Aspect="AspectFit" />

<Label
    Text="{x:Static sp:FontAwesomeGlyphs.Code}"
    FontFamily="{x:Static sp:FontAwesomeGlyphs.FontAlias}"
    TextColor="{StaticResource PrimaryBrandColor}" />

<Label
    Text="Welcome to &#10;.NET Multi-platform App UI"
    TextColor="{StaticResource PrimaryBrandColor}" /> 

PackageConsumerApp Sample

Running the sample

Initial setup

If you have freshly cloned the repository, you must perform the following steps before loading the SamplePackage solution:

  1. In Terminal, change the directory to the root of this git repository

  2. Use dotnet build to create the initial NuGet package for the SamplePackage project.

    dotnet build src/SamplePackage/SamplePackage.csproj
    

Important

Building the SamplePackage project results in the NuGet package getting created automatically. The package gets output to a local git ignored artifacts folder under src. The .NET MAUI test app resolves the package from this directory instead of using a project reference. See notable details for more information.

Building and running

  1. Open the SamplePackage solution in Visual Studio / Visual Studio Code
  2. Set the PackageConsumerApp as the startup project
  3. Build and run the app.

Important

After making changes to the SamplePackage project, to test it using the PackageConsumerApp project:

Notable details

The SamplePackage solution is comprised of two projects. The SamplePackage class library and a .NET MAUI test app, PackageConsumerApp. To demonstrate the relevant NuGet and MSBuild concepts, the test app references the generated NuGet package rather than the class library project itself.

The solution has been configured to simplify updating the test app to use new versions of the library project NuGet package. In this case, a new package gets generated automatically when building the class library project. Changes to the package are then observed after you rebuild the test app or when you start debugging it. The approach being used is described in more detail in the NuGet DevLoop Sample repo.

SamplePackage project

The SampleProject csproj file defines some typical package metadata and some other properties relating to the pack operation. Notably, PackageVersion, PackageOutputPath, and GeneratePackageOnBuild.

<PropertyGroup>
    ...
    <PackageVersion>0.0.99999-sample</PackageVersion>
    <PackageOutputPath>../artifacts/</PackageOutputPath>
    <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>

In addition to the standard packaging properties, an <ItemGroup> includes the files from the Content folder so they are packed into the appropriate folders within the resulting package.

<ItemGroup>
    ...
    <None Include="Content\font_awesome\*" Pack="true" PackagePath="\fonts" />
    <None Include="Content\images\*" Pack="true" PackagePath="\images" />
    <None Include="Content\resources\*" Pack="true" PackagePath="\resources" />
    <None Include="Content\SamplePackage.targets" Pack="true" PackagePath="\build\SamplePackage.targets" />
    <None Include="Content\SamplePackage.targets" Pack="true" PackagePath="\buildTransitive\SamplePackage.targets" />
</ItemGroup>

The None build action is used so these files are not included in the build process. The Pack attribute determines that the items should be packed. Their paths within the package have been specififed using the PackagePath attribute.

Lastly, the Microsoft.SourceLink.GitHub and System.Management packages are referenced. The former links the source code to the corresponding files in the repo for easier debugging and the latter is specific to Windows builds providing access to system management information.

<ItemGroup>
    <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="all" IsImplicitlyDefined="true" />
    <PackageReference Include="System.Management" Version="7.0.0" Condition="$(TargetFramework.Contains('-windows')) == true" />
</ItemGroup>

PackageConsumerApp project

The PackageConsumerApp test app references the SamplePackage NuGet package built in the same solution.

<ItemGroup>
    <PackageReference Include="SamplePackage" Version="0.0.99999-sample" />
</ItemGroup>

Attributions

This sample uses the Font Awesome 6 Free-Regular-400 otf provided under MIT license by Font Awesome.

About

A basic example of how to use MSBuild files in NuGet packages.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • C# 100.0%