Architecture
Mapperly is an incremental .NET source generator implementation. Source generators are explained here and here. However, the incremental source generator of Mapperly is not yet optimal (see #72).
Projects
Mapperly is structured in two projects.
Riok.Mapperly.Abstractions
includes abstractions and attributes to be used by the application code,
this is referenced by the source generator.
Riok.Mapperly
includes the implementation of the source generator.
Flow
This describes the process implemented by Mapperly on a higher level.
For each discovered MapperAttribute
a new DescriptorBuilder
is created.
The DescriptorBuilder
is responsible to build a MapperDescriptor
which holds all the mappings.
The DescriptorBuilder
does this by following this process:
- Extracting the configuration from the attribute
- Extracting user implemented object factories
- Extracting user implemented and user defined mapping methods.
It instantiates a
User*Mapping
(eg.UserDefinedNewInstanceMethodMapping
) for each discovered mapping method and adds it to the queue of mappings to work on. - For each mapping in the queue the
DescriptorBuilder
tries to build its implementation bodies. This is done by a so called*MappingBodyBuilder
. A mapping body builder tries to map each property from the source to the target. To do this, it asks theDescriptorBuilder
to create mappings for the according types. To create a mapping from one type to another, theDescriptorBuilder
loops through a set of*MappingBuilder
s. Each of the mapping builders try to create a mapping (anITypeMapping
implementation) for the asked type mapping by using one approach on how to map types (eg. an explicit cast is implemented by theExplicitCastMappingBuilder
). These mappings are queued in the queue of mappings which need the body to be built (currently body builders are only used for object to object (property-based) mappings). - The
SourceEmitter
emits the code described by theMapperDescriptor
and all its mappings.
Roslyn multi targeting
Mapperly targets multiple Roslyn versions by building multiple NuGet packages and merging them together into a single one. Multi-targeting is needed to support new language features, such as required members introduced in C# 11, while still supporting older compiler versions.
See build/package.sh
for details.
Support a new roslyn version
- Include the new version in
roslyn_versions
inbuild/package.sh
. - Create a new file
Riok.Mapperly.Roslyn$(Version).props
insrc/Riok.Mapperly
similar to the existing ones and define constants as needed. - Update the default
ROSLYN_VERSION
insrc/Riok.Mapperly/Riok.Mapperly.csproj
. - Update the
Microsoft.CodeAnalysis.CSharp
dependency version intest/Riok.Mapperly.Tests/Riok.Mapperly.Tests.csproj
. - Adjust the .NET version matrix of the
integration-test
GitHub Actions job (defined in.github/workflows/test.yml
) to include a dotnet version which is based on the added Roslyn version. - Adjust the .NET version in the
global.json
file as needed. - If generated code changes based on the new Roslyn version,
introduce a new
roslynVersionName
inRiok.Mapperly.IntegrationTests.BaseMapperTest.GetRoslynVersion()
and generate the new snapshots.