|
40 | 40 | <TargetPlatformMinVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.19041.0</TargetPlatformMinVersion> |
41 | 41 | <SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'tizen'">6.5</SupportedOSPlatformVersion> |
42 | 42 | </PropertyGroup> |
43 | | - <!-- Side-by-side "Dev" flavor: set -p:FwLiteFlavor=Dev to install alongside the prod app |
44 | | - (different package name + label so both can coexist on a device). --> |
45 | | - <PropertyGroup Condition="'$(FwLiteFlavor)' == 'Dev'"> |
46 | | - <ApplicationId>org.sil.FwLiteMaui.dev</ApplicationId> |
47 | | - <ApplicationTitle>FieldWorks Lite Dev</ApplicationTitle> |
48 | | - </PropertyGroup> |
49 | 43 | <PropertyGroup Condition="'$(Configuration)' == 'Debug' "> |
50 | 44 | <WindowsPackageType>None</WindowsPackageType> |
51 | 45 | <PublishReadyToRun>false</PublishReadyToRun> |
|
60 | 54 | <PropertyGroup> |
61 | 55 | <TargetPlatform>$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)'))</TargetPlatform> |
62 | 56 | </PropertyGroup> |
| 57 | + <!-- Exclude the build-time tool's sources from FwLiteMaui's own compile. It's a |
| 58 | + standalone Microsoft.NET.Sdk Exe project, not part of this project's code. --> |
| 59 | + <ItemGroup> |
| 60 | + <Compile Remove="build\**" /> |
| 61 | + <Content Remove="build\**" /> |
| 62 | + <None Remove="build\**" /> |
| 63 | + <EmbeddedResource Remove="build\**" /> |
| 64 | + <MauiAsset Remove="build\**" /> |
| 65 | + </ItemGroup> |
63 | 66 | <ItemGroup> |
64 | 67 | <!-- App Icon --> |
65 | 68 | <!-- background color is required for mac per: https://learn.microsoft.com/en-us/dotnet/maui/user-interface/images/app-icons?view=net-maui-8.0&tabs=windows#recolor-the-background --> |
|
100 | 103 | --> |
101 | 104 | <PackageReference Include="Mono.Unix" ExcludeAssets="all" /> |
102 | 105 | </ItemGroup> |
| 106 | + <!-- |
| 107 | + Android workaround for linq2db.EntityFrameworkCore 10.3.x. |
| 108 | +
|
| 109 | + SqlTransparentExpression's static ctor does a GetConstructor lookup for |
| 110 | + (ExceptExpression, RelationalTypeMapping) which doesn't exist on the type |
| 111 | + (the only declared ctor takes (ConstantExpression, RelationalTypeMapping?)). |
| 112 | + The lookup returns null and the cctor throws InvalidOperationException. |
| 113 | + Reproducible on plain net10.0 via RuntimeHelpers.RunClassConstructor — this |
| 114 | + is a linq2db bug, not a missing-metadata-from-trimming issue. See |
| 115 | + backend/FwLite/LcmCrdt.Tests/SqlTransparentExpressionCctorRepro.cs and |
| 116 | + backend/FwLite/LcmCrdt/LINQ2DB-V6-NOTES.md (Cctor patcher section). |
| 117 | +
|
| 118 | + Upstream issue: TODO INSERT UPSTREAM ISSUE URL HERE |
| 119 | +
|
| 120 | + On desktop the class is beforefieldinit and its static fields are only read |
| 121 | + from Quote(), which our CRDT workload never calls, so the bug is silent. |
| 122 | + Android surfaces it the first time a CRDT save runs. |
| 123 | +
|
| 124 | + An earlier attempt used ILLink.Substitutions.xml to stub the .cctor, but |
| 125 | + Debug Android builds skip the linker entirely (PublishTrimmed=false), so |
| 126 | + the substitution never applied. Even in Release publish, the substitution |
| 127 | + site differs from where the dll ends up staged for packaging, so a |
| 128 | + single-target hook is fragile. |
| 129 | +
|
| 130 | + Instead, we Cecil-patch the assembly at build time, unconditionally, |
| 131 | + via the small tool under build/Linq2DbCctorPatcher. |
| 132 | +
|
| 133 | + Where the dll lives depends on configuration: |
| 134 | + - Debug (PublishTrimmed=false): staged into $(MonoAndroidIntermediateAssemblyDir)<abi>\ |
| 135 | + by _LinkAssembliesNoShrink, then bundled for fast-deploy. |
| 136 | + - Release (PublishTrimmed=true) : output by ILLink into <rid>\linked\ and copied to |
| 137 | + <rid>\linked\shrunk\ by _RemoveRegisterAttribute. _CollectAssembliesToCompress |
| 138 | + then consumes the shrunk copies for the assembly store. |
| 139 | + We must patch BOTH locations (and any other staged copies under $(IntermediateOutputPath)). |
| 140 | + The target runs BeforeTargets on the consumers (_CollectAssembliesToCompress and |
| 141 | + _BuildApkFastDev) so it fires regardless of trimmed/untrimmed and AAB/APK flows. |
| 142 | +
|
| 143 | + KILL-SWITCH: |
| 144 | + - The version-pin check below fails the build the moment somebody bumps |
| 145 | + the package outside the verified-broken range. When that happens, either |
| 146 | + widen the range (after re-verifying the bug still exists in the new |
| 147 | + version) or delete this whole block + the build/Linq2DbCctorPatcher |
| 148 | + project + unskip SqlTransparentExpressionCctorRepro.cs. |
| 149 | + --> |
| 150 | + |
| 151 | + <!-- Version pin: hard-error if linq2db.EntityFrameworkCore is outside the patched range. |
| 152 | + We don't want this patcher silently chugging along on a version we never tested, |
| 153 | + nor do we want to carry it past an upstream fix. Range is the closed interval |
| 154 | + [10.3.0, 10.3.999]; anything 10.4.x or 11.x is rejected. |
| 155 | +
|
| 156 | + Source of truth is backend/Directory.Packages.props (central package management), |
| 157 | + which we read directly: PackageReference items have no Version metadata under CPM, |
| 158 | + and ResolvedPackageReference is only populated after restore. Reading the props |
| 159 | + file is the most robust signal that works pre- and post-restore. --> |
| 160 | + <Target Name="_VerifyLinq2DbEfCoreVersionPin" |
| 161 | + Condition="'$(TargetPlatform)' == 'android'" |
| 162 | + BeforeTargets="_BuildLinq2DbCctorPatcher"> |
| 163 | + <PropertyGroup> |
| 164 | + <!-- Pull "X.Y.Z" from a line like: <PackageVersion Include="linq2db.EntityFrameworkCore" Version="10.3.0" /> --> |
| 165 | + <_Linq2DbEfCoreEffectiveVersion>$([System.Text.RegularExpressions.Regex]::Match($([System.IO.File]::ReadAllText('$(MSBuildThisFileDirectory)..\..\Directory.Packages.props')), 'linq2db\.EntityFrameworkCore[^>]*Version="([^"]+)"').Groups[1].Value)</_Linq2DbEfCoreEffectiveVersion> |
| 166 | + </PropertyGroup> |
| 167 | + <Error Condition="'$(_Linq2DbEfCoreEffectiveVersion)' == ''" |
| 168 | + Text="Could not determine resolved version of linq2db.EntityFrameworkCore from backend/Directory.Packages.props. The cctor-patcher version pin can't verify itself. Investigate before proceeding." /> |
| 169 | + <!-- Patched range: 10.3.x only. Anything else fails the build. --> |
| 170 | + <Error Condition="'$(_Linq2DbEfCoreEffectiveVersion)' != '' And !$([System.Text.RegularExpressions.Regex]::IsMatch('$(_Linq2DbEfCoreEffectiveVersion)', '^10\.3\.[0-9]+(-.*)?$'))" |
| 171 | + Text="linq2db.EntityFrameworkCore is at $(_Linq2DbEfCoreEffectiveVersion); the cctor patcher in build/Linq2DbCctorPatcher only verifies the 10.3.x IL shape. Re-verify the SqlTransparentExpression cctor bug still exists in $(_Linq2DbEfCoreEffectiveVersion) (unskip the repro test in LcmCrdt.Tests/SqlTransparentExpressionCctorRepro.cs and run it against the new version), then either: (a) widen this pin if still broken, or (b) DELETE build/Linq2DbCctorPatcher and the _BuildLinq2DbCctorPatcher / _PatchLinq2DbSqlTransparentExpressionCctor / _VerifyLinq2DbEfCoreVersionPin / _CollectLinq2DbStagedAssemblies targets if fixed. Upstream issue: TODO INSERT UPSTREAM ISSUE URL HERE. See backend/FwLite/LcmCrdt/LINQ2DB-V6-NOTES.md (Cctor patcher section)." /> |
| 172 | + </Target> |
| 173 | + <Target Name="_BuildLinq2DbCctorPatcher" |
| 174 | + Condition="'$(TargetPlatform)' == 'android'" |
| 175 | + DependsOnTargets="_VerifyLinq2DbEfCoreVersionPin" |
| 176 | + BeforeTargets="_LinkAssembliesNoShrink;_AfterILLinkAdditionalSteps;_RemoveRegisterAttribute"> |
| 177 | + <!-- Build the patcher tool as a side-channel net10.0 project. We use <MSBuild> rather |
| 178 | + than a <ProjectReference> so the patcher's TargetFramework/RuntimeIdentifier isn't |
| 179 | + entangled with FwLiteMaui's android-arm64 graph. RemoveProperties strips inherited |
| 180 | + RID/TF so the inner build resolves cleanly as plain net10.0. --> |
| 181 | + <MSBuild Projects="$(MSBuildThisFileDirectory)build\Linq2DbCctorPatcher\Linq2DbCctorPatcher.csproj" |
| 182 | + Targets="Restore;Build" |
| 183 | + Properties="Configuration=$(Configuration)" |
| 184 | + RemoveProperties="TargetFramework;TargetFrameworks;RuntimeIdentifier;RuntimeIdentifiers;TargetPlatform;TargetPlatformIdentifier;TargetPlatformVersion;UseMaui;SingleProject;SelfContained;PublishReadyToRun;PublishSingleFile" /> |
| 185 | + </Target> |
| 186 | + <!-- Glob staged dlls before the patch target's Inputs/Outputs check evaluates. |
| 187 | + Lives in a separate target so the ItemGroup is materialized by the time |
| 188 | + _PatchLinq2Db...'s batching examines @(_Linq2DbStagedAssemblies). --> |
| 189 | + <Target Name="_CollectLinq2DbStagedAssemblies" |
| 190 | + Condition="'$(TargetPlatform)' == 'android'"> |
| 191 | + <ItemGroup> |
| 192 | + <!-- Glob every staged copy under obj\<Config>\<TF>\: Debug ships them via |
| 193 | + android\assets\<abi>\, Release publishes through <rid>\linked\ (and linked\shrunk\). --> |
| 194 | + <_Linq2DbStagedAssemblies Include="$(IntermediateOutputPath)**\linq2db.EntityFrameworkCore.dll" /> |
| 195 | + </ItemGroup> |
| 196 | + </Target> |
| 197 | + <!-- |
| 198 | + Incremental patching: per-dll sentinel files at <dll>.cctor-patched. |
| 199 | + Inputs are the staged dlls; Outputs are %()-batched sentinels so MSBuild |
| 200 | + skips already-patched files on subsequent builds. The patcher itself also |
| 201 | + short-circuits via the same sentinel — if dotnet restore re-extracts the |
| 202 | + package, the dll's mtime moves forward past the sentinel's and patching |
| 203 | + re-runs. Belt-and-braces against any case where MSBuild's incremental |
| 204 | + check disagrees with the file-system reality. |
| 205 | + --> |
| 206 | + <Target Name="_PatchLinq2DbSqlTransparentExpressionCctor" |
| 207 | + Condition="'$(TargetPlatform)' == 'android'" |
| 208 | + DependsOnTargets="_BuildLinq2DbCctorPatcher;_CollectLinq2DbStagedAssemblies" |
| 209 | + AfterTargets="_LinkAssembliesNoShrink;_AfterILLinkAdditionalSteps;_RemoveRegisterAttribute" |
| 210 | + BeforeTargets="_CollectAssembliesToCompress;_BuildApkFastDev" |
| 211 | + Inputs="@(_Linq2DbStagedAssemblies)" |
| 212 | + Outputs="@(_Linq2DbStagedAssemblies->'%(FullPath).cctor-patched')"> |
| 213 | + <PropertyGroup> |
| 214 | + <_Linq2DbPatcherDll>$(MSBuildThisFileDirectory)build\Linq2DbCctorPatcher\bin\$(Configuration)\net10.0\Linq2DbCctorPatcher.dll</_Linq2DbPatcherDll> |
| 215 | + </PropertyGroup> |
| 216 | + <Error Condition="!Exists('$(_Linq2DbPatcherDll)')" |
| 217 | + Text="Linq2DbCctorPatcher.dll not found at $(_Linq2DbPatcherDll). _BuildLinq2DbCctorPatcher should have produced it." /> |
| 218 | + <Message Importance="high" Text="Linq2db cctor patcher: @(_Linq2DbStagedAssemblies->Count()) staged linq2db.EntityFrameworkCore.dll copies under $(IntermediateOutputPath)" /> |
| 219 | + <Exec Command="dotnet "$(_Linq2DbPatcherDll)" "%(_Linq2DbStagedAssemblies.FullPath)"" |
| 220 | + Condition="'@(_Linq2DbStagedAssemblies)' != ''" /> |
| 221 | + </Target> |
103 | 222 | </Project> |
0 commit comments