Skip to content

Commit

Permalink
feat: remove runtime dependency (#1027)
Browse files Browse the repository at this point in the history
This adds the conditional attribute to the attributes and allows the removal of the runtime assets of Mapperly (`ExcludeAssets="runtime"`), except when using reference handling. Removes the templates since they are not needed anymore. The constant `MAPPERLY_ABSTRACTIONS_SCOPE_RUNTIME` allows to keep the attributes at runtime.
  • Loading branch information
latonz authored Dec 21, 2023
1 parent edfb841 commit ca3ea5a
Show file tree
Hide file tree
Showing 80 changed files with 329 additions and 1,430 deletions.
27 changes: 21 additions & 6 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,6 @@ env:
DOTNET_NOLOGO: true

jobs:
sample:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
- run: dotnet run --project ./samples/Riok.Mapperly.Sample
test:
runs-on: ubuntu-latest
steps:
Expand Down Expand Up @@ -136,3 +130,24 @@ jobs:
with:
name: verify-test-results
path: '**/*.received.*'
sample:
runs-on: ubuntu-latest
needs: package
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
- run: dotnet run --project ./samples/Riok.Mapperly.Sample

# run the same again with the real nuget without runtime assets
- uses: actions/download-artifact@v4
with:
name: mapperly-nupkg
path: artifacts

# use nupkg artifact instead of project references
- run: dotnet nuget add source "$(pwd)/artifacts"
- run: dotnet clean
- run: dotnet build -p:MapperlyNugetPackageVersion="0.0.1-dev.$GITHUB_RUN_ID"
working-directory: ./samples/Riok.Mapperly.Sample
- run: dotnet run --no-build
working-directory: ./samples/Riok.Mapperly.Sample
14 changes: 0 additions & 14 deletions Riok.Mapperly.sln
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Riok.Mapperly.Benchmarks",
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarks", "benchmarks", "{961BAABA-0672-48E7-A5B3-30A676146BE3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Riok.Mapperly.Templates", "src\Riok.Mapperly.Templates\Riok.Mapperly.Templates.csproj", "{FF31D522-6A62-4466-90F7-6B297F82FCF3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Riok.Mapperly.Templates.Tests", "test\Riok.Mapperly.Templates.Tests\Riok.Mapperly.Templates.Tests.csproj", "{1500A843-37E3-4DBA-8BAB-A40CF14678ED}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -63,14 +59,6 @@ Global
{F2214C71-15A7-46EB-A3AA-D02EF4B705EF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F2214C71-15A7-46EB-A3AA-D02EF4B705EF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F2214C71-15A7-46EB-A3AA-D02EF4B705EF}.Release|Any CPU.Build.0 = Release|Any CPU
{FF31D522-6A62-4466-90F7-6B297F82FCF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FF31D522-6A62-4466-90F7-6B297F82FCF3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FF31D522-6A62-4466-90F7-6B297F82FCF3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FF31D522-6A62-4466-90F7-6B297F82FCF3}.Release|Any CPU.Build.0 = Release|Any CPU
{1500A843-37E3-4DBA-8BAB-A40CF14678ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1500A843-37E3-4DBA-8BAB-A40CF14678ED}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1500A843-37E3-4DBA-8BAB-A40CF14678ED}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1500A843-37E3-4DBA-8BAB-A40CF14678ED}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -83,8 +71,6 @@ Global
{C3C40A0A-168F-4A66-B9F9-FC80D2F26306} = {3598BE50-28D5-4BF4-BEA7-09E5FEA2910C}
{43A2E8E0-5A2C-45E9-84EF-CF934EC946FA} = {0FBD6C81-7E7A-4915-90D2-896F11C89FF0}
{F2214C71-15A7-46EB-A3AA-D02EF4B705EF} = {961BAABA-0672-48E7-A5B3-30A676146BE3}
{FF31D522-6A62-4466-90F7-6B297F82FCF3} = {B65AF89A-4A3B-473C-83C8-5F0CB0EED30E}
{1500A843-37E3-4DBA-8BAB-A40CF14678ED} = {3598BE50-28D5-4BF4-BEA7-09E5FEA2910C}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BAAC5976-BEE6-440E-8DDC-90916A1001A1}
Expand Down
6 changes: 6 additions & 0 deletions docs/docs/configuration/reference-handling.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ public partial class CarMapper
This enables the usage of a default reference handler
which reuses the same target object instance if encountered the same source object instance.

:::info
When using reference handling, the Mapperly package reference needs to include the runtime assets
(the runtime assets are needed for the reference handler implementation).
Make sure `ExcludeAssets` on the `PackageReference` does not include `runtime` when using reference handling.
:::

## Custom reference handler

To use a custom `IReferenceHandler` implementation,
Expand Down
6 changes: 6 additions & 0 deletions docs/docs/getting-started/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,9 @@ Try rebuilding the solution or restarting the IDE. This is a bug in the IDE.
## My advanced use case isn't supported by Mapperly or needs lots of configuration. What should I do?

Write the mapping for that class manually. You can mix automatically generated mappings and [user implemented mappings](../configuration/user-implemented-methods.mdx) without problems.

## My code throws `FileNotFoundException` with `Riok.Mapperly.Abstractions`. What should I do?

Are you using [reference handling](../configuration/reference-handling.md)
or have you enabled the [preservation of Mapperly attributes at runtime](installation.mdx#preserving-the-attributes-at-runtime)?
Make sure `ExcludeAssets` on the `PackageReference` does not include `runtime` as these features require runtime assets.
38 changes: 27 additions & 11 deletions docs/docs/getting-started/installation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,33 @@ All you need to do, to install Mapperly is to add a NuGet reference pointing to

<Tabs>
<TabItem value="csproj" label="PackageReference" default>
<CodeBlock language="xml">{`<PackageReference Include="Riok.Mapperly" Version="${useDocusaurusContext().siteConfig.customFields.mapperlyVersion}" />`}</CodeBlock>
<CodeBlock language="xml">{`<PackageReference Include="Riok.Mapperly" Version="${useDocusaurusContext().siteConfig.customFields.mapperlyVersion}" ExcludeAssets="runtime" PrivateAssets="all" />`}</CodeBlock>
</TabItem>
<TabItem value="dontet-cli" label=".NET CLI">

```bash
dotnet add package Riok.Mapperly
```

:::info
To remove the runtime dependency of Mapperly add
`ExcludeAssets="runtime"` and `PrivateAssets="all"`
to the resulting `PackageReference`.
:::

</TabItem>
<TabItem value="pkg-manager" label="Package Manager">

```powershell
Install-Package Riok.Mapperly
```

:::info
To remove the runtime dependency of Mapperly add
`ExcludeAssets="runtime"` and `PrivateAssets="all"`
to the resulting `PackageReference`.
:::

</TabItem>
</Tabs>

Expand All @@ -45,16 +57,20 @@ Checkout the latest stable version [here](https://mapperly.riok.app).

</Conditional>

### Removing runtime dependency
### Preserving the attributes at runtime

It is possible to remove Mapperly runtime references completely by setting `PrivateAssets="all"` and `ExcludeAssets="runtime"` on the `PackageReference`.
`PrivateAssets="all"` ensures that projects referencing this project do not also get a reference to `Riok.Mapperly`.
`ExcludeAssets="runtime"` ensures that the Mapperly .dll files are not copied to the build output.
Mapperly removes the attribute references at compile time by default (they have the `ConditionalAttribute`).
If you want
to preserve the attribute references at runtime
you can set the MSBuild variable `MapperlyAbstractionsScopeRuntime` to `runtime`.

Removing the runtime dependency comes with the disadvantage that whenever the runtime tries
to load a referenced attribute from `Riok.Mapperly.Abstractions` a `FileNotFoundException` is thrown.
This is, for example, the case if `typeof(MyMapper).GetCustomAttributes()` is called.
```xml
<PropertyGroup>
<MapperlyAbstractionsScopeRuntime>runtime</MapperlyAbstractionsScopeRuntime>
</PropertyGroup>
```

In Blazor Wasm applications `<BlazorWebAssemblyLazyLoad Include="Riok.Mapperly.Abstractions.dll" />` can be added to lazily load `Riok.Mapperly.Abstractions`
and reduce the download size.
`Riok.Mapperly.Abstractions` will probably never be needed anyway.
:::info
When preserving the attribute usages, the Mapperly package reference needs to include the runtime assets.
Make sure `ExcludeAssets` on the `PackageReference` does not include `runtime`.
:::
7 changes: 6 additions & 1 deletion samples/Riok.Mapperly.Sample/Riok.Mapperly.Sample.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,14 @@
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
</PropertyGroup>

<ItemGroup>
<ItemGroup Condition="'$(MapperlyNugetPackageVersion)' == ''">
<ProjectReference Include="..\..\src\Riok.Mapperly\Riok.Mapperly.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
<ProjectReference Include="..\..\src\Riok.Mapperly.Abstractions\Riok.Mapperly.Abstractions.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="true" />
</ItemGroup>

<!-- In the CI pipeline, we always want to use test the prebuilt nuget package version, since that nuget package will then be distributed -->
<ItemGroup Condition="'$(MapperlyNugetPackageVersion)' != ''">
<PackageReference Include="Riok.Mapperly" Version="$(MapperlyNugetPackageVersion)" ExcludeAssets="runtime" PrivateAssets="all" />
</ItemGroup>

</Project>
3 changes: 3 additions & 0 deletions src/Riok.Mapperly.Abstractions/FormatProviderAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
using System.Diagnostics;

namespace Riok.Mapperly.Abstractions;

/// <summary>
/// Marks a property or field as a format provider.
/// A format provider needs to be of a type which implements <see cref="IFormatProvider"/> and needs to have a getter.
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
[Conditional("MAPPERLY_ABSTRACTIONS_SCOPE_RUNTIME")]
public sealed class FormatProviderAttribute : Attribute
{
/// <summary>
Expand Down
6 changes: 5 additions & 1 deletion src/Riok.Mapperly.Abstractions/MapDerivedTypeAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System.Diagnostics;

namespace Riok.Mapperly.Abstractions;

/// <summary>
Expand All @@ -8,6 +10,7 @@ namespace Riok.Mapperly.Abstractions;
/// Each target type needs to extend or implement the return type of the mapping method.
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
[Conditional("MAPPERLY_ABSTRACTIONS_SCOPE_RUNTIME")]
public sealed class MapDerivedTypeAttribute : Attribute
{
/// <summary>
Expand Down Expand Up @@ -40,4 +43,5 @@ public MapDerivedTypeAttribute(Type sourceType, Type targetType)
/// Each target type needs to extend or implement the return type of the mapping method.
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public sealed class MapDerivedTypeAttribute<TSource, TTarget> : Attribute { }
[Conditional("MAPPERLY_ABSTRACTIONS_SCOPE_RUNTIME")]
public sealed class MapDerivedTypeAttribute<TSource, TTarget> : Attribute;
3 changes: 3 additions & 0 deletions src/Riok.Mapperly.Abstractions/MapEnumAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
using System.Diagnostics;

namespace Riok.Mapperly.Abstractions;

/// <summary>
/// Customizes how enums are mapped.
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
[Conditional("MAPPERLY_ABSTRACTIONS_SCOPE_RUNTIME")]
public sealed class MapEnumAttribute : Attribute
{
/// <summary>
Expand Down
3 changes: 3 additions & 0 deletions src/Riok.Mapperly.Abstractions/MapEnumValueAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
using System.Diagnostics;

namespace Riok.Mapperly.Abstractions;

/// <summary>
/// Customizes how enum values are mapped
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
[Conditional("MAPPERLY_ABSTRACTIONS_SCOPE_RUNTIME")]
public sealed class MapEnumValueAttribute : Attribute
{
/// <summary>
Expand Down
3 changes: 3 additions & 0 deletions src/Riok.Mapperly.Abstractions/MapPropertyAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
using System.Diagnostics;

namespace Riok.Mapperly.Abstractions;

/// <summary>
/// Specifies options for a property mapping.
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
[Conditional("MAPPERLY_ABSTRACTIONS_SCOPE_RUNTIME")]
public sealed class MapPropertyAttribute : Attribute
{
private const string PropertyAccessSeparatorStr = ".";
Expand Down
2 changes: 2 additions & 0 deletions src/Riok.Mapperly.Abstractions/MapperAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Diagnostics;
using Riok.Mapperly.Abstractions.ReferenceHandling;

namespace Riok.Mapperly.Abstractions;
Expand All @@ -6,6 +7,7 @@ namespace Riok.Mapperly.Abstractions;
/// Marks a partial class as a mapper.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
[Conditional("MAPPERLY_ABSTRACTIONS_SCOPE_RUNTIME")]
public class MapperAttribute : Attribute
{
/// <summary>
Expand Down
5 changes: 4 additions & 1 deletion src/Riok.Mapperly.Abstractions/MapperConstructorAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
using System.Diagnostics;

namespace Riok.Mapperly.Abstractions;

/// <summary>
/// Marks the constructor to be used when type gets activated by Mapperly.
/// </summary>
[AttributeUsage(AttributeTargets.Constructor)]
public sealed class MapperConstructorAttribute : Attribute { }
[Conditional("MAPPERLY_ABSTRACTIONS_SCOPE_RUNTIME")]
public sealed class MapperConstructorAttribute : Attribute;
5 changes: 4 additions & 1 deletion src/Riok.Mapperly.Abstractions/MapperDefaultsAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
using System.Diagnostics;

namespace Riok.Mapperly.Abstractions;

/// <summary>
/// Used to set mapper default values in the assembly.
/// </summary>
[AttributeUsage(AttributeTargets.Assembly)]
public sealed class MapperDefaultsAttribute : MapperAttribute { }
[Conditional("MAPPERLY_ABSTRACTIONS_SCOPE_RUNTIME")]
public sealed class MapperDefaultsAttribute : MapperAttribute;
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
using System.Diagnostics;

namespace Riok.Mapperly.Abstractions;

/// <summary>
/// Specifies options for obsolete ignoring strategy.
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
[Conditional("MAPPERLY_ABSTRACTIONS_SCOPE_RUNTIME")]
public sealed class MapperIgnoreObsoleteMembersAttribute : Attribute
{
/// <summary>
Expand Down
3 changes: 3 additions & 0 deletions src/Riok.Mapperly.Abstractions/MapperIgnoreSourceAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
using System.Diagnostics;

namespace Riok.Mapperly.Abstractions;

/// <summary>
/// Ignores a source property from the mapping.
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
[Conditional("MAPPERLY_ABSTRACTIONS_SCOPE_RUNTIME")]
public sealed class MapperIgnoreSourceAttribute : Attribute
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
using System.Diagnostics;

namespace Riok.Mapperly.Abstractions;

/// <summary>
/// Ignores a source enum value from the mapping.
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
[Conditional("MAPPERLY_ABSTRACTIONS_SCOPE_RUNTIME")]
public sealed class MapperIgnoreSourceValueAttribute : Attribute
{
/// <summary>
Expand Down
3 changes: 3 additions & 0 deletions src/Riok.Mapperly.Abstractions/MapperIgnoreTargetAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
using System.Diagnostics;

namespace Riok.Mapperly.Abstractions;

/// <summary>
/// Ignores a target property from the mapping.
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
[Conditional("MAPPERLY_ABSTRACTIONS_SCOPE_RUNTIME")]
public sealed class MapperIgnoreTargetAttribute : Attribute
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
using System.Diagnostics;

namespace Riok.Mapperly.Abstractions;

/// <summary>
/// Ignores a target enum value from the mapping.
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
[Conditional("MAPPERLY_ABSTRACTIONS_SCOPE_RUNTIME")]
public sealed class MapperIgnoreTargetValueAttribute : Attribute
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
using System.Diagnostics;

namespace Riok.Mapperly.Abstractions;

/// <summary>
/// Defines the strategy used when emitting warnings for unmapped members.
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
[Conditional("MAPPERLY_ABSTRACTIONS_SCOPE_RUNTIME")]
public sealed class MapperRequiredMappingAttribute : Attribute
{
/// <summary>
Expand Down
5 changes: 4 additions & 1 deletion src/Riok.Mapperly.Abstractions/ObjectFactoryAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System.Diagnostics;

namespace Riok.Mapperly.Abstractions;

/// <summary>
Expand All @@ -8,4 +10,5 @@ namespace Riok.Mapperly.Abstractions;
/// If the object factory has a parameter, the source object is provided as an argument.
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public sealed class ObjectFactoryAttribute : Attribute { }
[Conditional("MAPPERLY_ABSTRACTIONS_SCOPE_RUNTIME")]
public sealed class ObjectFactoryAttribute : Attribute;
4 changes: 4 additions & 0 deletions src/Riok.Mapperly.Abstractions/PublicAPI.Shipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,7 @@ Riok.Mapperly.Abstractions.FormatProviderAttribute.Default.set -> void
Riok.Mapperly.Abstractions.FormatProviderAttribute.FormatProviderAttribute() -> void
Riok.Mapperly.Abstractions.MapPropertyAttribute.FormatProvider.get -> string?
Riok.Mapperly.Abstractions.MapPropertyAttribute.FormatProvider.set -> void
Riok.Mapperly.Abstractions.ReferenceHandling.PreserveReferenceHandler
Riok.Mapperly.Abstractions.ReferenceHandling.PreserveReferenceHandler.PreserveReferenceHandler() -> void
Riok.Mapperly.Abstractions.ReferenceHandling.PreserveReferenceHandler.SetReference<TSource, TTarget>(TSource source, TTarget target) -> void
Riok.Mapperly.Abstractions.ReferenceHandling.PreserveReferenceHandler.TryGetReference<TSource, TTarget>(TSource source, out TTarget? target) -> bool
Loading

0 comments on commit ca3ea5a

Please sign in to comment.