dotnet / roslyn

The Roslyn .NET compiler provides C# and Visual Basic languages with rich code analysis APIs.
https://docs.microsoft.com/dotnet/csharp/roslyn-sdk/
MIT License
19.1k stars 4.04k forks source link

/deterministic msbuild flag does not create deterministic .winmd files #23456

Open martinsuchan opened 6 years ago

martinsuchan commented 6 years ago

I'm trying to make perfect deterministic/reproducible builds for our C# UWP app. Source: http://blog.paranoidcoding.com/2016/04/05/deterministic-builds-in-roslyn.html

The flag /deterministic works for standard UWP apps and libs (.exe and .dll files), but not for .winmd files created for Windows Runtime Components.

Version Used: Using VS 15.4.5, msbuild 15.4.8.50001

Steps to Reproduce: The attachment contains repro with simple Hello World UWP app with simple Windows Runtime Component: ReproTest.zip

  1. Build it using the build.ps1 command and extract the ReproTest_1.0.0.0_x86_bundle.appxupload to a new folder. Extract all files within this package and all nested appx packages.
  2. Build the project for a second time and extract the second .appxupload to a different folder.
  3. Compare these two folders. Ideally both should contain exactly same files.

The repro already includes two builds, that can be compared using the compare.ps1 script

Expected Behavior: When using /deterministic flag and building UWP Windows Runtime Component in the same directory multiple times, the created .winmd files should be always byte-to-byte identical.

Actual Behavior: The .winmd files created by the steps above are different each time.

Note the created .pri files are also different, maybe I should create another ticket for making the MakePri.exe tool deterministic as well?

tmat commented 6 years ago

WinRT components built from C# are built in two steps - the Roslyn compiler produces an intermediary file that is transformed by WinMDExp tool. That tool is not deterministic. This is not a bug in Roslyn compilers, although it can be argued that we shouldn't need this extra post-processing step and the compilers should produce .winmd files directly.

martinsuchan commented 6 years ago

Hi, any chance to forward this issue to .NET Native team that is maintaining the WinMDExp and MakePri tools? It would be really useful if these tools supported deterministic builds as well with optional flags, thanks!

jcouv commented 6 years ago

Tagging @MichalStrehovsky I'm not sure how .NET Native tracks such feature requests.

MichalStrehovsky commented 6 years ago

.NET Native team doesn't own WinMDExp or MakePri. WinMDExp is part of the (desktop CLR) .NET SDK and MakePri is part of the Windows SDK. I don't know how those are tracked either.

MichalStrehovsky commented 6 years ago

Maybe cc @russellhadley for the WinMDExp?

martinsuchan commented 6 years ago

Here's the thought that started all this:

Windows Microsoft Store uses when updating apps differential updates, where only modified file blocks since the last version are downloaded. Let's say our UWP app, which is used by 100 000 users, consists of 6 projects, each producing 100KB DLL during the build process.

We plan to release update with changes only in one project. Without deterministic builds the build process for Store creates 6 new different DLLs inside the appxupload package, or 3x6 DLLs in case of build for x86, x64 and ARM. That's 1.8MB update size for 100K users for change in just one project.

With deterministic builds only one changed DLL will be produced per platform, making the update size only 300KB per user. That's 150GB less traffic just for one update of one app in Microsoft Store. Using deterministic build process for all UWP Store packages by default could save terabytes of traffic each day, basically for free! This is not just a cosmetic change, this could save real money at the end.

MichalStrehovsky commented 6 years ago

Here's the thought that started all this

Oh, okay - deterministic IL assemblies won't help much with this. The appxupload file is not what the user will download to their device.

Once appxupload bundle is received by the Store, it gets compiled to native code with the .NET Native compiler. At that point all the input IL files get compiled down to native code and produces a single DLL with native code in it for the entire app (you can have a look at it by inspecting the outputs of a Release build of your app).

This process is deterministic. But: at the point of native compilation, global/whole program optimizations are applied to the program. The change in the single input assembly can have ripple effects on this native DLL that can completely invalidate all 64 kB blocks within the file (shift things around enough so that a block based differential update will be invalidated).

.NET Native tries to at least move type information to the beginning of the file so that code changes that don't affect the shape of types don't invalidate the section, but we haven't made investments beyond that.

There are differential update techniques that better apply to native code, but I don't know if Store uses them.

gafter commented 5 years ago

Moving to a sooner release so we can track forwarding this to an appropriate product team.

martinsuchan commented 1 year ago

Hi, are there any news regarding deterministic .winmd builds? It has been three years since the last update, thanks.

jaredpar commented 1 year ago

I think the best approach here is to file a VS feedback bug. That will get the issue routed to the appropriate team here and it will be more meaningful if it's coming from a customer. If you post the link to the issue here I can keep an eye on it and see who ends up with it.

There really isn't much we can do though in terms of our pipeline as these tools exist outside of it. The artifacts we produce are deterministic to my understanding