Skip to content

[Draft] Modify Bootstrap to support Hybrid#6400

Draft
haonanttt wants to merge 3 commits intomainfrom
user/haonanttt/supportHybrid
Draft

[Draft] Modify Bootstrap to support Hybrid#6400
haonanttt wants to merge 3 commits intomainfrom
user/haonanttt/supportHybrid

Conversation

@haonanttt
Copy link
Copy Markdown
Contributor

A microsoft employee must use /azp run to validate using the pipelines below.

WARNING:
Comments made by azure-pipelines bot maybe inaccurate.
Please see pipeline link to verify that the build is being ran.

For status checks on the main branch, please use TransportPackage-Foundation-PR
(https://microsoft.visualstudio.com/ProjectReunion/_build?definitionId=81063&_a=summary)
and run the build against your PR branch with the default parameters.

// 1. Bootstrap can detect hybrid mode and keep the package graph empty
// 2. On Win10, catalog loading during DllMain can expand loadFrom env vars
#if MICROSOFT_WINDOWSAPPSDK_AUTOINITIALIZE_HYBRIDDEPLOYSETUP
global::System.Environment.SetEnvironmentVariable("MICROSOFT_WINDOWSAPPRUNTIME_HYBRID_DEPLOY", "1");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

env vars should be avoided if at all possible. MICROSOFT_WINDOWSAPPRUNTIME_FRAMEWORK_PATH and MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY are required for loadFrom. But the boolean MICROSOFT_WINDOWSAPPRUNTIME_HYBRID_DEPLOY could instead be an API that's explicitly set by the autoinitialization source. Or better yet, can we just infer it from the existing and differing values of MICROSOFT_WINDOWSAPPRUNTIME_FRAMEWORK_PATH and MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

env vars should be avoided if at all possible

+1000

We should be seriously examining dev\UndockedRegFreeWinRT\UndockedRegFreeWinRT-AutoInitializer.cs to see how we can remove

// Set base directory env var for PublishSingleFile support (referenced by SxS redirection)
Environment.SetEnvironmentVariable("MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY", AppContext.BaseDirectory);

not lean in even more heavily on it.

Q: If we need altered Assembly.Load*() behavior why aren't we adding a Custom Load Context to .NET callers? We're already adding *autoiniitalizer*.cs to .NET projects through build magic. What's a little more code?

@Scottj1s
Copy link
Copy Markdown
Member

Have we tested PublishSingleFile to ensure it still works and also supports hybrid now? That was the reason MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY was originally introduced.

@Scottj1s
Copy link
Copy Markdown
Member

Definitely want to get @DrusTheAxe's opinion on these changes - particularly any consequences of removing the package dependency to keep the graph empty.

@DrusTheAxe
Copy link
Copy Markdown
Member

Definitely want to get @DrusTheAxe's opinion on these changes - particularly any consequences of removing the package dependency to keep the graph empty.

There's no description of what "Hybrid" means, why it exists or what its goals are. Makes it hard to make any meaningful analysis.

What I see from the code raises multiple questions and concerns, but lacking a design spec it's hard to assess any clearer than that.

The Dynamic Dependencies spec includes the bootstrapper (because the bootstrapper was conceived, related and intimately dependent on winappsdk's DynDep polyfill implementation). Perhaps it would be better to start with spec to iron out intent, goals, caveats, etc before jumping to code?

https://github.com/microsoft/WindowsAppSDK/blob/main/specs/dynamicdependencies/DynamicDependencies.md

@haonanttt
Copy link
Copy Markdown
Contributor Author

Definitely want to get @DrusTheAxe's opinion on these changes - particularly any consequences of removing the package dependency to keep the graph empty.

There's no description of what "Hybrid" means, why it exists or what its goals are. Makes it hard to make any meaningful analysis.

What I see from the code raises multiple questions and concerns, but lacking a design spec it's hard to assess any clearer than that.

The Dynamic Dependencies spec includes the bootstrapper (because the bootstrapper was conceived, related and intimately dependent on winappsdk's DynDep polyfill implementation). Perhaps it would be better to start with spec to iron out intent, goals, caveats, etc before jumping to code?

https://github.com/microsoft/WindowsAppSDK/blob/main/specs/dynamicdependencies/DynamicDependencies.md

Oh sorry I didn't paste the whole background here. I had a spec about the feature background, overall changes, and the reason for changes at https://microsoft.visualstudio.com/ProjectReunion/_git/WindowsAppSDKAggregator/pullrequest/14931000?path=/docs/hybrid-deployment-spec-v2.md&_a=files

In short, this feature is to support the scenario that "An application developer may want to use a newer version of a specific component package (e.g., the latest WinUI or Foundation) while continuing to depend on the currently installed runtime package for everything else."

To support this, I had 3 draft PRs in Foundation, Aggregator and WWinUI:
Pull request 14931000: [Draft]Enable Hybrid Deployment - Repos - Aggregator
[Draft] Modify Bootstrap to support Hybrid by haonanttt · Pull Request #6400 · microsoft/WindowsApp… - Foundation
Pull request 15309825: [Draft] Add Fallback in LoadLibraryAbs and ModernResourceProvider to support… - WinUI

Copy link
Copy Markdown
Member

@DrusTheAxe DrusTheAxe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left comments about implementation details which are just details

More importantly, I left comments and questions about the requirements, intent and design underlying 'hybrid deploy'. This seems a critical gap we need to address first.

const MddAddPackageDependencyOptions addOptions{};
MDD_PACKAGEDEPENDENCY_CONTEXT tempContext{};
wil::unique_process_heap_string packageFullName;
THROW_IF_FAILED(MddCore::Win11::AddPackageDependency(packageDependencyId.get(), MDD_PACKAGE_DEPENDENCY_RANK_DEFAULT, addOptions, &tempContext, &packageFullName));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MddCore::Win11::* are calls to the OS DynDep APIs

I don't understand the intent. It looks like it's playing hide'n'seek with OS DynDep and winappsdk OS-alternative polyfill hacks (SetFrameworkPathEnvironmentVariable, AddDllDirectory, etc).

This is all terribly confusing and disturbing, unless I'm missing something.

auto& activityContext{ WindowsAppRuntime::MddBootstrap::Activity::Context::Get() };
activityContext.SetInitializationPackageFullName(packageFullName.get());

// Track our initialized state (keep packageDependencyId alive for lifetime protection)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Say whaaat? Track it...but we just removed it (line 487)

// Resolve framework package install path from package full name
static std::wstring GetFrameworkPackagePath(PCWSTR packageFullName)
{
UINT32 pathLength{ 0 };
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: UINT32 pathLength{};

if (rc != ERROR_INSUFFICIENT_BUFFER)
{
THROW_WIN32(rc);
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: THROW_HR_IF(HRESULT_FROM_WIN32(rc), rc != ERROR_INSUFFICIENT_BUFFER);

}

// Resolve framework package install path from package full name
static std::wstring GetFrameworkPackagePath(PCWSTR packageFullName)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to just use GetInstallPath<std::wstring>(pkgfullname) from #6200

That's only 2 steps away from checkin

  • Address Letao's codereview feedback
  • Resolve the RS5 test failure

SetEnvironmentVariableW(L"MICROSOFT_WINDOWSAPPRUNTIME_FRAMEWORK_PATH", frameworkPath);
}

// For C++ apps (no C# auto-initializer), set BASE_DIRECTORY to exe dir if not already set
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this needed?
Why is it needed by C++ but not C#?
Does Rust need it?
Does Python?
Does Node?
...

MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY was added in 1.0? as an emergency workaround. Has it ever been spec'd? 100line comment somewhere?

static void SetBaseDirectoryEnvironmentVariableIfNotSet()
{
wchar_t existing[2]{};
if (GetEnvironmentVariableW(L"MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY", existing, ARRAYSIZE(existing)) > 0)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very loose in behavior

SET MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY=1
SET MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY=0
SET MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY=x
SET MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY=hello
...

But hard to assess how problematic as I can't find docs or spec about MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY.

P.S. For an example how to deal with this sort of hackery in a more rigorous fashion see IsLifetimeManagerViaEnumeration() in dev\WindowsAppRuntime_BootstrapDLL\MddBootstrap.cpp

wchar_t existing[2]{};
if (GetEnvironmentVariableW(L"MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY", existing, ARRAYSIZE(existing)) > 0)
{
return; // Already set by C# auto-initializer
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the C# auto-initializer handles this for C#
why can't/shouldn't the C++ auto-initializer handle it for C++?

{
return; // Already set by C# auto-initializer
}
wchar_t exePath[MAX_PATH]{};
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not LongPath friendly

return; // Already set by C# auto-initializer
}
wchar_t exePath[MAX_PATH]{};
if (GetModuleFileNameW(nullptr, exePath, ARRAYSIZE(exePath)) > 0)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wil::GetModuleFileNameW() beats raw GetModuleFileNameW()

You can find uses in winappsdk e.g. dev\WindowsAppRuntime_BootstrapDLL\MddBootstrap.cpp line 1274 in MddBootstrapInitialize_ShowUI_OnNoMatch()

@haonanttt
Copy link
Copy Markdown
Contributor Author

I left comments about implementation details which are just details

More importantly, I left comments and questions about the requirements, intent and design underlying 'hybrid deploy'. This seems a critical gap we need to address first.

sure, I fully agree the very first thing is we should get an agreement about the background and there is no big concern about the changes. I've added link towards the sepc and 3 draft PRs, and I also added you as review for those PRs. Looking forward for any feedback / concerns / other suggestions. Thank you @DrusTheAxe

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants